Skip to content

Commit 5ea44fb

Browse files
committed
make v-repeat work with primitive values + minor directive refactor
1 parent 0982349 commit 5ea44fb

File tree

8 files changed

+109
-35
lines changed

8 files changed

+109
-35
lines changed

src/compiler.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -653,14 +653,18 @@ CompilerProto.destroy = function () {
653653

654654
compiler.execHook('beforeDestroy')
655655

656+
// unobserve data
657+
Observer.unobserve(compiler.data, '', compiler.observer)
658+
656659
// unbind all direcitves
657660
i = directives.length
658661
while (i--) {
659662
dir = directives[i]
660663
// if this directive is an instance of an external binding
661664
// e.g. a directive that refers to a variable on the parent VM
662665
// we need to remove it from that binding's instances
663-
if (!dir.isEmpty && dir.binding.compiler !== compiler) {
666+
// * empty and literal bindings do not have binding.
667+
if (dir.binding && dir.binding.compiler !== compiler) {
664668
instances = dir.binding.instances
665669
if (instances) instances.splice(instances.indexOf(dir), 1)
666670
}
@@ -673,13 +677,10 @@ CompilerProto.destroy = function () {
673677
exps[i].unbind()
674678
}
675679

676-
// unbind/unobserve all own bindings
680+
// unbind all own bindings
677681
for (key in bindings) {
678682
binding = bindings[key]
679683
if (binding) {
680-
if (binding.root) {
681-
Observer.unobserve(binding.value, binding.key, compiler.observer)
682-
}
683684
binding.unbind()
684685
}
685686
}

src/directive.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,12 @@ DirProto.applyFilters = function (value) {
152152

153153
/**
154154
* Unbind diretive
155-
* @ param {Boolean} update
156-
* Sometimes we call unbind before an update (i.e. not destroy)
157-
* just to teardown previous stuff, in that case we do not want
158-
* to null everything.
159155
*/
160-
DirProto.unbind = function (update) {
156+
DirProto.unbind = function () {
161157
// this can be called before the el is even assigned...
162-
if (!this.el) return
163-
if (this._unbind) this._unbind(update)
164-
if (!update) this.vm = this.el = this.binding = this.compiler = null
158+
if (!this.el || !this.vm) return
159+
if (this._unbind) this._unbind()
160+
this.vm = this.el = this.binding = this.compiler = null
165161
}
166162

167163
// exposed methods ------------------------------------------------------------

src/directives/on.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = {
2222
},
2323

2424
update: function (handler) {
25-
this.unbind(true)
25+
this.reset()
2626
if (typeof handler !== 'function') {
2727
return utils.warn('Directive "on" expects a function value.')
2828
}
@@ -72,9 +72,13 @@ module.exports = {
7272
}
7373
},
7474

75-
unbind: function (update) {
75+
reset: function () {
7676
this.el.removeEventListener(this.arg, this.handler)
7777
this.handler = null
78-
if (!update) this.el.vue_viewmodel = null
78+
},
79+
80+
unbind: function () {
81+
this.reset()
82+
this.el.vue_viewmodel = null
7983
}
8084
}

src/directives/repeat.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ module.exports = {
118118

119119
update: function (collection, init) {
120120

121-
this.unbind(true)
121+
this.reset()
122122
// attach an object to container to hold handlers
123123
this.container.vue_dHandlers = utils.hash()
124124
// if initiating with an empty collection, we need to
@@ -173,7 +173,7 @@ module.exports = {
173173
ctn = this.container,
174174
vms = this.vms,
175175
col = this.collection,
176-
ref, item
176+
ref, item, primitive
177177

178178
// append node into DOM first
179179
// so v-if can get access to parentNode
@@ -188,6 +188,11 @@ module.exports = {
188188
transition(el, 1, function () {
189189
ctn.insertBefore(el, ref)
190190
}, this.compiler)
191+
// wrap primitive element in an object
192+
if (utils.typeOf(data) !== 'Object') {
193+
primitive = true
194+
data = { value: data }
195+
}
191196
}
192197

193198
item = new this.Ctor({
@@ -207,6 +212,14 @@ module.exports = {
207212
item.$destroy()
208213
} else {
209214
vms.splice(index, 0, item)
215+
// for primitive values, listen for value change
216+
if (primitive) {
217+
data.__observer__.on('set', function (key, val) {
218+
if (key === 'value') {
219+
col[item.$index] = val
220+
}
221+
})
222+
}
210223
// in case `$destroy` is called directly on a repeated vm
211224
// make sure the vm's data is properly removed
212225
item.$compiler.observer.on('hook:afterDestroy', function () {
@@ -225,7 +238,7 @@ module.exports = {
225238
}
226239
},
227240

228-
unbind: function () {
241+
reset: function () {
229242
if (this.childId) {
230243
delete this.vm.$[this.childId]
231244
}
@@ -242,5 +255,9 @@ module.exports = {
242255
ctn.removeEventListener(handlers[key].event, handlers[key])
243256
}
244257
ctn.vue_dHandlers = null
258+
},
259+
260+
unbind: function () {
261+
this.reset()
245262
}
246263
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div id="test">
2+
<p v-repeat="numbers" v-on="click: value+=' modified'">{{value}}</p>
3+
</div>
4+
5+
<script src="../../../dist/vue.js"></script>
6+
<script>
7+
var numbers = [1, 2, 'text']
8+
new Vue({
9+
el: '#test',
10+
data: {
11+
numbers: numbers
12+
}
13+
})
14+
</script>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* global numbers */
2+
3+
casper.test.begin('Repeated Primitives', 8, function (test) {
4+
5+
casper
6+
.start('./fixtures/repeated-primitive.html')
7+
.then(function () {
8+
9+
test.assertSelectorHasText('p:nth-child(1)', '1')
10+
test.assertSelectorHasText('p:nth-child(2)', '2')
11+
test.assertSelectorHasText('p:nth-child(3)', 'text')
12+
13+
})
14+
// click everything to test event handlers (delegated)
15+
.thenClick('p:nth-child(1)', function () {
16+
test.assertSelectorHasText('p:nth-child(1)', '1 modified')
17+
})
18+
.thenClick('p:nth-child(2)', function () {
19+
test.assertSelectorHasText('p:nth-child(2)', '2 modified')
20+
})
21+
.thenClick('p:nth-child(3)', function () {
22+
test.assertSelectorHasText('p:nth-child(3)', 'text modified')
23+
})
24+
// more clicks
25+
.thenClick('p:nth-child(1)', function () {
26+
test.assertSelectorHasText('p:nth-child(1)', '1 modified modified')
27+
})
28+
.then(function () {
29+
test.assertEvalEquals(function () {
30+
return numbers
31+
}, ['1 modified modified', '2 modified', 'text modified'])
32+
})
33+
.run(function () {
34+
test.done()
35+
})
36+
37+
})

test/unit/specs/directive.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,16 +289,17 @@ describe('UNIT: Directive', function () {
289289
assert.strictEqual(unbound, false)
290290
})
291291

292-
it('should call _unbind() if it has an element', function () {
292+
it('should call _unbind() and null everything if it has an element', function () {
293293
d.el = true
294-
d.unbind(true)
294+
d.unbind()
295295
assert.strictEqual(unbound, true)
296-
assert.ok(d.el)
296+
assert.ok(d.el === null && d.vm === null && d.binding === null && d.compiler === null)
297297
})
298298

299-
it('should null everything if it\'s called for VM destruction', function () {
299+
it('should not execute if called more than once', function () {
300+
unbound = false
300301
d.unbind()
301-
assert.ok(d.el === null && d.vm === null && d.binding === null && d.compiler === null)
302+
assert.notOk(unbound)
302303
})
303304

304305
})

test/unit/specs/viewmodel.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ describe('UNIT: ViewModel', function () {
370370
dirUnbindCalled = false,
371371
expUnbindCalled = false,
372372
bindingUnbindCalled = false,
373-
unobserveCalled = 0,
373+
unobserveCalled = false,
374374
elRemoved = false
375375

376376
var dirMock = {
@@ -388,14 +388,6 @@ describe('UNIT: ViewModel', function () {
388388
test: {
389389
root: true,
390390
key: 'test',
391-
value: {
392-
__observer__: {
393-
off: function () {
394-
unobserveCalled++
395-
return this
396-
}
397-
}
398-
},
399391
unbind: function () {
400392
bindingUnbindCalled = true
401393
}
@@ -411,12 +403,21 @@ describe('UNIT: ViewModel', function () {
411403
afterDestroyCalled = true
412404
}
413405
},
406+
data: {
407+
__observer__: {
408+
off: function () {
409+
unobserveCalled = true
410+
return this
411+
}
412+
}
413+
},
414414
observer: {
415415
off: function () {
416416
observerOffCalled = true
417417
},
418418
proxies: {
419-
'test.': {}
419+
'test.': {},
420+
'': {}
420421
}
421422
},
422423
emitter: {
@@ -464,6 +465,10 @@ describe('UNIT: ViewModel', function () {
464465
assert.ok(emitterOffCalled)
465466
})
466467

468+
it('should unobserve the data', function () {
469+
assert.ok(unobserveCalled)
470+
})
471+
467472
it('should unbind all directives', function () {
468473
assert.ok(dirUnbindCalled)
469474
})
@@ -478,7 +483,6 @@ describe('UNIT: ViewModel', function () {
478483

479484
it('should unbind and unobserve own bindings', function () {
480485
assert.ok(bindingUnbindCalled)
481-
assert.strictEqual(unobserveCalled, 3)
482486
})
483487

484488
it('should remove self from parentCompiler', function () {

0 commit comments

Comments
 (0)