Skip to content

Commit 5b61b19

Browse files
committed
compiler: properly expose modifiers to directives (fix vuejs#1399)
1 parent a0fa04d commit 5b61b19

File tree

5 files changed

+74
-50
lines changed

5 files changed

+74
-50
lines changed

src/compiler/compile.js

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ var resolveAsset = _.resolveAsset
1010
// special binding prefixes
1111
var bindRE = /^v-bind:|^:/
1212
var onRE = /^v-on:|^@/
13-
var literalRE = /\.literal$/
1413
var argRE = /:(.*)$/
14+
var modifierRE = /\.[^\.]+/g
1515
var transitionRE = /^(v-bind:|:)?transition$/
1616

1717
// terminal directives
@@ -480,8 +480,10 @@ function checkComponent (el, options) {
480480
var descriptor = {
481481
name: 'component',
482482
expression: component.id,
483-
literal: !component.dynamic,
484-
def: internalDirectives.component
483+
def: internalDirectives.component,
484+
modifiers: {
485+
literal: !component.dynamic
486+
}
485487
}
486488
var componentLinkFn = function (vm, el, host, scope, frag) {
487489
vm._bindDir(descriptor, el, host, scope, frag)
@@ -568,34 +570,35 @@ function makeTerminalNodeLinkFn (el, dirName, value, options, def) {
568570
function compileDirectives (attrs, options) {
569571
var i = attrs.length
570572
var dirs = []
571-
var attr, name, raw, value, dirName, arg, dirDef, isLiteral, tokens
573+
var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens
572574
while (i--) {
573575
attr = attrs[i]
574-
name = attr.name
575-
raw = value = attr.value
576+
name = rawName = attr.name
577+
value = rawValue = attr.value
576578
tokens = textParser.parse(value)
579+
// reset arg
580+
arg = null
581+
// check modifiers
582+
modifiers = parseModifiers(name)
583+
name = name.replace(modifierRE, '')
577584

578585
// attribute interpolations
579586
if (tokens) {
580587
value = textParser.tokensToExp(tokens)
581-
pushDir('bind', publicDirectives.bind, {
582-
arg: name,
583-
interp: true
584-
})
588+
arg = name
589+
pushDir('bind', publicDirectives.bind, true)
585590
} else
586591

587592
// special attribute: transition
588593
if (transitionRE.test(name)) {
589-
pushDir('transition', internalDirectives.transition, {
590-
literal: !bindRE.test(name)
591-
})
594+
modifiers.literal = !bindRE.test(name)
595+
pushDir('transition', internalDirectives.transition)
592596
} else
593597

594598
// event handlers
595599
if (onRE.test(name)) {
596-
pushDir('on', publicDirectives.on, {
597-
arg: name.replace(onRE, '')
598-
})
600+
arg = name.replace(onRE, '')
601+
pushDir('on', publicDirectives.on)
599602
} else
600603

601604
// attribute bindings
@@ -604,19 +607,13 @@ function compileDirectives (attrs, options) {
604607
if (dirName === 'style' || dirName === 'class') {
605608
pushDir(dirName, internalDirectives[dirName])
606609
} else {
607-
pushDir('bind', publicDirectives.bind, {
608-
arg: dirName
609-
})
610+
arg = dirName
611+
pushDir('bind', publicDirectives.bind)
610612
}
611613
} else
612614

613615
// normal directives
614616
if (name.indexOf('v-') === 0) {
615-
// check literal
616-
isLiteral = literalRE.test(name)
617-
if (isLiteral) {
618-
name = name.replace(literalRE, '')
619-
}
620617
// check arg
621618
arg = (arg = name.match(argRE)) && arg[1]
622619
if (arg) {
@@ -637,14 +634,11 @@ function compileDirectives (attrs, options) {
637634
}
638635

639636
if (dirDef) {
640-
if (!isLiteral && _.isLiteral(value)) {
637+
if (_.isLiteral(value)) {
641638
value = _.stripQuotes(value)
642-
isLiteral = true
639+
modifiers.literal = true
643640
}
644-
pushDir(dirName, dirDef, {
645-
arg: arg,
646-
literal: isLiteral
647-
})
641+
pushDir(dirName, dirDef)
648642
}
649643
}
650644
}
@@ -654,30 +648,48 @@ function compileDirectives (attrs, options) {
654648
*
655649
* @param {String} dirName
656650
* @param {Object|Function} def
657-
* @param {Object} [opts]
651+
* @param {Boolean} [interp]
658652
*/
659653

660-
function pushDir (dirName, def, opts) {
654+
function pushDir (dirName, def, interp) {
661655
var parsed = dirParser.parse(value)
662-
var dir = {
656+
dirs.push({
663657
name: dirName,
664-
attr: name,
665-
raw: raw,
658+
attr: rawName,
659+
raw: rawValue,
666660
def: def,
661+
arg: arg,
662+
modifiers: modifiers,
667663
expression: parsed.expression,
668-
filters: parsed.filters
669-
}
670-
if (opts) {
671-
_.extend(dir, opts)
672-
}
673-
dirs.push(dir)
664+
filters: parsed.filters,
665+
interp: interp
666+
})
674667
}
675668

676669
if (dirs.length) {
677670
return makeNodeLinkFn(dirs)
678671
}
679672
}
680673

674+
/**
675+
* Parse modifiers from directive attribute name.
676+
*
677+
* @param {String} name
678+
* @return {Object}
679+
*/
680+
681+
function parseModifiers (name) {
682+
var res = Object.create(null)
683+
var match = name.match(modifierRE)
684+
if (match) {
685+
var i = match.length
686+
while (i--) {
687+
res[match[i].slice(1)] = true
688+
}
689+
}
690+
return res
691+
}
692+
681693
/**
682694
* Build a link function for all directives on a single node.
683695
*

src/directive.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ function Directive (descriptor, vm, el, host, scope, frag) {
3535
this.name = descriptor.name
3636
this.expression = descriptor.expression
3737
this.arg = descriptor.arg
38+
this.modifiers = descriptor.modifiers
3839
this.filters = descriptor.filters
39-
this.literal = descriptor.literal
40+
this.literal = this.modifiers && this.modifiers.literal
4041
// private
4142
this._locked = false
4243
this._bound = false

test/unit/specs/compiler/compile_spec.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ if (_.inBrowser) {
5050

5151
it('normal directives', function () {
5252
el.setAttribute('v-a', 'b')
53-
el.innerHTML = '<p v-a:hello="a" v-b="1">hello</p><div v-b.literal="hi"></div>'
53+
el.innerHTML = '<p v-a:hello.a.b="a" v-b="1">hello</p><div v-b.literal="hi"></div>'
5454
var defA = { priority: 1 }
5555
var defB = { priority: 2 }
5656
var options = _.mergeOptions(Vue.options, {
@@ -80,21 +80,24 @@ if (_.inBrowser) {
8080
expect(args[0].name).toBe('a')
8181
expect(args[0].expression).toBe('a')
8282
expect(args[0].def).toBe(defA)
83+
// args + multiple modifiers
8384
expect(args[0].arg).toBe('hello')
85+
expect(args[0].modifiers.a).toBe(true)
86+
expect(args[0].modifiers.b).toBe(true)
8487
expect(args[1]).toBe(el.firstChild)
8588
// 3 (expression literal)
8689
args = vm._bindDir.calls.argsFor(isAttrReversed ? 1 : 2)
8790
expect(args[0].name).toBe('b')
8891
expect(args[0].expression).toBe('1')
8992
expect(args[0].def).toBe(defB)
90-
expect(args[0].literal).toBe(true)
93+
expect(args[0].modifiers.literal).toBe(true)
9194
expect(args[1]).toBe(el.firstChild)
9295
// 4 (explicit literal)
9396
args = vm._bindDir.calls.argsFor(3)
9497
expect(args[0].name).toBe('b')
9598
expect(args[0].expression).toBe('hi')
9699
expect(args[0].def).toBe(defB)
97-
expect(args[0].literal).toBe(true)
100+
expect(args[0].modifiers.literal).toBe(true)
98101
expect(args[1]).toBe(el.lastChild)
99102
// check the priority sorting
100103
// the "b"s should be called first!
@@ -229,7 +232,7 @@ if (_.inBrowser) {
229232
var args = vm._bindDir.calls.argsFor(0)
230233
expect(args[0].name).toBe('component')
231234
expect(args[0].expression).toBe('my-component')
232-
expect(args[0].literal).toBe(true)
235+
expect(args[0].modifiers.literal).toBe(true)
233236
expect(args[0].def).toBe(internalDirectives.component)
234237
expect(_.warn).not.toHaveBeenCalled()
235238
})

test/unit/specs/directive_spec.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ describe('Directive', function () {
3434
name: 'test',
3535
def: def,
3636
expression: 'a',
37-
literal: false,
37+
modifiers: {
38+
literal: false
39+
},
3840
filters: [{ name: 'test' }]
3941
}, vm, el)
4042
d._bind()
@@ -66,7 +68,9 @@ describe('Directive', function () {
6668
expression: 'a',
6769
raw: 'a',
6870
def: def,
69-
literal: true
71+
modifiers: {
72+
literal: true
73+
}
7074
}, vm, el)
7175
d._bind()
7276
expect(d._watcher).toBeUndefined()

test/unit/specs/directives/internal/transition_spec.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ if (_.inBrowser) {
1818
name: 'transition',
1919
raw: 'test',
2020
def: def,
21-
literal: true
21+
modifiers: {
22+
literal: true
23+
}
2224
}, vm, el)
2325
dir._bind()
2426
var transition = dir.el.__v_trans
@@ -43,7 +45,9 @@ if (_.inBrowser) {
4345
name: 'transition',
4446
raw: 'test',
4547
def: def,
46-
literal: true
48+
modifiers: {
49+
literal: true
50+
}
4751
}, vm1, el)
4852
dir.el.__vue__ = vm2
4953
dir._bind()

0 commit comments

Comments
 (0)