-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
dynamic contents in v-if (in component's template) aren't persistent #804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for isolating the issue. Indeed this is due to transcluded content being snapshotted across |
I find that the directive placeholder comments are static when as <my-comp>
<div v-repeat='somewhat'>Do Something</div>
<div v-component='some-comp'>Some Content</div>
<div v-partial='some-partial'>Some Partial</div>
<div v-if='some-condition'>Do Something</div>
</my-comp>
<script type='text/template' id='my-comp'>
<div v-if='some-condition'>
<content></content>
</div>
</script> When compiling the transcluded template for my-comp, it will be: <my-comp>
<div v-if='some-condition'>
<div>Do Something</div>
...
<!--v-repeat-->
<div>Some Content...</div>
<!--v-component-->
<div>Some Partial</div>
<!--v-partial-->
<!--v-if-start-->
<div>Do Something</div>
<!--v-if-end-->
</div>
</my-comp> So if the directive comments are all like Here are some experimental code for checkContent: function (el) {
el = el || this.el
var stack = 0
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i]
// _isContent is a flag set in instance/compile
// after the raw content has been compiled by parent
if (node._isContent) {
if (node.nodeType === 8) {
if (node._isEnd && !(--stack)) {
this.placeholderNodes[this.placeholderNodes.length - 1][1] = node
this.placeholderPositions[this.placeholderPositions.length - 1][1] = i;
} else if (node._isStart) {
if (!stack) {
;(this.placeholderNodes = this.placeholderNodes || []).push([node])
;(this.placeholderPositions = this.placeholderPositions || []).push([i])
}
++stack
}
} else if (!stack) {
;(this.contentNodes = this.contentNodes || []).push(node)
;(this.contentPositions = this.contentPositions || []).push(i)
}
}
}
// keep track of any transcluded components contained within
// the conditional block. we need to call attach/detach hooks
// for them.
this.transCpnts =
this.vm._transCpnts &&
this.vm._transCpnts.filter(function (c) {
return el.contains(c.$el)
})
},
update: function (value) {
if (this.invalid) return
if (value) {
// avoid duplicate compiles, since update() can be
// called with different truthy values
if (!this.unlink) {
var frag = templateParser.clone(this.template)
// persist content nodes from parent.
this.transclude(frag)
this.compile(frag)
}
} else {
this.teardown()
}
},
transclude: function (frag) {
var el = frag.childNodes[0]
if (this.contentNodes) {
for (var i = 0, l = this.contentNodes.length; i < l; ++i) {
var node = this.contentNodes[i]
var j = this.contentPositions[i]
el.replaceChild(node, el.childNodes[j])
}
}
if (this.placeholderNodes) {
var offset = 0
for (var i = 0, l = this.placeholderNodes.length; i < l; ++i) {
var startNode = this.placeholderNodes[i][0]
var endNode = this.placeholderNodes[i][1]
var start = this.placeholderPositions[i][0] + offset
var end = this.placeholderPositions[i][1] + offset
var len = end - start
var node = startNode.nextSibling
var j = 1
while (node !== endNode) {
if (j < len) {
el.replaceChild(node, el.childNodes[start + j])
} else {
el.insertBefore(node, el.childNodes[end++])
}
++j
// it is safe to skip compiling the node and its children
node.setAttribute('v-pre', '')
node = startNode.nextSibling
}
if (j < len) {
offset += len - j
while (j < len) {
el.removeChild(el.childNodes[j++])
}
} else {
offset += j - len
}
el.replaceChild(startNode, el.childNodes[start])
el.replaceChild(endNode, el.childNodes[end])
}
/* If offset is not zero, it means the structure changed,
* we have to re-compile to adjust the linkFns.
* I think there is a better way to re-structure the linkFns.
*/
if (offset) {
this.template = templateParser.clone(frag)
if (this.contentNodes) {
this.contentNodes = []
this.contentPositions = []
}
this.placeholderNodes = []
this.placeholderPositions = []
this.checkContent(el)
this.linker = compile(
this.template,
this.vm.$options,
true
)
}
}
} Using this strategy, if there is no dynamic |
I find that this strategy only works for |
I've created a new branch with some major refactoring of transclusion logic: https://github.com/yyx990803/vue/tree/transclude-refactor It works correctly with your jsfiddle demo, and includes a test case for |
Thank you, the strategy used in branch has also solved some another problems. |
If We have a markup
[v-if] > content
and the content is dynamic, as long as the content changes and make the[v-if]
hides->shows, the content in[v-if]
will back to the previous [demo]. To find the actual problem, I check the source of vuejs and find that the[v-if]
only snapshots the content when binding, and I also find that the implements forinstance/compile
(snapshot for_transCpnts
) andcompile
in[v-if]
(snapshot forchildren
) will occur some problems (e.g., not fire the detach event) in some situations.The text was updated successfully, but these errors were encountered: