mirror of https://github.com/vuejs/vue.git
				
				
				
			dynamic context computed properties
This commit is contained in:
		
							parent
							
								
									e4d06a10ea
								
							
						
					
					
						commit
						de1c1a1112
					
				
							
								
								
									
										2
									
								
								TODO.md
								
								
								
								
							
							
						
						
									
										2
									
								
								TODO.md
								
								
								
								
							| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
- tests
 | 
			
		||||
- docs
 | 
			
		||||
- sd-with
 | 
			
		||||
- standarized way to reuse components (sd-component?)
 | 
			
		||||
- plugins: seed-touch, seed-storage, seed-router
 | 
			
		||||
| 
						 | 
				
			
			@ -7,10 +7,8 @@
 | 
			
		|||
    </head>
 | 
			
		||||
    <body>
 | 
			
		||||
        <section id="todoapp" sd-controller="Todos">
 | 
			
		||||
 | 
			
		||||
            <header id="header">
 | 
			
		||||
                <h1>todos</h1>
 | 
			
		||||
                <!-- main input box -->
 | 
			
		||||
                <input
 | 
			
		||||
                    id="new-todo"
 | 
			
		||||
                    autofocus
 | 
			
		||||
| 
						 | 
				
			
			@ -20,16 +18,13 @@
 | 
			
		|||
                    sd-on="keyup:addTodo | key enter"
 | 
			
		||||
                >
 | 
			
		||||
            </header>
 | 
			
		||||
            
 | 
			
		||||
            <section id="main" sd-show="total">
 | 
			
		||||
                <!-- toggle all checkbox-->
 | 
			
		||||
                <input 
 | 
			
		||||
                    id="toggle-all"
 | 
			
		||||
                    type="checkbox"
 | 
			
		||||
                    sd-checked="allDone"
 | 
			
		||||
                >
 | 
			
		||||
                <ul id="todo-list">
 | 
			
		||||
                    <!-- a single todo item -->
 | 
			
		||||
                    <li
 | 
			
		||||
                        class="todo"
 | 
			
		||||
                        sd-each="todo:todos"
 | 
			
		||||
| 
						 | 
				
			
			@ -56,8 +51,6 @@
 | 
			
		|||
                    </li>
 | 
			
		||||
                </ul>
 | 
			
		||||
            </section>
 | 
			
		||||
 | 
			
		||||
            <!-- footer controls -->
 | 
			
		||||
            <footer id="footer" sd-show="total">
 | 
			
		||||
                <span id="todo-count">
 | 
			
		||||
                    <strong sd-text="remaining"></strong> {{itemLabel}} left
 | 
			
		||||
| 
						 | 
				
			
			@ -71,17 +64,12 @@
 | 
			
		|||
                    Remove Completed ({{completed}})
 | 
			
		||||
                </button>
 | 
			
		||||
            </footer>
 | 
			
		||||
 | 
			
		||||
        </section>
 | 
			
		||||
 | 
			
		||||
        <!-- info -->
 | 
			
		||||
        <footer id="info">
 | 
			
		||||
            <p>Double-click to edit a todo</p>
 | 
			
		||||
            <p>Powered by <a href="https://github.com/yyx990803/seed">Seed.js</a></p>
 | 
			
		||||
            <p>Created by <a href="http://evanyou.me">Evan You</a></p>
 | 
			
		||||
        </footer>
 | 
			
		||||
 | 
			
		||||
        <!-- js -->
 | 
			
		||||
        <script src="../../dist/seed.js"></script>
 | 
			
		||||
        <script src="js/app.js"></script>
 | 
			
		||||
    </body>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,27 @@
 | 
			
		|||
