mirror of https://github.com/vuejs/vue.git
new v-component
This commit is contained in:
parent
c1f24e4e5c
commit
b4aa4378ea
69
changes.md
69
changes.md
|
|
@ -67,6 +67,18 @@ computed: {
|
|||
|
||||
## Directive changes
|
||||
|
||||
### Dynamic literals
|
||||
|
||||
Literal directives can now also be dynamic via bindings like this:
|
||||
|
||||
``` html
|
||||
<div v-component="{{test}}"></div>
|
||||
```
|
||||
|
||||
When `test` changes, the component used will change! This essentially replaces the old `v-view` directive.
|
||||
|
||||
When authoring literal directives, you can now provide an `update()` function if you wish to handle it dynamically. If no `update()` is provided the directive will be treated as a static literal and only evaluated once.
|
||||
|
||||
### New options
|
||||
|
||||
- `twoWay`: indicates the directive is two-way and may write back to the model. Allows the use of `this.set(value)` inside directive functions.
|
||||
|
|
@ -95,25 +107,7 @@ Vue.filter('format', {
|
|||
|
||||
## Block logic control
|
||||
|
||||
``` html
|
||||
<!-- v-repeat="list" -->
|
||||
<h2>{{title}}</h2>
|
||||
<p>{{content}}</p>
|
||||
<!-- v-repeat-end -->
|
||||
```
|
||||
|
||||
``` html
|
||||
<!-- v-if="showProfile" -->
|
||||
<my-avatar></my-avatar>
|
||||
<my-bio></my-bio>
|
||||
<!-- v-if-end -->
|
||||
```
|
||||
|
||||
``` html
|
||||
<!-- v-partial="hello" -->
|
||||
```
|
||||
|
||||
**Note** The old inline partial syntax `{{> partial}}` has been removed. This is to keep the semantics of interpolation tags purely for interpolation purposes; flow control and partials are now either used in the form of attribute directives or comment directives.
|
||||
Still open to suggestions. See details [here].
|
||||
|
||||
## Config API change
|
||||
|
||||
|
|
@ -143,24 +137,25 @@ Vue.config.delimiters = ['(%', '%)']
|
|||
|
||||
* Note you still cannot use `<` or `>` in delimiters because Vue uses DOM-based templating.
|
||||
|
||||
## (Experimental) Validators
|
||||
|
||||
This is largely write filters that accept a Boolean return value. Probably should live as a plugin.
|
||||
|
||||
``` html
|
||||
<input v-model="abc @ email">
|
||||
```
|
||||
|
||||
``` js
|
||||
Vue.validator('email', function (val) {
|
||||
return val.match(...)
|
||||
})
|
||||
// this.$validation.abc // false
|
||||
// this.$valid // false
|
||||
```
|
||||
|
||||
## (Experimental) One time interpolations
|
||||
## One time interpolations
|
||||
|
||||
``` html
|
||||
<span>{{* hello }}</span>
|
||||
```
|
||||
```
|
||||
|
||||
## `$watch` API change
|
||||
|
||||
`vm.$watch` can now accept an expression:
|
||||
|
||||
``` js
|
||||
vm.$watch('a + b', function (newVal, oldVal) {
|
||||
// do something
|
||||
})
|
||||
```
|
||||
|
||||
By default the callback only fires when the value changes. If you want it to be called immediately with the initial value, use the third optional `immediate` argument:
|
||||
|
||||
``` js
|
||||
vm.$watch('a', callback, true)
|
||||
// callback is fired immediately with current value of `a`
|
||||
```
|
||||
|
|
@ -65,12 +65,16 @@ exports.$delete = function (key) {
|
|||
*
|
||||
* @param {String} exp
|
||||
* @param {Function} cb
|
||||
* @param {Boolean} [immediate]
|
||||
* @return {Number}
|
||||
*/
|
||||
|
||||
exports.$watch = function (exp, cb) {
|
||||
exports.$watch = function (exp, cb, immediate) {
|
||||
var watcher = new Watcher(this, exp, cb, this)
|
||||
this._watchers[watcher.id] = watcher
|
||||
if (immediate) {
|
||||
cb.call(this, watcher.value)
|
||||
}
|
||||
return watcher.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ exports.$before = function (target, cb) {
|
|||
exports.$after = function (target, cb) {
|
||||
target = query(target)
|
||||
if (target.nextSibling) {
|
||||
this.$before(target.nextSibling)
|
||||
this.$before(target.nextSibling, cb)
|
||||
} else {
|
||||
this.$appendTo(target.parentNode)
|
||||
this.$appendTo(target.parentNode, cb)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,19 +63,19 @@ p._initDef = function () {
|
|||
*/
|
||||
|
||||
p._bind = function () {
|
||||
this.watcherExp = this.expression
|
||||
var isDynamicLiteral = this._checkDynamicLiteral()
|
||||
this._watcherExp = this.expression
|
||||
this._checkDynamicLiteral()
|
||||
if (this.bind) {
|
||||
this.bind()
|
||||
}
|
||||
if (
|
||||
this.expression && this.update &&
|
||||
(!this.isLiteral || isDynamicLiteral)
|
||||
(!this.isLiteral || this._isDynamicLiteral)
|
||||
) {
|
||||
if (!this._checkExpFn()) {
|
||||
this._watcher = new Watcher(
|
||||
this.vm,
|
||||
this.watcherExp,
|
||||
this._watcherExp,
|
||||
this._update, // callback
|
||||
this, // callback context
|
||||
this.filters,
|
||||
|
|
@ -91,8 +91,6 @@ p._bind = function () {
|
|||
* check if this is a dynamic literal binding.
|
||||
*
|
||||
* e.g. v-component="{{currentView}}"
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
p._checkDynamicLiteral = function () {
|
||||
|
|
@ -108,9 +106,10 @@ p._checkDynamicLiteral = function () {
|
|||
'in literal directives.'
|
||||
)
|
||||
} else {
|
||||
this.watcherExp = tokens[0].value
|
||||
this.expression = this.vm.$eval(expression)
|
||||
return true
|
||||
var exp = tokens[0].value
|
||||
this.expression = this.vm.$get(exp)
|
||||
this._watcherExp = exp
|
||||
this._isDynamicLiteral = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
var _ = require('../util')
|
||||
var Watcher = require('../watcher')
|
||||
|
||||
/**
|
||||
* Possible permutations:
|
||||
*
|
||||
* - literal:
|
||||
* v-component="comp"
|
||||
*
|
||||
* - dynamic:
|
||||
* v-component="{{currentView}}"
|
||||
*
|
||||
* - conditional:
|
||||
* v-component="comp" v-if="abc"
|
||||
*
|
||||
* - dynamic + conditional:
|
||||
* v-component="{{currentView}}" v-if="abc"
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
|
|
@ -6,25 +24,87 @@ module.exports = {
|
|||
|
||||
bind: function () {
|
||||
if (!this.el.__vue__) {
|
||||
var registry = this.vm.$options.components
|
||||
var Ctor = registry[this.expression]
|
||||
if (Ctor) {
|
||||
this.childVM = new Ctor({
|
||||
el: this.el,
|
||||
parent: this.vm
|
||||
})
|
||||
} else {
|
||||
_.warn(
|
||||
'Failed to resolve component: ' +
|
||||
this.expression
|
||||
)
|
||||
// create a ref anchor
|
||||
this.ref = document.createComment('v-component')
|
||||
_.before(this.ref, this.el)
|
||||
_.remove(this.el)
|
||||
// check v-if conditionals
|
||||
this.checkIf()
|
||||
// if static, build right now.
|
||||
if (!this._isDynamicLiteral) {
|
||||
this.resolveCtor(this.expression)
|
||||
this.build()
|
||||
}
|
||||
} else {
|
||||
_.warn(
|
||||
'v-component ' + this.expression + ' cannot be ' +
|
||||
'used on an already mounted instance.'
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
checkIf: function () {
|
||||
var condition = _.attr(this.el, 'if')
|
||||
if (condition !== null) {
|
||||
this.ifWatcher = new Watcher(
|
||||
this.vm,
|
||||
condition,
|
||||
this.ifCallback,
|
||||
this
|
||||
)
|
||||
this.active = this.ifWatcher.value
|
||||
} else {
|
||||
this.active = true
|
||||
}
|
||||
},
|
||||
|
||||
ifCallback: function (value) {
|
||||
if (value) {
|
||||
this.active = true
|
||||
this.build()
|
||||
} else {
|
||||
this.active = false
|
||||
this.unbuild(true)
|
||||
}
|
||||
},
|
||||
|
||||
resolveCtor: function (id) {
|
||||
var registry = this.vm.$options.components
|
||||
this.Ctor = registry[id]
|
||||
if (!this.Ctor) {
|
||||
_.warn('Failed to resolve component: ' + id)
|
||||
}
|
||||
},
|
||||
|
||||
build: function () {
|
||||
if (this.active && this.Ctor && !this.childVM) {
|
||||
this.childVM = new this.Ctor({
|
||||
el: this.el.cloneNode(true),
|
||||
parent: this.vm
|
||||
})
|
||||
this.childVM.$before(this.ref)
|
||||
}
|
||||
},
|
||||
|
||||
unbuild: function (remove) {
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy(remove)
|
||||
this.childVM = null
|
||||
}
|
||||
},
|
||||
|
||||
update: function (value) {
|
||||
this.unbuild(true)
|
||||
if (value) {
|
||||
this.resolveCtor(value)
|
||||
this.build()
|
||||
}
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy()
|
||||
this.unbuild()
|
||||
if (this.ifWatcher) {
|
||||
this.ifWatcher.teardown()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,51 +3,15 @@ var _ = require('../util')
|
|||
module.exports = {
|
||||
|
||||
bind: function () {
|
||||
// resolve component
|
||||
var registry = this.vm.$options.components
|
||||
var el = this.el
|
||||
this.Ctor =
|
||||
registry[el.tagName.toLowerCase()] ||
|
||||
registry[_.attr(el, 'component')] ||
|
||||
_.Vue
|
||||
this.isAnonymous = this.Ctor === _.Vue
|
||||
// insert ref
|
||||
this.ref = document.createComment('v-if')
|
||||
_.before(this.ref, el)
|
||||
_.remove(el)
|
||||
// warn conflicts
|
||||
if (_.attr(el, 'view')) {
|
||||
_.warn(
|
||||
'Conflict: v-if cannot be used together with ' +
|
||||
'v-view. Just set v-view\'s binding value to ' +
|
||||
'empty string to empty it.'
|
||||
)
|
||||
}
|
||||
if (_.attr(el, 'repeat')) {
|
||||
_.warn(
|
||||
'Conflict: v-if cannot be used together with ' +
|
||||
'v-repeat. Use `v-show` or the `filterBy` filter ' +
|
||||
'instead.'
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
update: function (value) {
|
||||
if (!value) {
|
||||
this.unbind()
|
||||
} else if (!this.childVM) {
|
||||
this.childVM = new this.Ctor({
|
||||
el: this.el.cloneNode(true),
|
||||
parent: this.vm,
|
||||
anonymous: this.isAnonymous
|
||||
})
|
||||
this.childVM.$before(this.ref)
|
||||
}
|
||||
update: function () {
|
||||
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ directives.on = require('./on')
|
|||
directives.model = require('./model')
|
||||
|
||||
// child vm directives
|
||||
directives.view = require('./view')
|
||||
directives.component = require('./component')
|
||||
directives.repeat = require('./repeat')
|
||||
directives['if'] = require('./if')
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ module.exports = {
|
|||
return
|
||||
}
|
||||
partial = templateParser.parse(partial, true)
|
||||
var el = this.el
|
||||
var vm = this.vm
|
||||
// comment ref node means inline partial
|
||||
if (el.nodeType === 8) {
|
||||
var el = this.el
|
||||
var vm = this.vm
|
||||
// keep a ref for the partial's content nodes
|
||||
var nodes = _.toArray(partial.childNodes)
|
||||
_.before(partial, el)
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
var _ = require('../util')
|
||||
|
||||
module.exports = {
|
||||
|
||||
bind: function () {
|
||||
// track position in DOM with a ref node
|
||||
var el = this.el
|
||||
var ref = this.ref = document.createComment('v-view')
|
||||
_.before(ref, el)
|
||||
_.remove(el)
|
||||
},
|
||||
|
||||
update: function(value) {
|
||||
this.unbind()
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
var Ctor = this.vm.$options.components[value]
|
||||
if (!Ctor) {
|
||||
_.warn('Failed to resolve component: ' + value)
|
||||
return
|
||||
}
|
||||
this.childVM = new Ctor({
|
||||
el: this.el.cloneNode(true),
|
||||
parent: this.vm
|
||||
})
|
||||
this.childVM.$before(this.ref)
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,7 +23,6 @@ exports._objToArray = function (obj) {
|
|||
return
|
||||
}
|
||||
var res = []
|
||||
var val, data
|
||||
for (var key in obj) {
|
||||
res.push({
|
||||
key: key,
|
||||
|
|
@ -59,7 +58,7 @@ exports.filterBy = function (arr, searchKey, delimiter, dataKey) {
|
|||
// get the optional dataKey
|
||||
dataKey =
|
||||
dataKey &&
|
||||
(stripQuotes(dataKey) || this.$get(dataKey))
|
||||
(_.stripQuotes(dataKey) || this.$get(dataKey))
|
||||
return arr.filter(function (item) {
|
||||
return dataKey
|
||||
? contains(Path.get(item, dataKey), search)
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ filters.currency = function (value, sign) {
|
|||
*/
|
||||
|
||||
filters.pluralize = function (value) {
|
||||
var args = slice.call(arguments, 1)
|
||||
var args = _.toArray(arguments, 1)
|
||||
return args.length > 1
|
||||
? (args[value - 1] || args[args.length - 1])
|
||||
: (args[value - 1] || args[0] + 's')
|
||||
|
|
|
|||
|
|
@ -180,9 +180,8 @@ exports._compileTextNode = function (node) {
|
|||
|
||||
var priorityDirs = [
|
||||
'repeat',
|
||||
'if',
|
||||
'view',
|
||||
'component'
|
||||
'component',
|
||||
'if'
|
||||
]
|
||||
|
||||
exports._checkPriorityDirs = function (node) {
|
||||
|
|
@ -194,7 +193,7 @@ exports._checkPriorityDirs = function (node) {
|
|||
for (var i = 0, l = priorityDirs.length; i < l; i++) {
|
||||
dir = priorityDirs[i]
|
||||
if (value = _.attr(node, dir)) {
|
||||
this._bindDirective(dir, value)
|
||||
this._bindDirective(dir, value, node)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue