8000 prop assertions WIP · mikehins/vue@775c982 · GitHub
[go: up one dir, main page]

Skip to content

Commit 775c982

Browse files
committed
prop assertions WIP
1 parent a6de6e9 commit 775c982

File tree

6 files changed

+127
-21
lines changed

6 files changed

+127
-21
lines changed

src/compiler/compile.js

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -396,21 +396,29 @@ function makeChildLinkFn (linkFns) {
396396
*
397397
* @param {Element|DocumentFragment} el
398398
* @param {Object} attrs
399-
* @param {Array} propNames
399+
* @param {Array} propDescriptors
400400
* @return {Function} propsLinkFn
401401
*/
402402

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

408-
function compileProps (el, attrs, propNames) {
408+
function compileProps (el, attrs, propDescriptors) {
409409
var props = []
410-
var i = propNames.length
411-
var name, value, path, prop, literal, single
410+
var i = propDescriptors.length
411+
var descriptor, name, assertions, value, path, prop, literal, single
412412
while (i--) {
413-
name = propNames[i]
413+
descriptor = propDescriptors[i]
414+
// normalize prop string/descriptor
415+
if (typeof descriptor === 'object') {
416+
name = descriptor.name
417+
assertions = descriptor.assertions
418+
} else {
419+
name = descriptor
420+
assertions = null
421+
}
414422
// props could contain dashes, which will be
415423
// interpreted as minus calculations by the parser
416424
// so we need to camelize the path here
@@ -437,6 +445,7 @@ function compileProps (el, attrs, propNames) {
437445
name: name,
438446
raw: value,
439447
path: path,
448+
assertions: descriptor,
440449
mode: propBindingModes.ONE_WAY
441450
}
442451
var tokens = textParser.parse(value)
@@ -485,15 +494,18 @@ function compileProps (el, attrs, propNames) {
485494
function makePropsLinkFn (props) {
486495
return function propsLinkFn (vm, el) {
487496
var i = props.length
488-
var prop, path
497+
var prop, path, value
489498
while (i--) {
490499
prop = props[i]
491500
path = prop.path
492501
if (prop.dynamic) {
493502
if (vm.$parent) {
494503
if (prop.mode === propBindingModes.ONE_TIME) {
495504
// one time binding
496-
vm.$set(path, vm.$parent.$get(prop.parentPath))
505+
value = vm.$parent.$get(prop.parentPath)
506+
if (_.assertProp(prop, value)) {
507+
vm.$set(path, value)
508+
}
497509
} else {
498510
// dynamic binding
499511
vm._bindDir('prop', el, prop, propDef)
@@ -506,8 +518,11 @@ function makePropsLinkFn (props) {
506518
)
507519
}
508520
} else {
509-
// literal, just set once
510-
vm.$set(path, _.toNumber(prop.raw))
521+
// literal, cast it and just set once
522+
value = _.toBoolean(_.toNumber(prop.raw))
523+
if (_.assertProp(prop, value)) {
524+
vm.$set(path, value)
525+
}
511526
}
512527
}
513528
}

src/instance/init.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,4 @@ exports._init = function (options) {
8989
if (options.el) {
9090
this.$mount(options.el)
9191
}
92-
}
92+
}

src/instance/scope.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,48 @@ var Dep = require('../observer/dep')
1111
*/
1212

1313
exports._initScope = function () {
14+
this._initProps()
1415
this._initData()
1516
this._initComputed()
1617
this._initMethods()
1718
this._initMeta()
1819
}
1920

2021
/**
21-
* Initialize the data.
22+
* Initialize props.
2223
*/
2324

24-
exports._initData = function () {
25-
// proxy data on instance
26-
var data = this._data
27-
var i, key
25+
exports._initProps = function () {
2826
// make sure all props properties are observed
27+
var data = this._data
2928
var props = this.$options.props
29+
var prop, key, i
3030
if (props) {
3131
i = props.length
3232
while (i--) {
33-
key = _.camelize(props[i])
33+
prop = props[i]
34+
// props can be strings or object descriptors
35+
key = _.camelize(
36+
typeof prop === 'string'
37+
? prop
38+
: prop.name
39+
)
3440
if (!(key in data) && key !== '$data') {
3541
data[key] = undefined
3642
}
3743
}
3844
}
45+
}
46+
47+
/**
< 10000 /td>
48+
* Initialize the data.
49+
*/
50+
51+
exports._initData = function () {
52+
// proxy data on instance
53+
var data = this._data
3954
var keys = Object.keys(data)
55+
var i, key
4056
i = keys.length
4157
while (i--) {
4258
key = keys[i]

src/util/lang.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ exports.toNumber = function (value) {
4141
: Number(value)
4242
}
4343

44+
/**
45+
* Convert string boolean literals into real booleans.
46+
*
47+
* @param {*} value
48+
* @return {*|Boolean}
49+
*/
50+
51+
exports.toBoolean = function (value) {
52+
return value === 'true'
53+
? true
54+
: value === 'false'
55+
? false
56+
: value
57+
}
58+
4459
/**
4560
* Strip quotes from a string
4661
*
@@ -266,4 +281,4 @@ exports.cancellable = function (fn) {
266281
cb.cancelled = true
267282
}
268283
return cb
269-
}
284+
}

src/util/misc.js

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,64 @@
11
var _ = require('./index')
22
var config = require('../config')
3-
var commonTagRE = /^(div|p|span|img|a|br|ul|ol|li|h1|h2|h3|h4|h5|code|pre)$/
4-
var tableElementsRE = /^caption|colgroup|thead|tfoot|tbody|tr|td|th$/
3+
4+
/**
5+
* Assert whether a prop is valid.
6+
*
7+
* @param {Object} prop
8+
* @param {*} value
9+
*/
10+
11+
exports.assertProp = function (prop, value) {
12+
var assertions = prop.assertions
13+
if (!assertions) {
14+
return true
15+
}
16+
var type = assertions.type
17+
var valid = true
18+
var expectedType
19+
if (type) {
20+
if (type === String) {
21+
expectedType = 'string'
22+
valid = typeof value === expectedType
23+
} else if (type === Number) {
24+
expectedType = 'number'
25+
valid = typeof value === 'number'
26+
} else if (type === Boolean) {
27+
expectedType = 'boolean'
28+
valid = typeof value === 'boolean'
29+
} else if (type === Function) {
30+
expectedType = 'function'
31+
valid = typeof value === 'function'
32+
} else if (type === Object) {
33+
expectedType = 'object'
34+
valid = _.isPlainObject(value)
35+
} else if (type === Array) {
36+
expectedType = 'array'
37+
valid = _.isArray(value)
38+
} else {
< F438 /td>
39+
valid = value instanceof type
40+
}
41+
}
42+
if (!valid) {
43+
_.warn(
44+
'Invalid prop: type check failed for ' +
45+
prop.path + '="' + prop.raw + '".' +
46+
(expectedType ? ' Expected ' + expectedType + '.' : '')
47+
)
48+
return false
49+
}
50+
var validator = assertions.validator
51+
if (validator) {
52+
if (!validator.call(null, value)) {
53+
_.warn(
54+
'Invalid prop: custom validator check failed for ' +
55+
prop.path + '="' + prop.raw + '"'
56+
)
57+
return false
58+
}
59+
}
60+
return true
61+
}
562

663
/**
764
* Check if an element is a component, if yes return its
@@ -12,6 +69,9 @@ var tableElementsRE = /^caption|colgroup|thead|tfoot|tbody|tr|td|th$/
1269
* @return {String|undefined}
1370
*/
1471

72+
var commonTagRE = /^(div|p|span|img|a|br|ul|ol|li|h1|h2|h3|h4|h5|code|pre)$/
73+
var tableElementsRE = /^caption|colgroup|thead|tfoot|tbody|tr|td|th$/
74+
1575
exports.checkComponent = function (el, options) {
1676
var tag = el.tagName.toLowerCase()
1777
if (tag === 'component') {

test/unit/specs/directives/prop_spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if (_.inBrowser) {
1010
spyOn(_, 'warn')
1111
})
1212

13-
it('one way down binding', function (done) {
13+
it('one way binding', function (done) {
1414
var vm = new Vue({
1515
el: el,
1616
data: {

0 commit comments

Comments
 (0)
0