Skip to content

Commit 52f6d2f

Browse files
committed
compile block instance container attributes separately (fix vuejs#805)
1 parent 1166f87 commit 52f6d2f

File tree

6 files changed

+74
-28
lines changed

6 files changed

+74
-28
lines changed

src/compiler/compile.js

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@ module.exports = compile
2121

2222
function compile (el, options, partial, transcluded) {
2323
var isBlock = el.nodeType === 11
24-
var params = !partial && options.paramAttributes
25-
// if el is a fragment, this is a block instance
26-
// and paramAttributes will be stored on the first
27-
// element in the template. (excluding the _blockStart
28-
// comment node)
29-
var paramsEl = isBlock ? el.childNodes[1] : el
30-
var paramsLinkFn = params
31-
? compileParamAttributes(paramsEl, params, options)
24+
// link function for param attributes.
25+
var params = options.paramAttributes
26+
var paramsLinkFn = params && !partial && !transcluded && !isBlock
27+
? compileParamAttributes(el, params, options)
3228
: null
29+
// link function for the node itself.
30+
// if this is a block instance, we return a link function
31+
// for the attributes found on the container, if any.
32+
// options._containerAttrs are collected during transclusion.
3333
var nodeLinkFn = isBlock
34-
? null
34+
? compileBlockContainer(options._containerAttrs, params, options)
3535
: compileNode(el, options)
36+
// link function for the childNodes
3637
var childLinkFn =
3738
!(nodeLinkFn && nodeLinkFn.terminal) &&
3839
el.tagName !== 'SCRIPT' &&
@@ -41,22 +42,21 @@ function compile (el, options, partial, transcluded) {
4142
: null
4243

4344
/**
44-
* A linker function to be called on a already compiled
45-
* piece of DOM, which instantiates all directive
45+
* A composite linker function to be called on a already
46+
* compiled piece of DOM, which instantiates all directive
4647
* instances.
4748
*
4849
* @param {Vue} vm
4950
* @param {Element|DocumentFragment} el
5051
* @return {Function|undefined}
5152
*/
5253

53-
function linkFn (vm, el) {
54+
function compositeLinkFn (vm, el) {
5455
var originalDirCount = vm._directives.length
5556
var parentOriginalDirCount =
5657
vm.$parent && vm.$parent._directives.length
5758
if (paramsLinkFn) {
58-
var paramsEl = isBlock ? el.childNodes[1] : el
59-
paramsLinkFn(vm, paramsEl)
59+
paramsLinkFn(vm, el)
6060
}
6161
// cache childNodes before linking parent, fix #657
6262
var childNodes = _.toArray(el.childNodes)
@@ -102,10 +102,44 @@ function compile (el, options, partial, transcluded) {
102102
// transcluded linkFns are terminal, because it takes
103103
// over the entire sub-tree.
104104
if (transcluded) {
105-
linkFn.terminal = true
105+
compositeLinkFn.terminal = true
106106
}
107107

108-
return linkFn
108+
return compositeLinkFn
109+
}
110+
111+
/**
112+
* Compile the attributes found on a "block container" -
113+
* i.e. the container node in the parent tempate of a block
114+
* instance. We are only concerned with v-with and
115+
* paramAttributes here.
116+
*
117+
* @param {Object} attrs - a map of attr name/value pairs
118+
* @param {Array} params - param attributes list
119+
* @param {Object} options
120+
* @return {Function}
121+
*/
122+
123+
function compileBlockContainer (attrs, params, options) {
124+
if (!attrs) return null
125+
var paramsLinkFn = params
126+
? compileParamAttributes(attrs, params, options)
127+
: null
128+
var withVal = attrs[config.prefix + 'with']
129+
var withLinkFn = null
130+
if (withVal) {
131+
var descriptor = dirParser.parse(withVal)[0]
132+
var def = options.directives['with']
133+
withLinkFn = function (vm, el) {
134+
vm._bindDir('with', el, descriptor, def)
135+
}
136+
}
137+
return function blockContainerLinkFn (vm) {
138+
// explicitly passing null to the linkers
139+
// since v-with doesn't need a real element
140+
if (paramsLinkFn) paramsLinkFn(vm, null)
141+
if (withLinkFn) withLinkFn(vm, null)
142+
}
109143
}
110144

111145
/**
@@ -363,14 +397,15 @@ function makeChildLinkFn (linkFns) {
363397
* Compile param attributes on a root element and return
364398
* a paramAttributes link function.
365399
*
366-
* @param {Element} el
400+
* @param {Element|Object} el
367401
* @param {Array} attrs
368402
* @param {Object} options
369403
* @return {Function} paramsLinkFn
370404
*/
371405

372406
function compileParamAttributes (el, attrs, options) {
373407
var params = []
408+
var isEl = el.nodeType
374409
var i = attrs.length
375410
var name, value, param
376411
while (i--) {
@@ -384,15 +419,15 @@ function compileParamAttributes (el, attrs, options) {
384419
'http://vuejs.org/api/options.html#paramAttributes'
385420
)
386421
}
387-
value = el.getAttribute(name)
422+
value = isEl ? el.getAttribute(name) : el[name]
388423
if (value !== null) {
389424
param = {
390425
name: name,
391426
value: value
392427
}
393428
var tokens = textParser.parse(value)
394429
if (tokens) {
395-
el.removeAttribute(name)
430+
if (isEl) el.removeAttribute(name)
396431
if (tokens.length > 1) {
397432
_.warn(
398433
'Invalid param attribute binding: "' +

src/compiler/transclude.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ var transcludedFlagAttr = '__vue__transcluded'
1717

1818
module.exports = function transclude (el, options) {
1919
if (options && options._asComponent) {
20+
// mutating the options object here assuming the same
21+
// object will be used for compile right after this
22+
options._transcludedAttrs = extractAttrs(el.attributes)
2023
// Mark content nodes and attrs so that the compiler
2124
// knows they should be compiled in parent scope.
22-
options._transcludedAttrs = extractAttrs(el.attributes)
2325
var i = el.childNodes.length
2426
while (i--) {
2527
var node = el.childNodes[i]
@@ -70,8 +72,20 @@ function transcludeTemplate (el, options) {
7072
var rawContent = options._content || _.extractContent(el)
7173
if (options.replace) {
7274
if (frag.childNodes.length > 1) {
75+
// this is a block instance which has no root node.
76+
// however, the container in the parent template
77+
// (which is replaced here) may contain v-with and
78+
// paramAttributes that still need to be compiled
79+
// for the child. we store all the container
80+
// attributes on the options object and pass it down
81+
// to the compiler.
82+
var containerAttrs = options._containerAttrs = {}
83+
var i = el.attributes.length
84+
while (i--) {
85+
var attr = el.attributes[i]
86+
containerAttrs[attr.name] = attr.value
87+
}
7388
transcludeContent(frag, rawContent)
74-
_.copyAttributes(el, frag.firstChild)
7589
return frag
7690
} else {
7791
var replacer = frag.firstChild

src/directive.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var p = Directive.prototype
5151
*/
5252

5353
p._bind = function (def) {
54-
if (this.name !== 'cloak' && this.el.removeAttribute) {
54+
if (this.name !== 'cloak' && this.el && this.el.removeAttribute) {
5555
this.el.removeAttribute(config.prefix + this.name)
5656
}
5757
if (typeof def === 'function') {

src/directives/repeat.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ module.exports = {
239239
vm.$before(ref)
240240
}
241241
} else {
242-
// make sure to insert before the comment node if
243-
// the vms are block instances
244-
var nextEl = targetNext._blockStart || targetNext.$el
242+
var nextEl = targetNext.$el
245243
if (vm._reused) {
246244
// this is the vm we are actually in front of
247245
currentNext = findNextVm(vm, ref)

src/directives/with.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
var childKey = this.arg || '$data'
1313
var parentKey = this.expression
1414

15-
if (this.el !== child.$el) {
15+
if (this.el && this.el !== child.$el) {
1616
_.warn(
1717
'v-with can only be used on instance root elements.'
1818
)

src/instance/compile.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ exports._compile = function (el) {
4949
exports._initElement = function (el) {
5050
if (el instanceof DocumentFragment) {
5151
this._isBlock = true
52-
this._blockStart = el.firstChild
53-
this.$el = el.childNodes[1]
52+
this.$el = this._blockStart = el.firstChild
5453
this._blockEnd = el.lastChild
5554
this._blockFragment = el
5655
} else {

0 commit comments

Comments
 (0)