todomvc
Version:
> Helping you select an MV\* framework
117 lines (103 loc) • 4.44 kB
JavaScript
define(["dojo/_base/declare", "dojox/mvc/StatefulModel", "todo/store/LocalStorage", "dojox/mvc", "dojo/_base/lang", "dojo/_base/array"],
function(declare, StatefulModel, LocalStorage, mvc, lang, array) {
return declare([StatefulModel], {
/**
* Default model structure, overriden by any
* items found in localStorage.
*/
data: {
id: "todos-dojo",
todos : [],
incomplete: 0,
complete: 0
},
/**
* Initialise our custom dojo store, backed by localStorage. This will be
* used to read the initial items, if available, and persist the current items
* when the application finishes.
*/
store: new LocalStorage(),
constructor: function () {
/**
* Attempt to read todo items from localStorage,
* returning default value the first time the application
* is loaded... The "id" parameter is used as the unique
* localStorage key for this object.
*/
var data = this.store.get(this.data.id) || this.data;
this._createModel(data);
this.setUpModelBinding();
this.updateTotalItemsLeft();
},
setUpModelBinding: function () {
/**
* Set up a composite model attribute, "complete", that is automatically
* calculated whenever the "incomplete" source value is modified. The "complete"
* attribute is bound to a view widget, displaying the number of items that can
* be cleared using the link.
*/
mvc.bind(this.incomplete, "value", this.complete, "value", lang.hitch(this, function (value) {
return this.todos.get("length") - value;
}));
/**
* Bind all pre-populated todo items to update the
* total item values when the "completed" attribute is changed.
*/
array.forEach(this.todos, lang.hitch(this, "bindItemProps"));
/**
* Whenever the "todos" array is modified, an element is added
* or deleted, we need to recompute the model composite values.
* Binding attributes changes to the "onTodosModelChange" function
* using "watch", an attribute on dojo.Stateful objects.
*/
this.todos.watch(lang.hitch(this, "onTodosModelChange"));
},
/**
* Set up binding on a todo item, so that when the
* item's checked attribute changes, we re-calculate
* the composite model attribute's value, "complete".
*
* We also need to remove any tasks with empty titles.
*/
bindItemProps: function (item) {
mvc.bindInputs([item.completed], lang.hitch(this, "updateTotalItemsLeft"));
mvc.bindInputs([item.title], lang.hitch(window, setTimeout, lang.hitch(this, "deleteEmptyTasks")));
},
/**
* Search through current tasks list, removing all
* with empty titles.
*/
deleteEmptyTasks: function () {
var len = this.todos.length, idx = 0;
while (idx < len) {
if (!this.todos[idx].title.value.length) {
this.todos.remove(idx);
len--;
continue;
}
idx++;
}
},
/**
* When todos array is modified, we need to update the composite
* value attributes. If the modification was an addition, ensure the
* "completed" attribute is being watched for updates.
*/
onTodosModelChange: function (prop, oldValue, newValue) {
this.updateTotalItemsLeft();
if (typeof prop === "number" && !oldValue && newValue) {
this.bindItemProps(newValue);
}
},
/**
* Update the model's "incomplete" value with the
* total number of items not finished. This will automatically
* cause the bound "complete" value to be updated as well.
*/
updateTotalItemsLeft: function () {
this.incomplete.set("value", array.filter(this.todos, function (item) {
return item && !item.completed.value;
}).length);
}
});
});