Skip to content

Commit df5f640

Browse files
committed
refactor v-ref, allow use in child templates, fix vuejs#636
1 parent b8d4225 commit df5f640

File tree

4 files changed

+76
-35
lines changed

4 files changed

+76
-35
lines changed

src/directives/component.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ module.exports = {
2626
// we simply remove it from the DOM and save it in a
2727
// cache object, with its constructor id as the key.
2828
this.keepAlive = this._checkParam('keep-alive') != null
29+
// check ref
30+
this.refID = _.attr(this.el, 'ref')
2931
if (this.keepAlive) {
3032
this.cache = {}
3133
}
@@ -83,6 +85,10 @@ module.exports = {
8385
if (this.keepAlive) {
8486
this.cache[this.ctorId] = child
8587
}
88+
var refID = child._refID || this.refID
89+
if (refID) {
90+
vm.$[refID] = child
91+
}
8692
return child
8793
}
8894
},
@@ -94,6 +100,10 @@ module.exports = {
94100

95101
unbuild: function () {
96102
var child = this.childVM
103+
var refID = (child && child._refID) || this.refID
104+
if (refID) {
105+
this.vm.$[refID] = null
106+
}
97107
if (!child || this.keepAlive) {
98108
return
99109
}

src/directives/ref.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@ module.exports = {
55
isLiteral: true,
66

77
bind: function () {
8-
var child = this.el.__vue__
9-
if (!child || this.vm !== child.$parent) {
8+
var vm = this.el.__vue__
9+
if (!vm) {
1010
_.warn(
11-
'v-ref should only be used on a child component ' +
12-
'from the parent template.'
11+
'v-ref should only be used on a component root element.'
1312
)
1413
return
1514
}
16-
this.vm.$[this.expression] = child
17-
},
18-
19-
unbind: function () {
20-
if (this.vm.$[this.expression] === this.el.__vue__) {
21-
delete this.vm.$[this.expression]
22-
}
15+
// If we get here, it means this is a `v-ref` on a
16+
// child, because parent scope `v-ref` is stripped in
17+
// `v-component` already. So we just record our own ref
18+
// here - it will overwrite parent ref in `v-component`,
19+
// if any.
20+
vm._refID = this.expression
2321
}
2422

2523
}

src/directives/repeat.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ module.exports = {
6969
*/
7070

7171
checkRef: function () {
72-
var childId = _.attr(this.el, 'ref')
73-
this.childId = childId
74-
? this.vm.$interpolate(childId)
72+
var refID = _.attr(this.el, 'ref')
73+
this.refID = refID
74+
? this.vm.$interpolate(refID)
7575
: null
7676
var elId = _.attr(this.el, 'el')
7777
this.elId = elId
@@ -136,8 +136,8 @@ module.exports = {
136136
}
137137
this.vms = this.diff(data || [], this.vms)
138138
// update v-ref
139-
if (this.childId) {
140-
this.vm.$[this.childId] = this.vms
139+
if (this.refID) {
140+
this.vm.$[this.refID] = this.vms
141141
}
142142
if (this.elId) {
143143
this.vm.$$[this.elId] = this.vms.map(function (vm) {
@@ -325,8 +325,8 @@ module.exports = {
325325
*/
326326

327327
unbind: function () {
328-
if (this.childId) {
329-
delete this.vm.$[this.childId]
328+
if (this.refID) {
329+
this.vm.$[this.refID] = null
330330
}
331331
if (this.vms) {
332332
var i = this.vms.length

test/unit/specs/directives/ref_spec.js

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ if (_.inBrowser) {
1313
var components = {
1414
test: {
1515
id: 'test'
16+
},
17+
test2: {
18+
id: 'test2'
1619
}
1720
}
1821

@@ -24,8 +27,52 @@ if (_.inBrowser) {
2427
})
2528
expect(vm.$.test).toBeTruthy()
2629
expect(vm.$.test.$options.id).toBe('test')
27-
vm.$.test.$destroy()
28-
expect(vm.$.test).toBeUndefined()
30+
})
31+
32+
it('with dynamic v-component', function (done) {
33+
var vm = new Vue({
34+
el: el,
35+
components: components,
36+
data: { test: 'test' },
37+
template: '<div v-component="{{test}}" v-ref="test"></div>'
38+
})
39+
expect(vm.$.test.$options.id).toBe('test')
40+
vm.test = 'test2'
41+
_.nextTick(function () {
42+
expect(vm.$.test.$options.id).toBe('test2')
43+
vm.test = ''
44+
_.nextTick(function () {
45+
expect(vm.$.test).toBeNull()
46+
done()
47+
})
48+
})
49+
})
50+
51+
it('should also work in child template', function (done) {
52+
var vm = new Vue({
53+
el: el,
54+
data: { view: 'test1' },
55+
template: '<div v-component="{{view}}"></div>',
56+
components: {
57+
test1: {
58+
id: 'test1',
59+
template: '<div v-ref="test1"></div>',
60+
replace: true
61+
},
62+
test2: {
63+
id: 'test2',
64+
template: '<div v-ref="test2"></div>',
65+
replace: true
66+
}
67+
}
68+
})
69+
expect(vm.$.test1.$options.id).toBe('test1')
70+
vm.view = 'test2'
71+
_.nextTick(function () {
72+
expect(vm.$.test1).toBeNull()
73+
expect(vm.$.test2.$options.id).toBe('test2')
74+
done()
75+
})
2976
})
3077

3178
it('with v-repeat', function (done) {
@@ -42,7 +89,7 @@ if (_.inBrowser) {
4289
_.nextTick(function () {
4390
expect(vm.$.test.length).toBe(0)
4491
vm._directives[0].unbind()
45-
expect(vm.$.test).toBeUndefined()
92+
expect(vm.$.test).toBeNull()
4693
done()
4794
})
4895
})
@@ -71,19 +118,5 @@ if (_.inBrowser) {
71118
expect(_.warn).toHaveBeenCalled()
72119
})
73120

74-
it('should warn when used in child template', function () {
75-
var vm = new Vue({
76-
el: el,
77-
template: '<div v-component="test"></div>',
78-
components: {
79-
test: {
80-
template: '<div v-ref="test"></div>',
81-
replace: true
82-
}
83-
}
84-
})
85-
expect(_.warn).toHaveBeenCalled()
86-
})
87-
88121
})
89122
}

0 commit comments

Comments
 (0)