Skip to content

Commit 47d151b

Browse files
committed
tests for oneway and onetime props
1 parent dcf5fcd commit 47d151b

File tree

3 files changed

+140
-40
lines changed

3 files changed

+140
-40
lines changed

src/compiler/compile.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,13 @@ function makeChildLinkFn (linkFns) {
401401

402402
var dataAttrRE = /^data-/
403403
var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/
404-
var literalValueRE = /^true|false|\d+$/
404+
var literalValueRE = /^(true|false|\d+)$/
405405
var identRE = require('../parsers/path').identRE
406406

407407
function compileProps (el, attrs, propNames) {
408408
var props = []
409409
var i = propNames.length
410-
var name, value, path, prop, settable, single
410+
var name, value, path, prop, settable, literal, single
411411
while (i--) {
412412
name = propNames[i]
413413
// props could contain dashes, which will be
@@ -449,22 +449,24 @@ function compileProps (el, attrs, propNames) {
449449
prop.parentPath = textParser.tokensToExp(tokens)
450450
// check prop binding type.
451451
single = tokens.length === 1
452-
settable =
453-
settablePathRE.test(prop.parentPath) &&
454-
!literalValueRE.test(prop.parentPath)
452+
literal = literalValueRE.test(prop.parentPath)
455453
// one time: {{* prop}}
456454
prop.oneTime =
457-
!single ||
458-
!settable ||
459-
tokens[0].oneTime
460-
// one way down: {{> prop}}
461-
prop.oneWayDown =
462-
single &&
463-
tokens[0].oneWay === 62 // >
464-
// one way up: {{< prop}}
465-
prop.oneWayUp =
466-
tokens[0].oneWay === 60 && // <
467-
settable
455+
literal ||
456+
(single && tokens[0].oneTime)
457+
// check one-way bindings
458+
if (!prop.oneTime) {
459+
settable = !literal && settablePathRE.test(prop.parentPath)
460+
// one way down: {{> prop}}
461+
prop.oneWayDown =
462+
!settable ||
463+
(single && tokens[0].oneWay === 60) // <
464+
// one way up: {{< prop}}
465+
prop.oneWayUp =
466+
single &&
467+
settable &&
468+
tokens[0].oneWay === 62 // >
469+
}
468470
}
469471
props.push(prop)
470472
}
@@ -488,7 +490,7 @@ function makePropsLinkFn (props) {
488490
path = prop.path
489491
if (prop.dynamic) {
490492
if (vm.$parent) {
491-
if (prop.onetime) {
493+
if (prop.oneTime) {
492494
// one time binding
493495
vm.$set(path, vm.$parent.$get(prop.parentPath))
494496
} else {

test/unit/specs/compiler/compile_spec.js

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ if (_.inBrowser) {
2323
_teardown: directiveTeardown
2424
})
2525
},
26-
$set: jasmine.createSpy(),
26+
$set: jasmine.createSpy('vm.$set'),
2727
$eval: function (value) {
2828
return data[value]
2929
},
@@ -167,14 +167,14 @@ if (_.inBrowser) {
167167
el.setAttribute('some-other-attr', '2')
168168
el.setAttribute('multiple-attrs', 'a {{b}} c')
169169
el.setAttribute('onetime', '{{*a}}')
170-
el.setAttribute('oneway-up', '{{<a}}')
171-
el.setAttribute('oneway-down', '{{>a}}')
170+
el.setAttribute('oneway-up', '{{>a}}')
171+
el.setAttribute('oneway-down', '{{<a}}')
172172
el.setAttribute('with-filter', '{{a | filter}}')
173173
el.setAttribute('boolean-literal', '{{true}}')
174174
transclude(el, options)
175175
compiler.compileAndLinkRoot(vm, el, options)
176176
// should skip literals and one-time bindings
177-
expect(vm._bindDir.calls.count()).toBe(7)
177+
expect(vm._bindDir.calls.count()).toBe(5)
178178
// data-some-attr
179179
var args = vm._bindDir.calls.argsFor(0)
180180
expect(args[0]).toBe('prop')
@@ -189,16 +189,8 @@ if (_.inBrowser) {
189189
expect(args[2].path).toBe('multipleAttrs')
190190
expect(args[2].parentPath).toBe('"a "+(b)+" c"')
191191
expect(args[3]).toBe(def)
192-
// one time
193-
args = vm._bindDir.calls.argsFor(2)
194-
expect(args[0]).toBe('prop')
195-
expect(args[1]).toBe(null)
196-
expect(args[2].path).toBe('onetime')
197-
expect(args[2].oneTime).toBe(true)
198-
expect(args[2].parentPath).toBe('a')
199-
expect(args[3]).toBe(def)
200192
// one way up
201-
args = vm._bindDir.calls.argsFor(3)
193+
args = vm._bindDir.calls.argsFor(2)
202194
expect(args[0]).toBe('prop')
203195
expect(args[1]).toBe(null)
204196
expect(args[2].path).toBe('onewayUp')
@@ -207,7 +199,7 @@ if (_.inBrowser) {
207199
expect(args[2].parentPath).toBe('a')
208200
expect(args[3]).toBe(def)
209201
// one way down
210-
args = vm._bindDir.calls.argsFor(4)
202+
args = vm._bindDir.calls.argsFor(3)
211203
expect(args[0]).toBe('prop')
212204
expect(args[1]).toBe(null)
213205
expect(args[2].path).toBe('onewayDown')
@@ -216,25 +208,21 @@ if (_.inBrowser) {
216208
expect(args[2].parentPath).toBe('a')
217209
expect(args[3]).toBe(def)
218210
// with-filter
219-
args = vm._bindDir.calls.argsFor(5)
211+
args = vm._bindDir.calls.argsFor(4)
220212
expect(args[0]).toBe('prop')
221213
expect(args[1]).toBe(null)
222214
expect(args[2].path).toBe('withFilter')
223215
expect(args[2].parentPath).toBe('this._applyFilters(a,null,[{"name":"filter"}],false)')
224216
expect(args[3]).toBe(def)
225-
// boolean-literal
226-
args = vm._bindDir.calls.argsFor(6)
227-
expect(args[0]).toBe('prop')
228-
expect(args[1]).toBe(null)
229-
expect(args[2].path).toBe('booleanLiteral')
230-
expect(args[2].parentPath).toBe('true')
231-
expect(args[2].oneTime).toBe(true)
232217
// camelCase should've warn
233218
expect(hasWarned(_, 'using camelCase')).toBe(true)
234219
// literal and one time should've called vm.$set
235220
// and numbers should be casted
221+
expect(vm.$set.calls.count()).toBe(4)
236222
expect(vm.$set).toHaveBeenCalledWith('a', 1)
237223
expect(vm.$set).toHaveBeenCalledWith('someOtherAttr', 2)
224+
expect(vm.$set).toHaveBeenCalledWith('onetime', 'from parent: a')
225+
expect(vm.$set).toHaveBeenCalledWith('booleanLiteral', 'from parent: true')
238226
})
239227

240228
it('props on root instance', function () {

test/unit/specs/directives/prop_spec.js

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,116 @@ if (_.inBrowser) {
8282
})
8383
})
8484

85+
it('auto one-way binding for non-settable parent path', function (done) {
86+
var vm = new Vue({
87+
el: el,
88+
data: {
89+
b: 'B'
90+
},
91+
template: '<test b="{{b + \'B\'}}" v-ref="child"></test>',
92+
components: {
93+
test: {
94+
props: ['b'],
95+
template: '{{b}}'
96+
}
97+
}
98+
})
99+
expect(el.innerHTML).toBe('<test>BB</test>')
100+
vm.b = 'BB'
101+
_.nextTick(function () {
102+
expect(el.innerHTML).toBe('<test>BBB</test>')
103+
vm.$.child.b = 'hahaha'
104+
_.nextTick(function () {
105+
expect(vm.b).toBe('BB')
106+
expect(el.innerHTML).toBe('<test>hahaha</test>')
107+
done()
108+
})
109+
})
110+
})
111+
112+
it('explicit one time binding', function (done) {
113+
var vm = new Vue({
114+
el: el,
115+
data: {
116+
b: 'B'
117+
},
118+
template: '<test b="{{*b}}" v-ref="child"></test>',
119+
components: {
120+
test: {
121+
props: ['b'],
122+
template: '{{b}}'
123+
}
124+
}
125+
})
126+
expect(el.innerHTML).toBe('<test>B</test>')
127+
vm.b = 'BB'
128+
_.nextTick(function () {
129+
expect(el.innerHTML).toBe('<test>B</test>')
130+
done()
131+
})
132+
})
133+
134+
it('one way down binding', function (done) {
135+
var vm = new Vue({
136+
el: el,
137+
data: {
138+
b: 'B'
139+
},
140+
template: '<test b="{{<b}}" v-ref="child"></test>',
141+
components: {
142+
test: {
143+
props: ['b'],
144+
template: '{{b}}'
145+
}
146+
}
147+
})
148+
expect(el.innerHTML).toBe('<test>B</test>')
149+
vm.b = 'BB'
150+
_.nextTick(function () {
151+
expect(el.innerHTML).toBe('<test>BB</test>')
152+
vm.$.child.b = 'BBB'
153+
_.nextTick(function () {
154+
expect(el.innerHTML).toBe('<test>BBB</test>')
155+
expect(vm.b).toBe('BB')
156+
done()
157+
})
158+
})
159+
})
160+
161+
it('one way up binding', function (done) {
162+
var vm = new Vue({
163+
el: el,
164+
data: {
165+
b: null
166+
},
167+
template: '<test b="{{>b}}" v-ref="child"></test>',
168+
components: {
169+
test: {
170+
props: ['b'],
171+
template: '{{b}}',
172+
data: function () {
173+
return {
174+
b: 'B'
175+
}
176+
}
177+
}
178+
}
179+
})
180+
expect(el.innerHTML).toBe('<test>B</test>')
181+
// initial set
182+
expect(vm.b).toBe('B')
183+
vm.b = 'BB'
184+
_.nextTick(function () {
185+
expect(el.innerHTML).toBe('<test>B</test>')
186+
vm.$.child.b = 'BBB'
187+
_.nextTick(function () {
188+
expect(el.innerHTML).toBe('<test>BBB</test>')
189+
expect(vm.b).toBe('BBB')
190+
done()
191+
})
192+
})
193+
})
194+
85195
it('warn invalid keys', function () {
86196
var vm = new Vue({
87197
el: el,
@@ -142,4 +252,4 @@ if (_.inBrowser) {
142252
})
143253

144254
})
145-
}
255+
}

0 commit comments

Comments
 (0)