Skip to content

Commit c748bb3

Browse files
committed
Merge pull request #2272 from kazupon/improve/terminal-directive
Improve terminalable directive
2 parents 30e9eda + 9236191 commit c748bb3

File tree

4 files changed

+78
-30
lines changed

4 files changed

+78
-30
lines changed

src/compiler/compile.js

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,9 @@ const dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/
2525
const modifierRE = /\.[^\.]+/g
2626
const transitionRE = /^(v-bind:|:)?transition$/
2727

28-
// terminal directives
29-
export const terminalDirectives = [
30-
'for',
31-
'if'
32-
]
33-
3428
// default directive priority
3529
const DEFAULT_PRIORITY = 1000
30+
const DEFAULT_TERMINAL_PRIORITY = 2000
3631

3732
/**
3833
* Compile a template and return a reusable composite link
@@ -588,14 +583,28 @@ function checkTerminalDirectives (el, options) {
588583
return skip
589584
}
590585
}
591-
var value, dirName
592-
for (var i = 0, l = terminalDirectives.length; i < l; i++) {
593-
dirName = terminalDirectives[i]
594-
value = el.getAttribute('v-' + dirName)
595-
if (value != null) {
596-
return makeTerminalNodeLinkFn(el, dirName, value, options)
586+
587+
var attrs, attr, name, value, matched, dirName, arg, def, termDef
588+
attrs = el.attributes
589+
for (var i = 0, j = attrs.length; i < j; i++) {
590+
attr = attrs[i]
591+
if ((matched = attr.name.match(dirAttrRE))) {
592+
def = resolveAsset(options, 'directives', matched[1])
593+
if (def && def.terminal) {
594+
if (!termDef || ((def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority)) {
595+
termDef = def
596+
name = attr.name
597+
value = attr.value
598+
dirName = matched[1]
599+
arg = matched[2]
600+
}
601+
}
597602
}
598603
}
604+
605+
if (termDef) {
606+
return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, name, arg)
607+
}
599608
}
600609

601610
function skip () {}
@@ -611,20 +620,28 @@ skip.terminal = true
611620
* @param {String} dirName
612621
* @param {String} value
613622
* @param {Object} options
614-
* @param {Object} [def]
623+
* @param {Object} def
624+
* @param {String} [attrName]
625+
* @param {String} [arg]
615626
* @return {Function} terminalLinkFn
616627
*/
617628

618-
function makeTerminalNodeLinkFn (el, dirName, value, options, def) {
629+
function makeTerminalNodeLinkFn (el, dirName, value, options, def, attrName, arg) {
619630
var parsed = parseDirective(value)
620631
var descriptor = {
621632
name: dirName,
622633
expression: parsed.expression,
623634
filters: parsed.filters,
624635
raw: value,
625-
// either an element directive, or if/for
626-
// #2366 or custom terminal directive
627-
def: def || resolveAsset(options, 'directives', dirName)
636+
rawName: attrName,
637+
def: def
638+
}
639+
if (attrName) {
640+
descriptor.rawName = attrName
641+
descriptor.modifiers = parseModifiers(attrName)
642+
}
643+
if (arg) {
644+
descriptor.arg = arg.replace(modifierRE, '')
628645
}
629646
// check ref for v-for and router-view
630647
if (dirName === 'for' || dirName === 'router-view') {

src/directives/public/for.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ let uid = 0
2222
const vFor = {
2323

2424
priority: FOR,
25+
terminal: true,
2526

2627
params: [
2728
'track-by',

src/directives/public/if.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
export default {
1212

1313
priority: IF,
14+
terminal: true,
1415

1516
bind () {
1617
var el = this.el

test/unit/specs/compiler/compile_spec.js

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,48 @@ describe('Compile', function () {
249249
expect(args[1]).toBe(el.firstChild)
250250
})
251251

252+
it('custom terminal directives', function () {
253+
var defTerminal = {
254+
terminal: true,
255+
priority: Vue.options.directives.if.priority + 1
256+
}
257+
var options = _.mergeOptions(Vue.options, {
258+
directives: { term: defTerminal }
259+
})
260+
el.innerHTML = '<div v-term:arg1.modifier1.modifier2="foo"></div>'
261+
var linker = compile(el, options)
262+
linker(vm, el)
263+
expect(vm._bindDir.calls.count()).toBe(1)
264+
var args = vm._bindDir.calls.argsFor(0)
265+
expect(args[0].name).toBe('term')
266+
expect(args[0].expression).toBe('foo')
267+
expect(args[0].rawName).toBe('v-term:arg1.modifier1.modifier2')
268+
expect(args[0].arg).toBe('arg1')
269+
expect(args[0].modifiers.modifier1).toBe(true)
270+
expect(args[0].modifiers.modifier2).toBe(true)
271+
expect(args[0].def).toBe(defTerminal)
272+
})
273+
274+
it('custom terminal directives priority', function () {
275+
var defTerminal = {
276+
terminal: true,
277+
priority: Vue.options.directives.if.priority + 1
278+
}
279+
var options = _.mergeOptions(Vue.options, {
280+
directives: { term: defTerminal }
281+
})
282+
el.innerHTML = '<div v-term:arg1 v-if="ok"></div>'
283+
var linker = compile(el, options)
284+
linker(vm, el)
285+
expect(vm._bindDir.calls.count()).toBe(1)
286+
var args = vm._bindDir.calls.argsFor(0)
287+
expect(args[0].name).toBe('term')
288+
expect(args[0].expression).toBe('')
289+
expect(args[0].rawName).toBe('v-term:arg1')
290+
expect(args[0].arg).toBe('arg1')
291+
expect(args[0].def).toBe(defTerminal)
292+
})
293+
252294
it('custom element components', function () {
253295
var options = _.mergeOptions(Vue.options, {
254296
components: {
@@ -627,17 +669,4 @@ describe('Compile', function () {
627669
expect(el.textContent).toBe('worked!')
628670
expect(getWarnCount()).toBe(0)
629671
})
630-
631-
it('allow custom terminal directive', function () {
632-
Vue.mixin({}) // #2366 conflict with custom terminal directive
633-
Vue.compiler.terminalDirectives.push('foo')
634-
Vue.directive('foo', {})
635-
636-
new Vue({
637-
el: el,
638-
template: '<div v-foo></div>'
639-
})
640-
641-
expect(getWarnCount()).toBe(0)
642-
})
643672
})

0 commit comments

Comments
 (0)