Skip to content

Commit

Permalink
ensure slot compilation order inside conditional fragments (fix vuejs…
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Dec 10, 2015
1 parent 62229ef commit 2b9e072
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 43 deletions.
8 changes: 7 additions & 1 deletion src/compiler/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
findRef,
defineReactive,
assertAsset,
getAttr
getAttr,
hasBindAttr
} from '../util/index'

// special binding prefixes
Expand Down Expand Up @@ -514,6 +515,11 @@ function makeChildLinkFn (linkFns) {
function checkElementDirectives (el, options) {
var tag = el.tagName.toLowerCase()
if (commonTagRE.test(tag)) return
// special case: give named slot a higher priority
// than unnamed slots
if (tag === 'slot' && hasBindAttr(el, 'name')) {
tag = '_namedSlot'
}
var def = resolveAsset(options, 'elementDirectives', tag)
if (def) {
return makeTerminalNodeLinkFn(el, tag, '', options, def)
Expand Down
7 changes: 3 additions & 4 deletions src/compiler/transclude.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
createAnchor,
resolveAsset,
toArray,
addClass
addClass,
hasBindAttr
} from '../util/index'

const specialCharRE = /[^\w\-:\.]/
Expand Down Expand Up @@ -92,9 +93,7 @@ function transcludeTemplate (el, options) {
// single nested component
tag === 'component' ||
resolveAsset(options, 'components', tag) ||
replacer.hasAttribute('is') ||
replacer.hasAttribute(':is') ||
replacer.hasAttribute('v-bind:is') ||
hasBindAttr(replacer, 'is') ||
// element directive
resolveAsset(options, 'elementDirectives', tag) ||
// for block
Expand Down
3 changes: 2 additions & 1 deletion src/directives/element/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import slot from './slot'
import { slot, namedSlot as _namedSlot } from './slot'
import partial from './partial'

export default {
slot,
_namedSlot, // same as slot but with higher priority
partial
}
60 changes: 28 additions & 32 deletions src/directives/element/slot.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from '../../parsers/template'

import {
extend,
extractContent,
replace,
remove,
Expand All @@ -15,60 +16,46 @@ import {
// instance being stored as `$options._content` during
// the transclude phase.

export default {
// We are exporting two versions, one for named and one
// for unnamed, because the unnamed slots must be compiled
// AFTER all named slots have selected their content. So
// we need to give them different priorities in the compilation
// process. (See #1965)

priority: 1750,
export const slot = {

params: ['name'],
priority: 1750,

bind () {
var host = this.vm
var raw = host.$options._content
var content
if (!raw) {
this.fallback()
return
}
var context = host._context
var slotName = this.params.name
var slotName = this.params && this.params.name
if (!slotName) {
// Default content
var self = this
var compileDefaultContent = function () {
var content = extractFragment(raw.childNodes, raw, true)
if (content.hasChildNodes()) {
self.compile(content, context, host)
} else {
self.fallback()
}
}
if (!host._isCompiled) {
// defer until the end of instance compilation,
// because the default outlet must wait until all
// other possible outlets with selectors have picked
// out their contents.
host.$once('hook:compiled', compileDefaultContent)
} else {
compileDefaultContent()
}
// Default slot
this.tryCompile(extractFragment(raw.childNodes, raw, true), context, host)
} else {
// Named slot
var selector = '[slot="' + slotName + '"]'
var nodes = raw.querySelectorAll(selector)
if (nodes.length) {
content = extractFragment(nodes, raw)
if (content.hasChildNodes()) {
this.compile(content, context, host)
} else {
this.fallback()
}
this.tryCompile(extractFragment(nodes, raw), context, host)
} else {
this.fallback()
}
}
},

fallback () {
this.compile(extractContent(this.el, true), this.vm)
tryCompile (content, context, host) {
if (content.hasChildNodes()) {
this.compile(content, context, host)
} else {
this.fallback()
}
},

compile (content, context, host) {
Expand All @@ -87,13 +74,22 @@ export default {
}
},

fallback () {
this.compile(extractContent(this.el, true), this.vm)
},

unbind () {
if (this.unlink) {
this.unlink()
}
}
}

export const namedSlot = extend(extend({}, slot), {
priority: slot.priority + 1,
params: ['name']
})

/**
* Extract qualified content nodes from a node list.
*
Expand Down
14 changes: 14 additions & 0 deletions src/util/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ export function getBindAttr (node, name) {
return val
}

/**
* Check the presence of a bind attribute.
*
* @param {Node} node
* @param {String} name
* @return {Boolean}
*/

export function hasBindAttr (node, name) {
return node.hasAttribute(name) ||
node.hasAttribute(':' + name) ||
node.hasAttribute('v-bind:' + name)
}

/**
* Insert el before target
*
Expand Down
11 changes: 6 additions & 5 deletions test/unit/specs/directives/element/slot_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,27 +169,28 @@ describe('Slot Distribution', function () {
el: el,
data: {
a: 1,
b: 2,
show: true
},
template: '<test :show="show">{{a}}</test>',
template: '<test :show="show"><p slot="b">{{b}}</a><p>{{a}}</p></test>',
components: {
test: {
props: ['show'],
template: '<div v-if="show"><slot></cotent></div>'
template: '<div v-if="show"><slot></slot><slot name="b"></slot></div>'
}
}
})
expect(el.textContent).toBe('1')
expect(el.textContent).toBe('12')
vm.a = 2
_.nextTick(function () {
expect(el.textContent).toBe('2')
expect(el.textContent).toBe('22')
vm.show = false
_.nextTick(function () {
expect(el.textContent).toBe('')
vm.show = true
vm.a = 3
_.nextTick(function () {
expect(el.textContent).toBe('3')
expect(el.textContent).toBe('32')
done()
})
})
Expand Down

0 comments on commit 2b9e072

Please sign in to comment.