From 90c6c2902b1f124093ad0d514984230194cb818e Mon Sep 17 00:00:00 2001 From: Rahul Kadyan Date: Thu, 12 Jan 2017 18:15:07 +0530 Subject: [PATCH 1/8] :sparkles: Tests for ref callback --- test/unit/features/ref.spec.js | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index da637b7bfa3..515a18ddaa0 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -157,4 +157,49 @@ describe('ref', () => { }).$mount() expect(vm.$refs.test).toBe(vm.$children[0]) }) + + it('should should call callback (v-for)', done => { + const vm = new Vue({ + data: { + items: [1, 2, 3] + }, + template: ` +
+ +
+ `, + components: { + test: { + props: ['n'], + template: '
{{ n }}
' + } + }, + methods: { + onRef (ref, remove) { + if (!this.$refs.list) this.$refs.list = [] + + if (remove) { + const index = this.$refs.list.indexOf(ref) + + if (index > -1) this.$refs.list.splice(index, 1) + } else { + this.$refs.list.push(ref) + } + } + } + }).$mount() + assertRefs() + // updating + vm.items.push(4) + waitForUpdate(assertRefs) + .then(() => { vm.items = [] }) + .then(assertRefs) + .then(done) + + function assertRefs () { + expect(Array.isArray(vm.$refs.list)).toBe(true) + expect(vm.$refs.list.length).toBe(vm.items.length) + expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true) + } + }) }) From 76b3eda74e85c70550c824f5094d02a7ff0ecbaa Mon Sep 17 00:00:00 2001 From: Rahul Kadyan Date: Thu, 12 Jan 2017 18:15:30 +0530 Subject: [PATCH 2/8] :sparkles: Support for ref callback --- src/core/vdom/modules/ref.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js index c69547ce7a3..f6a6f5999f4 100644 --- a/src/core/vdom/modules/ref.js +++ b/src/core/vdom/modules/ref.js @@ -25,13 +25,17 @@ export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { const ref = vnode.componentInstance || vnode.elm const refs = vm.$refs if (isRemoval) { - if (Array.isArray(refs[key])) { + if (typeof key === 'function') { + key(ref, true) + } else if (Array.isArray(refs[key])) { remove(refs[key], ref) } else if (refs[key] === ref) { refs[key] = undefined } } else { - if (vnode.data.refInFor) { + if (typeof key === 'function') { + key(ref) + } else if (vnode.data.refInFor) { if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { refs[key].push(ref) } else { From 3aa5c8acb8f7eb43bc86b23942c1a2f817ad0ee7 Mon Sep 17 00:00:00 2001 From: Rahul Kadyan Date: Sun, 29 Jan 2017 13:40:03 +0530 Subject: [PATCH 3/8] Add test of inline ref callback --- test/unit/features/ref.spec.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index 515a18ddaa0..d7d4b6c0017 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -158,7 +158,7 @@ describe('ref', () => { expect(vm.$refs.test).toBe(vm.$children[0]) }) - it('should should call callback (v-for)', done => { + it('should should call callback method (v-for)', done => { const vm = new Vue({ data: { items: [1, 2, 3] @@ -202,4 +202,32 @@ describe('ref', () => { expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true) } }) + + it('should should call inline callback (v-for)', done => { + const vm = new Vue({ + data: { + items: [1, 2, 3] + }, + template: ` +
+ +
+ `, + components: { + test: { + props: ['n'], + template: '
{{ n }}
' + } + } + }).$mount() + assertRefs() + // updating + vm.items.push(4) + waitForUpdate(assertRefs).then(done) + + function assertRefs () { + expect(Object.keys(vm.$refs).length).toBe(vm.items.length) + expect(Object.keys(vm.$refs).every(i => vm.$refs[i].$el.textContent === String(i))).toBe(true) + } + }) }) From 7656661d3256a6224bb0fbbc61c1fbec16c6d2c3 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Feb 2017 16:56:07 -0500 Subject: [PATCH 4/8] adjust ref implementation strategy --- flow/component.js | 6 +++- src/core/instance/lifecycle.js | 2 ++ src/core/vdom/modules/ref.js | 57 ++++++++++++++-------------------- test/unit/features/ref.spec.js | 26 +++++++--------- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/flow/component.js b/flow/component.js index 49aebbe75a9..aae1103a2b1 100644 --- a/flow/component.js +++ b/flow/component.js @@ -2,6 +2,10 @@ import type { Config } from '../src/core/config' import type VNode from '../src/core/vdom/vnode' import type Watcher from '../src/core/observer/watcher' +declare type Refs = { + [key: string]: Component | Element | Array | void; +}; + declare interface Component { // constructor information static cid: number; @@ -23,7 +27,7 @@ declare interface Component { $parent: Component | void; $root: Component; $children: Array; - $refs: { [key: string]: Component | Element | Array | void }; + $refs: Refs; $slots: { [key: string]: Array }; $scopedSlots: { [key: string]: () => VNodeChildren }; $vnode: VNode; diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js index efe7ad6415f..b42b8bf7505 100644 --- a/src/core/instance/lifecycle.js +++ b/src/core/instance/lifecycle.js @@ -1,6 +1,7 @@ /* @flow */ import Watcher from '../observer/watcher' +import { resetRefs } from '../vdom/modules/ref' import { createEmptyVNode } from '../vdom/vnode' import { observerState } from '../observer/index' import { updateComponentListeners } from './events' @@ -83,6 +84,7 @@ export function lifecycleMixin (Vue: Class) { const prevVnode = vm._vnode const prevActiveInstance = activeInstance activeInstance = vm + vm.$refs = resetRefs(vm.$refs) vm._vnode = vnode // Vue.prototype.__patch__ is injected in entry points // based on the rendering backend used. diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js index f6a6f5999f4..ea5c811949a 100644 --- a/src/core/vdom/modules/ref.js +++ b/src/core/vdom/modules/ref.js @@ -1,48 +1,39 @@ /* @flow */ -import { remove } from 'shared/util' - export default { - create (_: any, vnode: VNodeWithData) { - registerRef(vnode) - }, - update (oldVnode: VNodeWithData, vnode: VNodeWithData) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true) - registerRef(vnode) - } - }, - destroy (vnode: VNodeWithData) { - registerRef(vnode, true) - } + create: registerRef, + update: registerRef } -export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { +export function registerRef (_: any, vnode: VNodeWithData) { const key = vnode.data.ref if (!key) return - const vm = vnode.context const ref = vnode.componentInstance || vnode.elm - const refs = vm.$refs - if (isRemoval) { - if (typeof key === 'function') { - key(ref, true) - } else if (Array.isArray(refs[key])) { - remove(refs[key], ref) - } else if (refs[key] === ref) { - refs[key] = undefined - } - } else { - if (typeof key === 'function') { - key(ref) - } else if (vnode.data.refInFor) { - if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { + const refs = vnode.context.$refs + + if (typeof key === 'function') { + key(ref) + } else if (vnode.data.refInFor) { + if (Array.isArray(refs[key])) { + if (refs[key].indexOf(ref) < 0) { refs[key].push(ref) - } else { - refs[key] = [ref] } } else { - refs[key] = ref + refs[key] = [ref] + } + } else { + refs[key] = ref + } +} + +export function resetRefs (refs: Refs): Refs { + const res = {} + // keep existing v-for ref arrays even if empty + for (const key in refs) { + if (Array.isArray(refs[key])) { + res[key] = [] } } + return res } diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index d7d4b6c0017..fa03eca378a 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -165,7 +165,7 @@ describe('ref', () => { }, template: `
- +
`, components: { @@ -175,16 +175,8 @@ describe('ref', () => { } }, methods: { - onRef (ref, remove) { - if (!this.$refs.list) this.$refs.list = [] - - if (remove) { - const index = this.$refs.list.indexOf(ref) - - if (index > -1) this.$refs.list.splice(index, 1) - } else { - this.$refs.list.push(ref) - } + onRef (ref) { + (this.$refs.list || (this.$refs.list = [])).push(ref) } } }).$mount() @@ -210,7 +202,7 @@ describe('ref', () => { }, template: `
- +
`, components: { @@ -223,11 +215,15 @@ describe('ref', () => { assertRefs() // updating vm.items.push(4) - waitForUpdate(assertRefs).then(done) + waitForUpdate(assertRefs) + .then(() => { vm.items = [] }) + .then(assertRefs) + .then(done) function assertRefs () { - expect(Object.keys(vm.$refs).length).toBe(vm.items.length) - expect(Object.keys(vm.$refs).every(i => vm.$refs[i].$el.textContent === String(i))).toBe(true) + expect(Array.isArray(vm.$refs.list)).toBe(true) + expect(vm.$refs.list.length).toBe(vm.items.length) + expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true) } }) }) From f17a4298eacbfac758974dd7082e390c370a073e Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Feb 2017 17:04:47 -0500 Subject: [PATCH 5/8] fix patch ref registration --- src/core/vdom/patch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index aac33dc49af..27632aee5f0 100644 --- a/src/core/vdom/patch.js +++ b/src/core/vdom/patch.js @@ -189,7 +189,7 @@ export function createPatchFunction (backend) { } else { // empty component root. // skip all element-related modules except for ref (#3455) - registerRef(vnode) + registerRef(null, vnode) // make sure to invoke the insert hook insertedVnodeQueue.push(vnode) } From bbef5e96fe124fae20197865a600fb7981a4f5fc Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Feb 2017 17:11:57 -0500 Subject: [PATCH 6/8] fix tests --- test/unit/features/ref.spec.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index fa03eca378a..976a0b2ef5f 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -221,9 +221,8 @@ describe('ref', () => { .then(done) function assertRefs () { - expect(Array.isArray(vm.$refs.list)).toBe(true) - expect(vm.$refs.list.length).toBe(vm.items.length) - expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true) + expect(Object.keys(vm.$refs).length).toBe(vm.items.length) + expect(Object.keys(vm.$refs).every(i => vm.$refs[i].$el.textContent === String(i))).toBe(true) } }) }) From 9b4a007a9bf57057bf4c1aac5e11f522ed7696bb Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Feb 2017 17:18:05 -0500 Subject: [PATCH 7/8] fix flow --- src/core/vdom/modules/ref.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js index ea5c811949a..d700e9e52c2 100644 --- a/src/core/vdom/modules/ref.js +++ b/src/core/vdom/modules/ref.js @@ -15,9 +15,10 @@ export function registerRef (_: any, vnode: VNodeWithData) { if (typeof key === 'function') { key(ref) } else if (vnode.data.refInFor) { - if (Array.isArray(refs[key])) { - if (refs[key].indexOf(ref) < 0) { - refs[key].push(ref) + const refArray = refs[key] + if (Array.isArray(refArray)) { + if (refArray.indexOf(ref) < 0) { + refArray.push(ref) } } else { refs[key] = [ref] From 220846360560f42538b73c0d9da1a369d061d1a3 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Feb 2017 17:25:18 -0500 Subject: [PATCH 8/8] fix test for phantomjs --- test/unit/features/ref.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index 976a0b2ef5f..1aa6760b90e 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -202,7 +202,7 @@ describe('ref', () => { }, template: `
- +
`, components: {