Browse Source

Fix: nested child elements can not be updated correctly, fix #5618 (#5627)

* fix:nested elements can not be updated correctly

* add tests

* ensure nestedIndex is always passed down
dev
laoxiong 8 years ago
committed by Evan You
parent
commit
f2bd882073
  1. 5
      src/core/instance/render-helpers/render-list.js
  2. 7
      src/core/vdom/helpers/normalize-children.js
  3. 21
      test/unit/features/component/component-slot.spec.js
  4. 28
      test/unit/modules/vdom/create-element.spec.js

5
src/core/instance/render-helpers/render-list.js

@ -1,6 +1,6 @@
/* @flow */
import { isObject } from 'core/util/index'
import { isObject, isDef } from 'core/util/index'
/**
* Runtime helper for rendering v-for lists.
@ -28,5 +28,8 @@ export function renderList (
ret[i] = render(val[key], key, i)
}
}
if (isDef(ret)) {
(ret: any)._isVList = true
}
return ret
}

7
src/core/vdom/helpers/normalize-children.js

@ -1,7 +1,7 @@
/* @flow */
import VNode, { createTextVNode } from 'core/vdom/vnode'
import { isFalse, isDef, isUndef, isPrimitive } from 'shared/util'
import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util'
// The template compiler attempts to minimize the need for normalization by
// statically analyzing the template at compile time.
@ -58,7 +58,10 @@ function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNo
res[res.length - 1] = createTextVNode(last.text + c.text)
} else {
// default key for nested array children (likely generated by v-for)
if (isDef(c.tag) && isUndef(c.key) && isDef(nestedIndex)) {
if (isTrue(children._isVList) &&
isDef(c.tag) &&
isUndef(c.key) &&
isDef(nestedIndex)) {
c.key = `__vlist${nestedIndex}_${i}__`
}
res.push(c)

21
test/unit/features/component/component-slot.spec.js

@ -639,4 +639,25 @@ describe('Component slot', () => {
expect(vm.$el.textContent).toBe('2foo')
}).then(done)
})
it('the elements of slot should be updated correctly', done => {
const vm = new Vue({
data: { n: 1 },
template: '<div><test><span v-for="i in n" :key="i">{{ i }}</span><input value="a"/></test></div>',
components: {
test: {
template: '<div><slot></slot></div>'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('<div><span>1</span><input value="a"></div>')
const input = vm.$el.querySelector('input')
input.value = 'b'
vm.n++
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe('<div><span>1</span><span>2</span><input value="a"></div>')
expect(vm.$el.querySelector('input')).toBe(input)
expect(vm.$el.querySelector('input').value).toBe('b')
}).then(done)
})
})

28
test/unit/modules/vdom/create-element.spec.js

@ -152,4 +152,32 @@ describe('create-element', () => {
}).$mount()
expect('Avoid using observed data object as vnode data').toHaveBeenWarned()
})
it('nested child elements should be updated correctly', done => {
const vm = new Vue({
data: { n: 1 },
render (h) {
const list = []
for (let i = 0; i < this.n; i++) {
list.push(h('span', i))
}
const input = h('input', {
attrs: {
value: 'a',
type: 'text'
}
})
return h('div', [[...list, input]])
}
}).$mount()
expect(vm.$el.innerHTML).toBe('<span>0</span><input value="a" type="text">')
const el = vm.$el.querySelector('input')
el.value = 'b'
vm.n++
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe('<span>0</span><span>1</span><input value="a" type="text">')
expect(vm.$el.querySelector('input')).toBe(el)
expect(vm.$el.querySelector('input').value).toBe('b')
}).then(done)
})
})

Loading…
Cancel
Save