var storageKey = 'todos-seedjs',
 | 
			
		||||
    todos = JSON.parse(localStorage.getItem(storageKey)) || [],
 | 
			
		||||
    filters = {
 | 
			
		||||
Seed.controller('Todos', function (scope) {
 | 
			
		||||
 | 
			
		||||
    // data persistence -------------------------------------------------------
 | 
			
		||||
    var STORAGE_KEY = 'todos-seedjs'
 | 
			
		||||
    function sync () {
 | 
			
		||||
        localStorage.setItem(STORAGE_KEY, scope.$serialize('todos'))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // filters ----------------------------------------------------------------
 | 
			
		||||
    var filters = {
 | 
			
		||||
        all: function () { return true },
 | 
			
		||||
        active: function (v) { return !v },
 | 
			
		||||
        completed: function (v) { return v }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Seed.controller('Todos', function (scope) {
 | 
			
		||||
    updateFilter()
 | 
			
		||||
    window.addEventListener('hashchange', updateFilter)
 | 
			
		||||
    function updateFilter () {
 | 
			
		||||
        if (!location.hash) return
 | 
			
		||||
        scope.filter = location.hash.slice(2) || 'all'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // regular properties -----------------------------------------------------
 | 
			
		||||
    scope.todos = todos
 | 
			
		||||
    scope.remaining = todos.reduce(function (n, todo) {
 | 
			
		||||
    scope.todos = JSON.parse(localStorage.getItem(STORAGE_KEY)) || []
 | 
			
		||||
    scope.remaining = scope.todos.reduce(function (n, todo) {
 | 
			
		||||
        return n + (todo.completed ? 0 : 1)
 | 
			
		||||
    }, 0)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,12 +38,12 @@ Seed.controller('Todos', function (scope) {
 | 
			
		|||
        return scope.remaining > 1 ? 'items' : 'item'
 | 
			
		||||
    }}
 | 
			
		||||
 | 
			
		||||
    // computed property using info from target scope
 | 
			
		||||
    // dynamic context computed property using info from target scope
 | 
			
		||||
    scope.filterTodo = {get: function (e) {
 | 
			
		||||
        return filters[scope.filter](e.scope.completed)
 | 
			
		||||
    }}
 | 
			
		||||
 | 
			
		||||
    // computed property using info from target element
 | 
			
		||||
    // dynamic context computed property using info from target element
 | 
			
		||||
    scope.checkFilter = {get: function (e) {
 | 
			
		||||
        return scope.filter === e.el.textContent.toLowerCase()
 | 
			
		||||
    }}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,8 +63,9 @@ Seed.controller('Todos', function (scope) {
 | 
			
		|||
 | 
			
		||||
    // event handlers ---------------------------------------------------------
 | 
			
		||||
    scope.addTodo = function () {
 | 
			
		||||
        if (scope.newTodo.trim()) {
 | 
			
		||||
            scope.todos.unshift({ title: scope.newTodo.trim(), completed: false })
 | 
			
		||||
        var value = scope.newTodo.trim()
 | 
			
		||||
        if (value) {
 | 
			
		||||
            scope.todos.unshift({ title: value, completed: false })
 | 
			
		||||
            scope.newTodo = ''
 | 
			
		||||
            scope.remaining++
 | 
			
		||||
            sync()
 | 
			
		||||
| 
						 | 
				
			
			@ -99,19 +111,8 @@ Seed.controller('Todos', function (scope) {
 | 
			
		|||
        sync()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // filters
 | 
			
		||||
    updateFilter()
 | 
			
		||||
    window.addEventListener('hashchange', updateFilter)
 | 
			
		||||
    function updateFilter () {
 | 
			
		||||
        if (!location.hash) return
 | 
			
		||||
        scope.filter = location.hash.slice(2) || 'all'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // data persistence using localStorage
 | 
			
		||||
    function sync () {
 | 
			
		||||
        localStorage.setItem(storageKey, scope.$serialize('todos'))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
Seed.bootstrap({ debug: true })
 | 
			
		||||
var s = Date.now()
 | 
			
		||||
Seed.bootstrap({ debug: false })
 | 
			
		||||
console.log(Date.now() - s)
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +115,13 @@ BindingProto.update = function (value) {
 | 
			
		|||
    this.pub()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BindingProto.refresh = function () {
 | 
			
		||||
    var i = this.instances.length
 | 
			
		||||
    while (i--) {
 | 
			
		||||
        this.instances[i].refresh()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Notify computed properties that depend on this binding
 | 
			
		||||
 *  to update themselves
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,8 +19,9 @@ function catchDeps (binding) {
 | 
			
		|||
    observer.on('get', function (dep) {
 | 
			
		||||
        binding.deps.push(dep)
 | 
			
		||||
    })
 | 
			
		||||
    parseContextDependency(binding)
 | 
			
		||||
    binding.value.get({
 | 
			
		||||
        scope: createDummyScope(binding.value.get),
 | 
			
		||||
        scope: createDummyScope(binding),
 | 
			
		||||
        el: dummyEl
 | 
			
		||||
    })
 | 
			
		||||
    observer.off('get')
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +38,7 @@ function filterDeps (binding) {
 | 
			
		|||
        dep = binding.deps[i]
 | 
			
		||||
        if (!dep.deps.length) {
 | 
			
		||||
            config.log('  └─' + dep.key)
 | 
			
		||||
            dep.subs.push.apply(dep.subs, binding.instances)
 | 
			
		||||
            dep.subs.push(binding)
 | 
			
		||||
        } else {
 | 
			
		||||
            binding.deps.splice(i, 1)
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -53,18 +54,15 @@ function filterDeps (binding) {
 | 
			
		|||
 *  the user expects the target scope to possess. They are all assigned
 | 
			
		||||
 *  a noop function so they can be invoked with no real harm.
 | 
			
		||||
 */
 | 
			
		||||
function createDummyScope (fn) {
 | 
			
		||||
function createDummyScope (binding) {
 | 
			
		||||
    var scope = {},
 | 
			
		||||
        str = fn.toString()
 | 
			
		||||
    var args = str.match(ARGS_RE)
 | 
			
		||||
    if (!args) return scope
 | 
			
		||||
    var argRE = new RegExp(args[1] + SCOPE_RE_STR, 'g'),
 | 
			
		||||
        matches = str.match(argRE)
 | 
			
		||||
    if (!matches) return scope
 | 
			
		||||
    var i = matches.length, j, path, key, level
 | 
			
		||||
        deps = binding.contextDeps
 | 
			
		||||
    if (!deps) return scope
 | 
			
		||||
    var i = binding.contextDeps.length,
 | 
			
		||||
        j, level, key, path
 | 
			
		||||
    while (i--) {
 | 
			
		||||
        level = scope
 | 
			
		||||
        path = matches[i].slice(args[1].length + 7).split('.')
 | 
			
		||||
        path = deps[i].split('.')
 | 
			
		||||
        j = 0
 | 
			
		||||
        while (j < path.length) {
 | 
			
		||||
            key = path[j]
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +74,22 @@ function createDummyScope (fn) {
 | 
			
		|||
    return scope
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Extract context dependency paths
 | 
			
		||||
 */
 | 
			
		||||
function parseContextDependency (binding) {
 | 
			
		||||
    var fn   = binding.value.get,
 | 
			
		||||
        str  = fn.toString(),
 | 
			
		||||
        args = str.match(ARGS_RE)
 | 
			
		||||
    if (!args) return null
 | 
			
		||||
    var argRE = new RegExp(args[1] + SCOPE_RE_STR, 'g'),
 | 
			
		||||
        matches = str.match(argRE),
 | 
			
		||||
        base = args[1].length + 7
 | 
			
		||||
    if (!matches) return null
 | 
			
		||||
    binding.contextDeps = matches.map(function (key) { return key.slice(base) })
 | 
			
		||||
    binding.seed._contextBindings.push(binding)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										28
									
								
								src/seed.js
								
								
								
								
							
							
						
						
									
										28
									
								
								src/seed.js
								
								
								
								
							| 
						 | 
				
			
			@ -24,7 +24,10 @@ function Seed (el, options) {
 | 
			
		|||
    this.el               = el
 | 
			
		||||
    el.seed               = this
 | 
			
		||||
    this._bindings        = {}
 | 
			
		||||
    // list of computed properties that need to parse dependencies for
 | 
			
		||||
    this._computed        = []
 | 
			
		||||
    // list of bindings that has dynamic context dependencies
 | 
			
		||||
    this._contextBindings = []
 | 
			
		||||
 | 
			
		||||
    // copy options
 | 
			
		||||
    options = options || {}
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +85,9 @@ function Seed (el, options) {
 | 
			
		|||
    // extract dependencies for computed properties
 | 
			
		||||
    if (this._computed.length) depsParser.parse(this._computed)
 | 
			
		||||
    delete this._computed
 | 
			
		||||
    
 | 
			
		||||
    if (this._contextBindings.length) this._bindContexts(this._contextBindings)
 | 
			
		||||
    delete this._contextBindings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for better compression
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +199,10 @@ SeedProto._bind = function (directive) {
 | 
			
		|||
    seed = traceOwnerSeed(directive, seed)
 | 
			
		||||
    var binding = seed._bindings[key] || seed._createBinding(key)
 | 
			
		||||
 | 
			
		||||
    // add directive to this binding
 | 
			
		||||
    if (binding.contextDeps) {
 | 
			
		||||
        console.log(1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    binding.instances.push(directive)
 | 
			
		||||
    directive.binding = binding
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -220,6 +229,23 @@ SeedProto._createBinding = function (key) {
 | 
			
		|||
    return binding
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Process subscriptions for computed properties that has
 | 
			
		||||
 *  dynamic context dependencies
 | 
			
		||||
 */
 | 
			
		||||
SeedProto._bindContexts = function (bindings) {
 | 
			
		||||
    var i = bindings.length, j, binding, depKey, dep
 | 
			
		||||
    while (i--) {
 | 
			
		||||
        binding = bindings[i]
 | 
			
		||||
        j = binding.contextDeps.length
 | 
			
		||||
        while (j--) {
 | 
			
		||||
            depKey = binding.contextDeps[j]
 | 
			
		||||
            dep = this._bindings[depKey]
 | 
			
		||||
            dep.subs.push(binding)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Call unbind() of all directive instances
 | 
			
		||||
 *  to remove event listeners, destroy child seeds, etc.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue