Skip to content

Commit da5e600

Browse files
committed
make ref reactive
1 parent bbad978 commit da5e600

File tree

10 files changed

+110
-30
lines changed

10 files changed

+110
-30
lines changed

src/deprecations.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ if (process.env.NODE_ENV !== 'production') {
156156
'<component is="{{view}}"> syntax will be depreacted in 1.0.0. Use ' +
157157
'<component bind-is="view"> instead.'
158158
)
159+
},
160+
161+
REF_IN_CHILD: function () {
162+
warn(
163+
'v-ref or ref can no longer be used on a component root in its own ' +
164+
'template in 1.0.0. Use it in the parent template instead.'
165+
)
159166
}
160167

161168
}

src/directive.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ Directive.prototype.param = function (name) {
191191
if (param != null) {
192192
this.el.removeAttribute('bind-' + name)
193193
param = (this._scope || this.vm).$eval(param)
194+
process.env.NODE_ENV !== 'production' && _.log(
195+
'You are using bind- syntax on "' + name + '", which ' +
196+
'is a directive param. It will be evaluated only once.'
197+
)
194198
}
195199
}
196200
return param

src/directives/component.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,14 @@ module.exports = {
3434
}
3535

3636
// check ref
37-
this.refID = this.param(config.prefix + 'ref')
37+
// TODO: only check ref in 1.0.0
38+
var ref = this.param(config.prefix + 'ref')
39+
/* istanbul ignore if */
40+
if (process.env.NODE_ENV !== 'production' && ref) {
41+
_.deprecation.V_REF()
42+
}
43+
this.ref = ref || this.param('ref')
44+
3845
if (this.keepAlive) {
3946
this.cache = {}
4047
}
@@ -336,9 +343,14 @@ module.exports = {
336343
setCurrent: function (child) {
337344
this.unsetCurrent()
338345
this.childVM = child
339-
var refID = child._refID || this.refID
340-
if (refID) {
341-
this.vm.$[refID] = child
346+
var ref = child._refId || this.ref
347+
if (ref) {
348+
var hash = (this._scope || this.vm).$
349+
if (!hash.hasOwnProperty(ref)) {
350+
_.defineReactive(hash, ref, child)
351+
} else {
352+
hash[ref] = child
353+
}
342354
}
343355
},
344356

@@ -349,9 +361,10 @@ module.exports = {
349361
unsetCurrent: function () {
350362
var child = this.childVM
351363
this.childVM = null
352-
var refID = (child && child._refID) || this.refID
353-
if (refID) {
354-
this.vm.$[refID] = null
364+
var ref = (child && child._refId) || this.ref
365+
var hash = (this._scope || this.vm).$
366+
if (ref && hash.hasOwnProperty(ref)) {
367+
hash[ref] = null
355368
}
356369
},
357370

src/directives/for.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ module.exports = {
5555
// check v-ref
5656
var ref = this.param(config.prefix + 'ref')
5757
/* istanbul ignore if */
58-
if (process.env.NODE_ENV !== 'production') {
59-
if (this.refID) _.deprecation.V_REF()
58+
if (process.env.NODE_ENV !== 'production' && ref) {
59+
_.deprecation.V_REF()
6060
}
6161
this.ref = ref || this.param('ref')
6262

@@ -206,6 +206,8 @@ module.exports = {
206206
// create iteration scope
207207
var parentScope = this._scope || this.vm
208208
var scope = Object.create(parentScope)
209+
// ref holder for the scope
210+
scope.$ = {}
209211
// make sure point $parent to parent scope
210212
scope.$parent = parentScope
211213
// for two-way binding on alias
@@ -230,14 +232,22 @@ module.exports = {
230232
*/
231233

232234
updateRef: function () {
235+
var ref = this.ref
236+
var hash = (this._scope || this.vm).$
237+
var refs
233238
if (!this.converted) {
234-
this.vm.$[this.ref] = this.frags.map(findVmFromFrag)
239+
refs = this.frags.map(findVmFromFrag)
235240
} else {
236-
var refs = this.vm.$[this.ref] = {}
241+
refs = {}
237242
this.frags.forEach(function (frag) {
238243
refs[frag.scope.$key] = findVmFromFrag(frag)
239244
})
240245
}
246+
if (!hash.hasOwnProperty(ref)) {
247+
_.defineReactive(hash, ref, refs)
248+
} else {
249+
hash[ref] = refs
250+
}
241251
},
242252

243253
/**
@@ -480,7 +490,7 @@ module.exports = {
480490

481491
unbind: function () {
482492
if (this.ref) {
483-
this.vm.$[this.ref] = null
493+
(this._scope || this.vm).$[this.ref] = null
484494
}
485495
if (this.frags) {
486496
var i = this.frags.length

src/directives/ref.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ module.exports = {
1717
// `v-component` already. So we just record our own ref
1818
// here - it will overwrite parent ref in `v-component`,
1919
// if any.
20-
vm._refID = this.expression
20+
vm._refId = this.expression
21+
22+
if (process.env.NODE_ENV !== 'production') {
23+
_.deprecation.REF_IN_CHILD()
24+
}
2125
}
2226
}

src/directives/repeat.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ module.exports = {
6969
this.leaveStagger = +this.param('leave-stagger') || stagger
7070

7171
// check for v-ref/v-el
72-
this.refID = this.param(config.prefix + 'ref')
72+
this.refId = this.param(config.prefix + 'ref')
7373
this.elID = this.param(config.prefix + 'el')
7474

7575
if (process.env.NODE_ENV !== 'production') {
76-
if (this.refID) _.deprecation.V_REF()
76+
if (this.refId) _.deprecation.V_REF()
7777
if (this.elID) _.deprecation.V_EL()
7878
}
79-
this.refID = this.refID || this.param('ref')
79+
this.refId = this.refId || this.param('ref')
8080

8181
// check other directives that need to be handled
8282
// at v-repeat level
@@ -228,8 +228,8 @@ module.exports = {
228228
realUpdate: function (data) {
229229
this.vms = this.diff(data, this.vms)
230230
// update v-ref
231-
if (this.refID) {
232-
this.vm.$[this.refID] = this.converted
231+
if (this.refId) {
232+
this.vm.$[this.refId] = this.converted
233233
? toRefObject(this.vms)
234234
: this.vms
235235
}
@@ -440,8 +440,8 @@ module.exports = {
440440

441441
unbind: function () {
442442
this.componentState = ABORTED
443-
if (this.refID) {
444-
this.vm.$[this.refID] = null
443+
if (this.refId) {
444+
this.vm.$[this.refId] = null
445445
}
446446
if (this.vms) {
447447
var i = this.vms.length

test/unit/specs/directives/component_spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var _ = require('../../../../src/util')
22
var Vue = require('../../../../src/vue')
33

44
if (_.inBrowser) {
5-
describe('v-component', function () {
5+
describe('Component', function () {
66

77
var el
88
beforeEach(function () {
@@ -531,6 +531,5 @@ if (_.inBrowser) {
531531
})
532532
}).not.toThrow()
533533
})
534-
535534
})
536535
}

test/unit/specs/directives/for/for_spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -664,9 +664,9 @@ if (_.inBrowser) {
664664
var vm = new Vue({
665665
el: el,
666666
template:
667-
'<div v-for="item in list" track-by="id" ref="outer">' +
667+
'<div v-for="item in list" track-by="id">' +
668668
'{{item.msg}}' +
669-
'<div v-for="subItem in item.list" track-by="id" bind-ref="\'inner\' + $index">' +
669+
'<div v-for="subItem in item.list" track-by="id">' +
670670
'{{subItem.msg}}' +
671671
'</div>' +
672672
'</div>',
@@ -682,8 +682,8 @@ if (_.inBrowser) {
682682
})
683683
assertMarkup()
684684

685-
var oldFrags = vm.$.outer
686-
var oldInnerFrags = vm.$.inner0
685+
var oldNodes = el.children
686+
var oldInnerNodes = el.children[0].children
687687

688688
vm.list = [
689689
{ id: 1, msg: 'wa', list: [
@@ -698,9 +698,9 @@ if (_.inBrowser) {
698698
// should reuse old frags!
699699
var i = 2
700700
while (i--) {
701-
expect(vm.$.outer[i]).toBe(oldFrags[i])
701+
expect(el.children[i]).toBe(oldNodes[i])
702702
}
703-
expect(vm.$.inner0[0]).toBe(oldInnerFrags[0])
703+
expect(el.children[0].children[0]).toBe(oldInnerNodes[0])
704704
done()
705705
})
706706

test/unit/specs/directives/ref_spec.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,23 @@ if (_.inBrowser) {
2323
var vm = new Vue({
2424
el: el,
2525
components: components,
26-
template: '<test v-ref="test"></test>'
26+
data: {
27+
ref: 'test2'
28+
},
29+
template: '<test ref="test"></test><test2 bind-ref="ref"></test2>'
2730
})
2831
expect(vm.$.test).toBeTruthy()
2932
expect(vm.$.test.$options.id).toBe('test')
33+
expect(vm.$.test2).toBeTruthy()
34+
expect(vm.$.test2.$options.id).toBe('test2')
3035
})
3136

3237
it('with dynamic v-component', function (done) {
3338
var vm = new Vue({
3439
el: el,
3540
components: components,
3641
data: { test: 'test' },
37-
template: '<component is="{{test}}" v-ref="test"></component>'
42+
template: '<component bind-is="test" ref="test"></component>'
3843
})
3944
expect(vm.$.test.$options.id).toBe('test')
4045
vm.test = 'test2'
@@ -48,6 +53,43 @@ if (_.inBrowser) {
4853
})
4954
})
5055

56+
it('should be reactive when bound by dynamic component', function (done) {
57+
var vm = new Vue({
58+
el: el,
59+
data: { view: 'one' },
60+
template: '<component bind-is="view" ref="test"></component>{{$.test.value}}',
61+
components: {
62+
one: {
63+
id: 'one',
64+
replace: true,
65+
data: function () {
66+
return { value: 1 }
67+
}
68+
},
69+
two: {
70+
id: 'two',
71+
replace: true,
72+
data: function () {
73+
return { value: 2 }
74+
}
75+
}
76+
}
77+
})
78+
expect(vm.$.test.$options.id).toBe('one')
79+
expect(el.textContent).toBe('1')
80+
vm.view = 'two'
81+
_.nextTick(function () {
82+
expect(vm.$.test.$options.id).toBe('two')
83+
expect(el.textContent).toBe('2')
84+
vm.view = ''
85+
_.nextTick(function () {
86+
expect(vm.$.test).toBeNull()
87+
expect(el.textContent).toBe('')
88+
done()
89+
})
90+
})
91+
})
92+
5193
it('should also work in child template', function (done) {
5294
var vm = new Vue({
5395
el: el,

test/unit/specs/instance/state_spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ describe('Instance state initialization', function () {
165165
return this.a + this.b
166166
},
167167
d: {
168+
cache: false, // for deprecation coverage. TODO: remove in 1.0.0
168169
get: function () {
169170
return this.a + this.b
170171
},

0 commit comments

Comments
 (0)