mirror of https://github.com/vuejs/vue.git
separate storage in todos example, expose some utils methods
This commit is contained in:
parent
d0c96c5c25
commit
cfc27d89f1
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="common/base.css">
|
<link rel="stylesheet" type="text/css" href="common/base.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<section id="todoapp" sd-controller="Todos">
|
<section id="todoapp" sd-controller="todos">
|
||||||
<header id="header">
|
<header id="header">
|
||||||
<h1>todos</h1>
|
<h1>todos</h1>
|
||||||
<input
|
<input
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
sd-checked="todo.completed"
|
sd-checked="todo.completed"
|
||||||
sd-on="change:updateCount"
|
sd-on="change:updateCount"
|
||||||
>
|
>
|
||||||
<label sd-text="todo.title" sd-on="dblclick:edit"></label>
|
<label sd-text="todo.title" sd-on="dblclick:editTodo"></label>
|
||||||
<button class="destroy" sd-on="click:removeTodo"></button>
|
<button class="destroy" sd-on="click:removeTodo"></button>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|
@ -71,6 +71,7 @@
|
||||||
<p>Created by <a href="http://evanyou.me">Evan You</a></p>
|
<p>Created by <a href="http://evanyou.me">Evan You</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="../../dist/seed.js"></script>
|
<script src="../../dist/seed.js"></script>
|
||||||
|
<script src="js/todoStorage.js"></script>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,25 +1,7 @@
|
||||||
Seed.controller('Todos', function (scope) {
|
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 }
|
|
||||||
}
|
|
||||||
updateFilter()
|
|
||||||
window.addEventListener('hashchange', updateFilter)
|
|
||||||
function updateFilter () {
|
|
||||||
scope.filter = location.hash ? location.hash.slice(2) : 'all'
|
|
||||||
}
|
|
||||||
|
|
||||||
// regular properties -----------------------------------------------------
|
// regular properties -----------------------------------------------------
|
||||||
scope.todos = JSON.parse(localStorage.getItem(STORAGE_KEY)) || []
|
scope.todos = todoStorage.fetch()
|
||||||
scope.remaining = scope.todos.reduce(function (n, todo) {
|
scope.remaining = scope.todos.reduce(function (n, todo) {
|
||||||
return n + (todo.completed ? 0 : 1)
|
return n + (todo.completed ? 0 : 1)
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
@ -63,23 +45,23 @@ Seed.controller('Todos', function (scope) {
|
||||||
scope.todos.unshift({ title: value, completed: false })
|
scope.todos.unshift({ title: value, completed: false })
|
||||||
scope.newTodo = ''
|
scope.newTodo = ''
|
||||||
scope.remaining++
|
scope.remaining++
|
||||||
sync()
|
todoStorage.save(scope.todos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.removeTodo = function (e) {
|
scope.removeTodo = function (e) {
|
||||||
scope.todos.remove(e.scope)
|
scope.todos.remove(e.scope)
|
||||||
scope.remaining -= e.scope.completed ? 0 : 1
|
scope.remaining -= e.scope.completed ? 0 : 1
|
||||||
sync()
|
todoStorage.save(scope.todos)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.updateCount = function (e) {
|
scope.updateCount = function (e) {
|
||||||
scope.remaining += e.scope.completed ? -1 : 1
|
scope.remaining += e.scope.completed ? -1 : 1
|
||||||
sync()
|
todoStorage.save(scope.todos)
|
||||||
}
|
}
|
||||||
|
|
||||||
var beforeEditCache
|
var beforeEditCache
|
||||||
scope.edit = function (e) {
|
scope.editTodo = function (e) {
|
||||||
beforeEditCache = e.scope.title
|
beforeEditCache = e.scope.title
|
||||||
e.scope.editing = true
|
e.scope.editing = true
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +71,7 @@ Seed.controller('Todos', function (scope) {
|
||||||
e.scope.editing = false
|
e.scope.editing = false
|
||||||
e.scope.title = e.scope.title.trim()
|
e.scope.title = e.scope.title.trim()
|
||||||
if (!e.scope.title) scope.removeTodo(e)
|
if (!e.scope.title) scope.removeTodo(e)
|
||||||
sync()
|
todoStorage.save(scope.todos)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.cancelEdit = function (e) {
|
scope.cancelEdit = function (e) {
|
||||||
|
|
@ -103,11 +85,23 @@ Seed.controller('Todos', function (scope) {
|
||||||
scope.todos = scope.todos.filter(function (todo) {
|
scope.todos = scope.todos.filter(function (todo) {
|
||||||
return !todo.completed
|
return !todo.completed
|
||||||
})
|
})
|
||||||
sync()
|
todoStorage.save(scope.todos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filters ----------------------------------------------------------------
|
||||||
|
var filters = {
|
||||||
|
all: function () { return true },
|
||||||
|
active: function (v) { return !v },
|
||||||
|
completed: function (v) { return v }
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFilter () {
|
||||||
|
scope.filter = location.hash ? location.hash.slice(2) : 'all'
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFilter()
|
||||||
|
window.addEventListener('hashchange', updateFilter)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var s = Date.now()
|
Seed.bootstrap()
|
||||||
Seed.bootstrap({ debug: false })
|
|
||||||
console.log(Date.now() - s)
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
var todoStorage = (function () {
|
||||||
|
var STORAGE_KEY = 'todos-seedjs'
|
||||||
|
return {
|
||||||
|
fetch: function () {
|
||||||
|
return JSON.parse(localStorage.getItem(this.STORAGE_KEY) || '[]')
|
||||||
|
},
|
||||||
|
save: function (todos) {
|
||||||
|
localStorage.setItem(this.STORAGE_KEY, Seed.utils.serialize(todos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}())
|
||||||
38
src/main.js
38
src/main.js
|
|
@ -2,7 +2,8 @@ var config = require('./config'),
|
||||||
Seed = require('./seed'),
|
Seed = require('./seed'),
|
||||||
directives = require('./directives'),
|
directives = require('./directives'),
|
||||||
filters = require('./filters'),
|
filters = require('./filters'),
|
||||||
textParser = require('./text-parser')
|
textParser = require('./text-parser'),
|
||||||
|
utils = require('./utils')
|
||||||
|
|
||||||
var controllers = config.controllers,
|
var controllers = config.controllers,
|
||||||
datum = config.datum,
|
datum = config.datum,
|
||||||
|
|
@ -10,6 +11,11 @@ var controllers = config.controllers,
|
||||||
reserved = ['datum', 'controllers'],
|
reserved = ['datum', 'controllers'],
|
||||||
booted = false
|
booted = false
|
||||||
|
|
||||||
|
/*
|
||||||
|
* expose utils
|
||||||
|
*/
|
||||||
|
api.utils = utils
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store a piece of plain data in config.datum
|
* Store a piece of plain data in config.datum
|
||||||
* so it can be consumed by sd-data
|
* so it can be consumed by sd-data
|
||||||
|
|
@ -45,12 +51,9 @@ api.filter = function (name, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bootstrap the whole thing
|
* Set config options
|
||||||
* by creating a Seed instance for top level nodes
|
|
||||||
* that has either sd-controller or sd-data
|
|
||||||
*/
|
*/
|
||||||
api.bootstrap = function (opts) {
|
api.config = function (opts) {
|
||||||
if (booted) return
|
|
||||||
if (opts) {
|
if (opts) {
|
||||||
for (var key in opts) {
|
for (var key in opts) {
|
||||||
if (reserved.indexOf(key) === -1) {
|
if (reserved.indexOf(key) === -1) {
|
||||||
|
|
@ -59,16 +62,31 @@ api.bootstrap = function (opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textParser.buildRegex()
|
textParser.buildRegex()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile a single element
|
||||||
|
*/
|
||||||
|
api.compile = function (el) {
|
||||||
|
new Seed(el)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bootstrap the whole thing
|
||||||
|
* by creating a Seed instance for top level nodes
|
||||||
|
* that has either sd-controller or sd-data
|
||||||
|
*/
|
||||||
|
api.bootstrap = function (opts) {
|
||||||
|
if (booted) return
|
||||||
|
api.config(opts)
|
||||||
var el,
|
var el,
|
||||||
ctrlSlt = '[' + config.prefix + '-controller]',
|
ctrlSlt = '[' + config.prefix + '-controller]',
|
||||||
dataSlt = '[' + config.prefix + '-data]',
|
dataSlt = '[' + config.prefix + '-data]'
|
||||||
seeds = []
|
|
||||||
/* jshint boss: true */
|
/* jshint boss: true */
|
||||||
while (el = document.querySelector(ctrlSlt) || document.querySelector(dataSlt)) {
|
while (el = document.querySelector(ctrlSlt) || document.querySelector(dataSlt)) {
|
||||||
seeds.push((new Seed(el)).scope)
|
new Seed(el)
|
||||||
}
|
}
|
||||||
booted = true
|
booted = true
|
||||||
return seeds.length > 1 ? seeds : seeds[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = api
|
module.exports = api
|
||||||
|
|
@ -56,7 +56,7 @@ ScopeProto.$unwatch = function (key) {
|
||||||
*/
|
*/
|
||||||
ScopeProto.$dump = function (key) {
|
ScopeProto.$dump = function (key) {
|
||||||
var bindings = this.$seed._bindings
|
var bindings = this.$seed._bindings
|
||||||
return utils.dumpValue(key ? bindings[key].value : this)
|
return utils.dump(key ? bindings[key].value : this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,10 @@ function Seed (el, options) {
|
||||||
var ctrlID = el.getAttribute(ctrlAttr)
|
var ctrlID = el.getAttribute(ctrlAttr)
|
||||||
if (ctrlID) {
|
if (ctrlID) {
|
||||||
el.removeAttribute(ctrlAttr)
|
el.removeAttribute(ctrlAttr)
|
||||||
var factory = config.controllers[ctrlID]
|
var controller = config.controllers[ctrlID]
|
||||||
if (factory) {
|
if (controller) {
|
||||||
factory(this.scope)
|
this._controller = controller
|
||||||
|
controller(this.scope)
|
||||||
} else {
|
} else {
|
||||||
config.warn('controller "' + ctrlID + '" is not defined.')
|
config.warn('controller "' + ctrlID + '" is not defined.')
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +87,7 @@ function Seed (el, options) {
|
||||||
if (this._computed.length) depsParser.parse(this._computed)
|
if (this._computed.length) depsParser.parse(this._computed)
|
||||||
delete this._computed
|
delete this._computed
|
||||||
|
|
||||||
|
// extract dependencies for computed properties with dynamic context
|
||||||
if (this._contextBindings.length) this._bindContexts(this._contextBindings)
|
if (this._contextBindings.length) this._bindContexts(this._contextBindings)
|
||||||
delete this._contextBindings
|
delete this._contextBindings
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ module.exports = {
|
||||||
* Parse a piece of text, return an array of tokens
|
* Parse a piece of text, return an array of tokens
|
||||||
*/
|
*/
|
||||||
parse: function (node) {
|
parse: function (node) {
|
||||||
|
if (!BINDING_RE) module.exports.buildRegex()
|
||||||
var text = node.nodeValue
|
var text = node.nodeValue
|
||||||
if (!BINDING_RE.test(text)) return null
|
if (!BINDING_RE.test(text)) return null
|
||||||
var m, i, tokens = []
|
var m, i, tokens = []
|
||||||
|
|
|
||||||
22
src/utils.js
22
src/utils.js
|
|
@ -24,21 +24,22 @@ function typeOf (obj) {
|
||||||
/*
|
/*
|
||||||
* Recursively dump stuff...
|
* Recursively dump stuff...
|
||||||
*/
|
*/
|
||||||
function dumpValue (val) {
|
function dump (val) {
|
||||||
var type = typeOf(val)
|
var type = typeOf(val)
|
||||||
if (type === 'Array') {
|
if (type === 'Array') {
|
||||||
return val.map(dumpValue)
|
return val.map(dump)
|
||||||
} else if (type === 'Object') {
|
} else if (type === 'Object') {
|
||||||
if (val.get) { // computed property
|
if (val.get) { // computed property
|
||||||
return val.get()
|
return val.get()
|
||||||
} else { // object / child scope
|
} else { // object / child scope
|
||||||
var ret = {}
|
var ret = {}, prop
|
||||||
for (var key in val) {
|
for (var key in val) {
|
||||||
if (val.hasOwnProperty(key) &&
|
prop = val[key]
|
||||||
typeof val[key] !== 'function' &&
|
if (typeof prop !== 'function' &&
|
||||||
|
val.hasOwnProperty(key) &&
|
||||||
key.charAt(0) !== '$')
|
key.charAt(0) !== '$')
|
||||||
{
|
{
|
||||||
ret[key] = dumpValue(val[key])
|
ret[key] = dump(prop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -51,7 +52,14 @@ function dumpValue (val) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
typeOf: typeOf,
|
typeOf: typeOf,
|
||||||
dumpValue: dumpValue,
|
dump: dump,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* shortcut for JSON.stringify-ing a dumped value
|
||||||
|
*/
|
||||||
|
serialize: function (val) {
|
||||||
|
return JSON.stringify(dump(val))
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a value from an object based on a path array
|
* Get a value from an object based on a path array
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue