mirror of https://github.com/vuejs/vue.git
v-view
This commit is contained in:
parent
656c0157f5
commit
10a0cc1838
|
|
@ -30,6 +30,7 @@
|
|||
"src/directives/with.js",
|
||||
"src/directives/html.js",
|
||||
"src/directives/style.js",
|
||||
"src/directives/partial.js"
|
||||
"src/directives/partial.js",
|
||||
"src/directives/view.js"
|
||||
]
|
||||
}
|
||||
|
|
@ -336,6 +336,7 @@ CompilerProto.compile = function (node, root) {
|
|||
|
||||
// special attributes to check
|
||||
var repeatExp,
|
||||
viewExp,
|
||||
withExp,
|
||||
directive,
|
||||
// resolve a standalone child component with no inherited data
|
||||
|
|
@ -361,6 +362,13 @@ CompilerProto.compile = function (node, root) {
|
|||
compiler.deferred.push(directive)
|
||||
}
|
||||
|
||||
} else if (viewExp = utils.attr(node, 'view')) {
|
||||
|
||||
directive = Directive.parse('view', viewExp, compiler, node)
|
||||
if (directive) {
|
||||
compiler.deferred.push(directive)
|
||||
}
|
||||
|
||||
// Child component has 2nd highest priority
|
||||
} else if (root !== true && ((withExp = utils.attr(node, 'with')) || hasComponent)) {
|
||||
|
||||
|
|
@ -377,8 +385,9 @@ CompilerProto.compile = function (node, root) {
|
|||
|
||||
} else {
|
||||
|
||||
// compile normal directives
|
||||
// remove the component directive
|
||||
utils.attr(node, 'component')
|
||||
// compile normal directives
|
||||
compiler.compileNode(node)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
html : require('./html'),
|
||||
style : require('./style'),
|
||||
partial : require('./partial'),
|
||||
view : require('./view'),
|
||||
|
||||
attr: function (value) {
|
||||
if (value || value === 0) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
module.exports = {
|
||||
|
||||
bind: function () {
|
||||
|
||||
// track position in DOM with a ref node
|
||||
var el = this.raw = this.el,
|
||||
parent = el.parentNode,
|
||||
ref = this.ref = document.createComment('v-view')
|
||||
parent.insertBefore(ref, el)
|
||||
parent.removeChild(el)
|
||||
|
||||
// cache original content
|
||||
/* jshint boss: true */
|
||||
var node,
|
||||
frag = this.inner = document.createDocumentFragment()
|
||||
while (node = el.firstChild) {
|
||||
frag.appendChild(node)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
update: function(value) {
|
||||
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy()
|
||||
}
|
||||
|
||||
var Ctor = this.compiler.getOption('components', value)
|
||||
if (!Ctor) return
|
||||
|
||||
var inner = this.inner.cloneNode(true)
|
||||
|
||||
this.childVM = new Ctor({
|
||||
el: this.raw.cloneNode(true),
|
||||
parent: this.vm,
|
||||
created: function () {
|
||||
this.$compiler.rawContent = inner
|
||||
}
|
||||
})
|
||||
|
||||
this.el = this.childVM.$el
|
||||
if (this.compiler.init) {
|
||||
this.ref.parentNode.insertBefore(this.el, this.ref)
|
||||
} else {
|
||||
this.childVM.$before(this.ref)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
if (this.childVM) {
|
||||
this.childVM.$destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
var nextTick = require('../utils').nextTick
|
||||
var utils = require('../utils')
|
||||
|
||||
module.exports = {
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ module.exports = {
|
|||
if (this.el.vue_vm) {
|
||||
this.subVM = this.el.vue_vm
|
||||
var compiler = this.subVM.$compiler
|
||||
if (!compiler.bindings[this.arg]) {
|
||||
if (this.arg && !compiler.bindings[this.arg]) {
|
||||
compiler.createBinding(this.arg)
|
||||
}
|
||||
} else if (this.isEmpty) {
|
||||
|
|
@ -55,6 +55,8 @@ module.exports = {
|
|||
delayReady: !this.last
|
||||
}
|
||||
})
|
||||
// mark that this VM is created by v-with
|
||||
utils.defProtected(this.subVM, '$with', true)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -69,7 +71,7 @@ module.exports = {
|
|||
this.subVM.$compiler.observer.on('change:' + this.arg, function (val) {
|
||||
if (!self.lock) {
|
||||
self.lock = true
|
||||
nextTick(function () {
|
||||
utils.nextTick(function () {
|
||||
self.lock = false
|
||||
})
|
||||
}
|
||||
|
|
@ -80,7 +82,9 @@ module.exports = {
|
|||
unbind: function () {
|
||||
// all watchers are turned off during destroy
|
||||
// so no need to worry about it
|
||||
this.subVM.$destroy()
|
||||
if (this.subVM.$with) {
|
||||
this.subVM.$destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,28 +1,101 @@
|
|||
<div v-if="route.hi">Hi! <a href="#ho">Next</a></div>
|
||||
<div v-if="route.ho">Ho! <a href="#ha">Next</a></div>
|
||||
<div v-if="route.ha">Ha! <a href="#hi">Next</a></div>
|
||||
|
||||
<script src="../../../dist/vue.js"></script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 20px;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
a {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.current {
|
||||
color: blue;
|
||||
}
|
||||
.view {
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
-webkit-transition: all .2s ease;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
.v-enter {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(30px, 0, 0);
|
||||
transform: translate3d(30px, 0, 0);
|
||||
}
|
||||
.v-leave {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-30px, 0, 0);
|
||||
transform: translate3d(-30px, 0, 0);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
<li v-repeat="routes">
|
||||
<a href="#!/{{$value}}" v-class="current:currentView == $value">{{$value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-view="currentView" class="view" v-transition>
|
||||
<p>Hello! {{msg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var route = {
|
||||
hi: false,
|
||||
ho: false,
|
||||
ha: false
|
||||
Vue.component('home', {
|
||||
template: '<h1>Home</h1><div class="content">{{>yield}}</div>',
|
||||
created: function () {
|
||||
this.msg = "Home sweet home!"
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('hashchange', updateRoute)
|
||||
function updateRoute () {
|
||||
var path = location.hash.slice(1) || 'hi'
|
||||
for (var key in route) {
|
||||
route[key] = key === path
|
||||
}
|
||||
Vue.component('page1', {
|
||||
template: '<h1>Page1</h1><div class="content">{{>yield}}</div>',
|
||||
created: function () {
|
||||
this.msg = "Welcome to page 1!"
|
||||
}
|
||||
})
|
||||
|
||||
var app = new Vue({
|
||||
el: 'body'
|
||||
})
|
||||
app.route = route
|
||||
Vue.component('page2', {
|
||||
template: '<h1>Page2</h1><div class="content">{{>yield}}</div>',
|
||||
created: function () {
|
||||
this.msg = "Welcome to page 2!"
|
||||
}
|
||||
})
|
||||
|
||||
Vue.component('notfound', {
|
||||
template: '<h1>404 yo</h1>'
|
||||
})
|
||||
|
||||
// simple routing
|
||||
var routes = ['home', 'page1', 'page2']
|
||||
|
||||
function getRoute () {
|
||||
var path = location.hash.replace(/^#!\/?/, '') || 'home'
|
||||
return routes.indexOf(path) > -1
|
||||
? path
|
||||
: 'notfound'
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', function () {
|
||||
app.currentView = getRoute()
|
||||
})
|
||||
|
||||
// load the app
|
||||
var app = new Vue({
|
||||
el: 'div',
|
||||
data: {
|
||||
currentView: getRoute(),
|
||||
routes: routes
|
||||
}
|
||||
})
|
||||
|
||||
updateRoute()
|
||||
</script>
|
||||
|
|
@ -1,26 +1,45 @@
|
|||
casper.test.begin('Routing', 10, function (test) {
|
||||
casper.test.begin('Routing', 24, function (test) {
|
||||
|
||||
casper
|
||||
.start('./fixtures/routing.html')
|
||||
.then(function () {
|
||||
test.assertElementCount('div', 1)
|
||||
test.assertSelectorHasText('div', 'Hi!')
|
||||
test.assertElementCount('.view', 1)
|
||||
test.assertElementCount('.view.v-leave', 0)
|
||||
test.assertSelectorHasText('a.current', 'home')
|
||||
test.assertSelectorHasText('h1', 'Home')
|
||||
test.assertSelectorHasText('.content', 'Home sweet home!')
|
||||
})
|
||||
.thenClick('a', function () {
|
||||
test.assertElementCount('div', 1)
|
||||
test.assertSelectorHasText('div', 'Ho!')
|
||||
.thenClick('a[href$="page1"]', function () {
|
||||
test.assertSelectorHasText('a.current', 'page1')
|
||||
// in transition
|
||||
test.assertElementCount('.view', 2)
|
||||
test.assertElementCount('.view.v-leave', 1)
|
||||
})
|
||||
.thenClick('a', function () {
|
||||
test.assertElementCount('div', 1)
|
||||
test.assertSelectorHasText('div', 'Ha!')
|
||||
.wait(250, function () {
|
||||
test.assertElementCount('.view', 1)
|
||||
test.assertElementCount('.view.v-leave', 0)
|
||||
test.assertSelectorHasText('h1', 'Page1')
|
||||
test.assertSelectorHasText('.content', 'Welcome to page 1!')
|
||||
})
|
||||
.thenClick('a', function () {
|
||||
test.assertElementCount('div', 1)
|
||||
test.assertSelectorHasText('div', 'Hi!')
|
||||
.thenClick('a[href$="page2"]', function () {
|
||||
test.assertSelectorHasText('a.current', 'page2')
|
||||
// in transition
|
||||
test.assertElementCount('.view', 2)
|
||||
test.assertElementCount('.view.v-leave', 1)
|
||||
})
|
||||
.thenOpen('./fixtures/routing.html#ho', function () {
|
||||
test.assertElementCount('div', 1)
|
||||
test.assertSelectorHasText('div', 'Ho!')
|
||||
.wait(250, function () {
|
||||
test.assertElementCount('.view', 1)
|
||||
test.assertElementCount('.view.v-leave', 0)
|
||||
test.assertSelectorHasText('h1', 'Page2')
|
||||
test.assertSelectorHasText('.content', 'Welcome to page 2!')
|
||||
})
|
||||
// reload to test initial page load with a route
|
||||
.reload(function () {
|
||||
test.assertSelectorHasText('a.current', 'page2')
|
||||
test.assertElementCount('.view', 1)
|
||||
test.assertElementCount('.view.v-leave', 0)
|
||||
test.assertSelectorHasText('h1', 'Page2')
|
||||
test.assertSelectorHasText('.content', 'Welcome to page 2!')
|
||||
})
|
||||
.run(function () {
|
||||
test.done()
|
||||
|
|
|
|||
Loading…
Reference in New Issue