Skip to content

Commit 4983c6c

Browse files
committed
fix content transclusion inside v-if (fix #736)
1 parent 99f011b commit 4983c6c

File tree

4 files changed

+81
-13
lines changed

4 files changed

+81
-13
lines changed

src/directives/if.js

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ module.exports = {
1212
this.end = document.createComment('v-if-end')
1313
_.replace(el, this.end)
1414
_.before(this.start, this.end)
15+
16+
// Note: content transclusion is not available for
17+
// <template> blocks
1518
if (el.tagName === 'TEMPLATE') {
1619
this.template = templateParser.parse(el, true)
1720
} else {
1821
this.template = document.createDocumentFragment()
19-
this.template.appendChild(el)
22+
this.template.appendChild(templateParser.clone(el))
23+
this.checkContent()
2024
}
2125
// compile the nested partial
2226
this.linker = compile(
@@ -33,26 +37,50 @@ module.exports = {
3337
}
3438
},
3539

40+
// check if there are any content nodes from parent.
41+
// these nodes are compiled by the parent and should
42+
// not be cloned during a re-compilation - otherwise the
43+
// parent directives bound to them will no longer work.
44+
// (see #736)
45+
checkContent: function () {
46+
var el = this.el
47+
for (var i = 0; i < el.childNodes.length; i++) {
48+
var node = el.childNodes[i]
49+
// _isContent is a flag set in instance/compile
50+
// after the raw content has been compiled by parent
51+
if (node._isContent) {
52+
;(this.contentNodes = this.contentNodes || []).push(node)
53+
;(this.contentPositions = this.contentPositions || []).push(i)
54+
}
55+
}
56+
},
57+
3658
update: function (value) {
3759
if (this.invalid) return
3860
if (value) {
39-
this.insert()
61+
// avoid duplicate compiles, since update() can be
62+
// called with different truthy values
63+
if (!this.unlink) {
64+
var frag = templateParser.clone(this.template)
65+
// persist content nodes from parent.
66+
if (this.contentNodes) {
67+
var el = frag.childNodes[0]
68+
for (var i = 0, l = this.contentNodes.length; i < l; i++) {
69+
var node = this.contentNodes[i]
70+
var j = this.contentPositions[i]
71+
el.replaceChild(node, el.childNodes[j])
72+
}
73+
}
74+
this.compile(frag)
75+
}
4076
} else {
4177
this.teardown()
4278
}
4379
},
4480

45-
insert: function () {
46-
// avoid duplicate inserts, since update() can be
47-
// called with different truthy values
48-
if (!this.unlink) {
49-
this.compile(this.template)
50-
}
51-
},
52-
53-
compile: function (template) {
81+
// NOTE: this function is shared in v-partial
82+
compile: function (frag) {
5483
var vm = this.vm
55-
var frag = templateParser.clone(template)
5684
var originalChildLength = vm._children.length
5785
this.unlink = this.linker
5886
? this.linker(vm, frag)
@@ -66,6 +94,7 @@ module.exports = {
6694
}
6795
},
6896

97+
// NOTE: this function is shared in v-partial
6998
teardown: function () {
7099
if (!this.unlink) return
71100
transition.blockRemove(this.start, this.end, this.vm)

src/directives/partial.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module.exports = {
3737
var partial = this.vm.$options.partials[id]
3838
_.assertAsset(partial, 'partial', id)
3939
if (partial) {
40-
this.compile(templateParser.parse(partial))
40+
this.compile(templateParser.parse(partial, true))
4141
}
4242
}
4343

src/instance/compile.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ exports._compile = function (el) {
4444
compile(content, parentOptions, true)
4545
// call content linker now, before transclusion
4646
this._contentUnlinkFn = contentLinkFn(parent, content)
47+
// mark all compiled nodes as transcluded, so that
48+
// directives that do partial compilation, e.g. v-if
49+
// and v-partial can detect them and persist them
50+
// through re-compilations.
51+
for (var i = 0; i < content.childNodes.length; i++) {
52+
content.childNodes[i]._isContent = true
53+
}
4754
this._transCpnts = parent._children.slice(ol)
4855
}
4956
// tranclude, this possibly replaces original

test/unit/specs/directives/if_spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,37 @@ if (_.inBrowser) {
180180
expect(_.warn).toHaveBeenCalled()
181181
})
182182

183+
it('v-if with content transclusion', function (done) {
184+
var vm = new Vue({
185+
el: el,
186+
data: {
187+
a: 1,
188+
show: true
189+
},
190+
template: '<div v-component="test" show="{{show}}">{{a}}</div>',
191+
components: {
192+
test: {
193+
paramAttributes: ['show'],
194+
template: '<div v-if="show"><content></cotent></div>'
195+
}
196+
}
197+
})
198+
expect(el.textContent).toBe('1')
199+
vm.a = 2
200+
_.nextTick(function () {
201+
expect(el.textContent).toBe('2')
202+
vm.show = false
203+
_.nextTick(function () {
204+
expect(el.textContent).toBe('')
205+
vm.show = true
206+
vm.a = 3
207+
_.nextTick(function () {
208+
expect(el.textContent).toBe('3')
209+
done()
210+
})
211+
})
212+
})
213+
})
214+
183215
})
184216
}

0 commit comments

Comments
 (0)