@@ -4,27 +4,22 @@ var textParser = require('../parsers/text')
4
4
var dirParser = require ( '../parsers/directive' )
5
5
var templateParser = require ( '../parsers/template' )
6
6
7
+ module . exports = compile
8
+
7
9
/**
8
10
* Compile a template and return a reusable composite link
9
11
* function, which recursively contains more link functions
10
12
* inside. This top level compile function should only be
11
13
* called on instance root nodes.
12
14
*
13
- * When the `asParent` flag is true, this means we are doing
14
- * a partial compile for a component's parent scope markup
15
- * (See #502). This could **only** be triggered during
16
- * compilation of `v-component`, and we need to skip v-with,
17
- * v-ref & v-component in this situation.
18
- *
19
15
* @param {Element|DocumentFragment } el
20
16
* @param {Object } options
21
17
* @param {Boolean } partial
22
- * @param {Boolean } asParent - compiling a component
23
- * container as its parent.
18
+ * @param {Boolean } transcluded
24
19
* @return {Function }
25
20
*/
26
21
27
- module . exports = function compile ( el , options , partial , asParent ) {
22
+ function compile ( el , options , partial , transcluded ) {
28
23
var isBlock = el . nodeType === 11
29
24
var params = ! partial && options . paramAttributes
30
25
// if el is a fragment, this is a block instance
@@ -37,7 +32,7 @@ module.exports = function compile (el, options, partial, asParent) {
37
32
: null
38
33
var nodeLinkFn = isBlock
39
34
? null
40
- : compileNode ( el , options , asParent )
35
+ : compileNode ( el , options )
41
36
var childLinkFn =
42
37
! ( nodeLinkFn && nodeLinkFn . terminal ) &&
43
38
el . tagName !== 'SCRIPT' &&
@@ -57,12 +52,16 @@ module.exports = function compile (el, options, partial, asParent) {
57
52
58
53
return function link ( vm , el ) {
59
54
var originalDirCount = vm . _directives . length
55
+ var parentOriginalDirCount =
56
+ vm . $parent && vm . $parent . _directives . length
60
57
if ( paramsLinkFn ) {
61
58
var paramsEl = isBlock ? el . childNodes [ 1 ] : el
62
59
paramsLinkFn ( vm , paramsEl )
63
60
}
64
61
// cache childNodes before linking parent, fix #657
65
62
var childNodes = _ . toArray ( el . childNodes )
63
+ // if transcluded, link in parent scope
64
+ if ( transcluded ) vm = vm . $parent
66
65
if ( nodeLinkFn ) nodeLinkFn ( vm , el )
67
66
if ( childLinkFn ) childLinkFn ( vm , childNodes )
68
67
@@ -73,16 +72,26 @@ module.exports = function compile (el, options, partial, asParent) {
73
72
* linking.
74
73
*/
75
74
76
- if ( partial ) {
77
- var dirs = vm . _directives . slice ( originalDirCount )
78
- return function unlink ( ) {
75
+ if ( partial && ! transcluded ) {
76
+ var selfDirs = vm . _directives . slice ( originalDirCount )
77
+ var parentDirs = vm . $parent &&
78
+ vm . $parent . _directives . slice ( parentOriginalDirCount )
79
+
80
+ var teardownDirs = function ( vm , dirs ) {
79
81
var i = dirs . length
80
82
while ( i -- ) {
81
83
dirs [ i ] . _teardown ( )
82
84
}
83
85
i = vm . _directives . indexOf ( dirs [ 0 ] )
84
86
vm . _directives . splice ( i , dirs . length )
85
87
}
88
+
89
+ return function unlink ( ) {
90
+ teardownDirs ( vm , selfDirs )
91
+ if ( parentDirs ) {
92
+ teardownDirs ( vm . $parent , parentDirs )
93
+ }
94
+ }
86
95
}
87
96
}
88
97
}
@@ -93,14 +102,13 @@ module.exports = function compile (el, options, partial, asParent) {
93
102
*
94
103
* @param {Node } node
95
104
* @param {Object } options
96
- * @param {Boolean } asParent
97
105
* @return {Function|null }
98
106
*/
99
107
100
- function compileNode ( node , options , asParent ) {
108
+ function compileNode ( node , options ) {
101
109
var type = node . nodeType
102
110
if ( type === 1 && node . tagName !== 'SCRIPT' ) {
103
- return compileElement ( node , options , asParent )
111
+ return compileElement ( node , options )
104
112
} else if ( type === 3 && config . interpolate && node . data . trim ( ) ) {
105
113
return compileTextNode ( node , options )
106
114
} else {
@@ -113,14 +121,20 @@ function compileNode (node, options, asParent) {
113
121
*
114
122
* @param {Element } el
115
123
* @param {Object } options
116
- * @param {Boolean } asParent
117
124
* @return {Function|null }
118
125
*/
119
126
120
- function compileElement ( el , options , asParent ) {
127
+ function compileElement ( el , options ) {
128
+ if ( checkTransclusion ( el ) ) {
129
+ // unwrap textNode
130
+ if ( el . hasAttribute ( '__vue__wrap' ) ) {
131
+ el = el . firstChild
132
+ }
133
+ return compile ( el , options . _parent . $options , true , true )
134
+ }
121
135
var linkFn , tag , component
122
136
// check custom element component, but only on non-root
123
- if ( ! asParent && ! el . __vue__ ) {
137
+ if ( ! el . __vue__ ) {
124
138
tag = el . tagName . toLowerCase ( )
125
139
component =
126
140
tag . indexOf ( '-' ) > 0 &&
@@ -131,12 +145,10 @@ function compileElement (el, options, asParent) {
131
145
}
132
146
if ( component || el . hasAttributes ( ) ) {
133
147
// check terminal direcitves
134
- if ( ! asParent ) {
135
- linkFn = checkTerminalDirectives ( el , options )
136
- }
148
+ linkFn = checkTerminalDirectives ( el , options )
137
149
// if not terminal, build normal link function
138
150
if ( ! linkFn ) {
139
- var dirs = collectDirectives ( el , options , asParent )
151
+ var dirs = collectDirectives ( el , options )
140
152
linkFn = dirs . length
141
153
? makeDirectivesLinkFn ( dirs )
142
154
: null
@@ -166,16 +178,21 @@ function makeDirectivesLinkFn (directives) {
166
178
return function directivesLinkFn ( vm , el ) {
167
179
// reverse apply because it's sorted low to high
168
180
var i = directives . length
169
- var dir , j , k
181
+ var dir , j , k , target
170
182
while ( i -- ) {
171
183
dir = directives [ i ]
184
+ // a directive can be transcluded if it's written
185
+ // on a component's container in its parent tempalte.
186
+ target = dir . transcluded
187
+ ? vm . $parent
188
+ : vm
172
189
if ( dir . _link ) {
173
190
// custom link fn
174
- dir . _link ( vm , el )
191
+ dir . _link ( target , el )
175
192
} else {
176
193
k = dir . descriptors . length
177
194
for ( j = 0 ; j < k ; j ++ ) {
178
- vm . _bindDir ( dir . name , el ,
195
+ target . _bindDir ( dir . name , el ,
179
196
dir . descriptors [ j ] , dir . def )
180
197
}
181
198
}
@@ -478,38 +495,37 @@ function makeTeriminalLinkFn (el, dirName, value, options) {
478
495
*
479
496
* @param {Element } el
480
497
* @param {Object } options
481
- * @param {Boolean } asParent
482
498
* @return {Array }
483
499
*/
484
500
485
- function collectDirectives ( el , options , asParent ) {
501
+ function collectDirectives ( el , options ) {
486
502
var attrs = _ . toArray ( el . attributes )
487
503
var i = attrs . length
488
504
var dirs = [ ]
489
- var attr , attrName , dir , dirName , dirDef
505
+ var attr , attrName , dir , dirName , dirDef , transcluded
490
506
while ( i -- ) {
491
507
attr = attrs [ i ]
492
508
attrName = attr . name
509
+ transcluded =
510
+ options . _transcludedAttrs &&
511
+ options . _transcludedAttrs [ attrName ]
493
512
if ( attrName . indexOf ( config . prefix ) === 0 ) {
494
513
dirName = attrName . slice ( config . prefix . length )
495
- if ( asParent &&
496
- ( dirName === 'with' ||
497
- dirName === 'component' ) ) {
498
- continue
499
- }
500
514
dirDef = options . directives [ dirName ]
501
515
_ . assertAsset ( dirDef , 'directive' , dirName )
502
516
if ( dirDef ) {
503
517
dirs . push ( {
504
518
name : dirName ,
505
519
descriptors : dirParser . parse ( attr . value ) ,
506
- def : dirDef
520
+ def : dirDef ,
521
+ transcluded : transcluded
507
522
} )
508
523
}
509
524
} else if ( config . interpolate ) {
510
525
dir = collectAttrDirective ( el , attrName , attr . value ,
511
526
options )
512
527
if ( dir ) {
528
+ dir . transcluded = transcluded
513
529
dirs . push ( dir )
514
530
}
515
531
}
@@ -531,10 +547,6 @@ function collectDirectives (el, options, asParent) {
531
547
*/
532
548
533
549
function collectAttrDirective ( el , name , value , options ) {
534
- if ( options . _skipAttrs &&
535
- options . _skipAttrs . indexOf ( name ) > - 1 ) {
536
- return
537
- }
538
550
var tokens = textParser . parse ( value )
539
551
if ( tokens ) {
540
552
var def = options . directives . attr
@@ -572,4 +584,19 @@ function directiveComparator (a, b) {
572
584
a = a . def . priority || 0
573
585
b = b . def . priority || 0
574
586
return a > b ? 1 : - 1
587
+ }
588
+
589
+ /**
590
+ * Check whether an element is transcluded
591
+ *
592
+ * @param {Element } el
593
+ * @return {Boolean }
594
+ */
595
+
596
+ var transcludedFlagAttr = '__vue__transcluded'
597
+ function checkTransclusion ( el ) {
598
+ if ( el . nodeType === 1 && el . hasAttribute ( transcludedFlagAttr ) ) {
599
+ el . removeAttribute ( transcludedFlagAttr )
600
+ return true
601
+ }
575
602
}
0 commit comments