Commit 5945668e authored by Rhsy's avatar Rhsy Committed by Sindre Sorhus

Close GH-286: update PlastronJS example.

parent d075d120
...@@ -4,6 +4,13 @@ ...@@ -4,6 +4,13 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>PlastronJS • TodoMVC</title> <title>PlastronJS • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css"> <link rel="stylesheet" href="../../../assets/base.css">
<style type="text/css">
#filters.none li.none,
#filters.active li.active,
#filters.completed li.completed {
font-weight: bold;
}
</style>
<!--[if IE]> <!--[if IE]>
<script src="../../../assets/ie.js"></script> <script src="../../../assets/ie.js"></script>
<![endif]--> <![endif]-->
...@@ -21,14 +28,14 @@ ...@@ -21,14 +28,14 @@
</section> </section>
<footer id="footer"> <footer id="footer">
<span id="todo-count"><strong>0</strong> item left</span> <span id="todo-count"><strong>0</strong> item left</span>
<ul id="filters"> <ul id="filters" class="none">
<li> <li class="none">
<a class="selected" href="#/">All</a> <a href="#/">All</a>
</li> </li>
<li> <li class="active">
<a href="#/active">Active</a> <a href="#/active">Active</a>
</li> </li>
<li> <li class="completed">
<a href="#/completed">Completed</a> <a href="#/completed">Completed</a>
</li> </li>
</ul> </ul>
......
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('mvc.Collection');
goog.require('mvc.Router'); goog.require('mvc.Router');
goog.require('todomvc.listcontrol'); goog.require('todomvc.listcontrol');
goog.require('todomvc.listmodel'); goog.require('todomvc.listmodel');
...@@ -10,38 +7,22 @@ var todolist = new todomvc.listmodel(); ...@@ -10,38 +7,22 @@ var todolist = new todomvc.listmodel();
// Create the control for the collection. // Create the control for the collection.
var todolistControl = new todomvc.listcontrol( todolist ); var todolistControl = new todomvc.listcontrol( todolist );
// HTML already there so use decorate.
todolistControl.decorate( goog.dom.getElement('todoapp') ); todolistControl.decorate( goog.dom.getElement('todoapp') );
// Setup router // Setup router
var router = new mvc.Router(); var router = new mvc.Router();
router.route( '{/}', function() {
/** todolist.set( 'filter', 'none' );
* toggles selected class on filters list
*
* @param {string} chosenFilter selected filter by name.
*/
var toggleFilters = function( chosenFilter ) {
var filters = goog.dom.getElementsByTagNameAndClass( 'A', undefined,
goog.dom.getElement('filters') );
goog.array.forEach( filters, function( filter ) {
goog.dom.classes.enable( filter, 'selected',
goog.dom.getTextContent( filter ) === chosenFilter );
});
};
router.route( '/', function() {
todolistControl.setFilter( todomvc.listcontrol.Filter.ALL );
toggleFilters('All');
}); });
router.route( '/active', function() { router.route( '/active', function() {
todolistControl.setFilter( todomvc.listcontrol.Filter.ACTIVE ); todolist.set( 'filter', 'active' );
toggleFilters('Active');
}); });
router.route( '/completed', function() { router.route( '/completed', function() {
todolistControl.setFilter( todomvc.listcontrol.Filter.COMPLETED ); todolist.set( 'filter', 'completed' );
toggleFilters('Completed');
}); });
goog.provide('todomvc.listcontrol'); goog.provide('todomvc.listcontrol');
goog.require('goog.dom');
goog.require('goog.events.KeyCodes'); goog.require('goog.events.KeyCodes');
goog.require('goog.string'); goog.require('goog.string');
goog.require('mvc.Control'); goog.require('mvc.Control');
...@@ -17,27 +16,10 @@ goog.require('todomvc.todocontrol'); ...@@ -17,27 +16,10 @@ goog.require('todomvc.todocontrol');
*/ */
todomvc.listcontrol = function( list ) { todomvc.listcontrol = function( list ) {
goog.base( this, list ); goog.base( this, list );
this.filter_ = todomvc.listcontrol.Filter.ALL;
}; };
goog.inherits( todomvc.listcontrol, mvc.Control ); goog.inherits( todomvc.listcontrol, mvc.Control );
/**
* @enum {Function}
*/
todomvc.listcontrol.Filter = {
ALL: function() {
return true
},
ACTIVE: function( model ) {
return !model.get('completed')
},
COMPLETED: function( model ) {
return model.get('completed')
}
};
/** /**
* setup for event listeners. * setup for event listeners.
* *
...@@ -48,126 +30,80 @@ todomvc.listcontrol.prototype.enterDocument = function() { ...@@ -48,126 +30,80 @@ todomvc.listcontrol.prototype.enterDocument = function() {
var list = /** @type {Object} */(this.getModel()); var list = /** @type {Object} */(this.getModel());
// Create new model from text box // handle new note entry
var input = this.getEls('input')[0]; this.on( goog.events.EventType.KEYUP, this.handleNewInput, '.todo-entry' );
this.on( goog.events.EventType.KEYUP, function( e ) {
// On return get trimmed text // update complete button based on completed
if ( e.keyCode !== goog.events.KeyCodes.ENTER ) { this.autobind('#clear-completed', {
return; template: 'Clear completed ({$completed})',
} noClick: true, // click should not set completed
show: true // hide when completed == false
var text = goog.string.trim( input.value ); });
if ( !text) {
return;
}
// Create new model
list.newModel({
'title': text
});
input.value = '';
}, 'todo-entry' );
// Clear completed // Clear completed
this.click(function( e ) { this.click( function() {
goog.array.forEach( list.get('completed'), function( model ) { goog.array.forEach( list.getModels( 'completed' ),
model.dispose(); function( model ) {
}); model.dispose();
}, 'clear-completed' );
// Toggle completed
this.click(function( e ) {
var checked = e.target.checked;
goog.array.forEach( list.getModels(), function( model ) {
model.set( 'completed', checked );
});
}, 'toggle-all' );
// Refresh the view on changes that effect the models order
this.anyModelChange(function() {
this.refresh();
list.save();
}, this );
// Toggle footer and main body
this.modelChange(function() {
this.showMainFooter( !!list.getLength() );
}, this );
this.showMainFooter( !!list.getLength() );
// Update counts
this.bind( 'completed', function( completedModels ) {
// Update "left" count
soy.renderElement(goog.dom.getElement('todo-count'),
todomvc.templates.itemsLeft, {
left: list.getLength() - completedModels.length
}); });
}, '.clear-completed' );
// when to check the check all
this.autobind('.toggle-all', {
reqs: 'allDone'
});
// Update clear button // change classes of ULs based on filter
var clearButton = goog.dom.getElement('clear-completed'); this.autobind( 'ul', {
goog.dom.setTextContent( clearButton, reqs: 'filter',
'Clear completed (' + completedModels.length + ')' ); reqClass: ['active', 'completed', 'none']
goog.style.showElement( clearButton, completedModels.length ); } );
// Update checkbox // update the count based on active
var checkBox = this.getEls('.toggle-all')[0]; this.autobind('#todo-count', {
checkBox.checked = completedModels.length === list.getLength(); template: todomvc.templates.itemsLeft,
reqs: 'active'
}); });
// Get the saved todos // show or hide based on the totals
list.fetch(); this.autobind(['#main', 'footer'], {
}; show: 'total',
noClick: true
});
// autolists on modelChange and return refresh function
var refresh = this.autolist( todomvc.todocontrol,
goog.dom.getElement('todo-list') ).fire;
/** // if filter changes refresh view
* show or hide the footer. this.bind( 'filter', refresh );
*
* @param {boolean=} opt_hide whether to hide the footer.
*/
todomvc.listcontrol.prototype.showMainFooter = function( opt_hide ) {
var main = goog.dom.getElement('main');
var footer = goog.dom.getElementsByTagNameAndClass('footer')[0];
goog.style.showElement( main, opt_hide ); // if anything changes save models and refresh view
goog.style.showElement( footer, opt_hide ); this.anyModelChange( refresh );
}; };
/** /**
* sets the function to determine which children are returned by the control. * adds the input as a new item
*
* @param {Function} filter to decide models returned.
*/ */
todomvc.listcontrol.prototype.setFilter = function( filter ) { todomvc.listcontrol.prototype.handleNewInput = function( e ) {
this.filter_ = filter; var input = e.target;
this.refresh();
};
// On return get trimmed text
if ( e.keyCode !== goog.events.KeyCodes.ENTER ) {
return;
}
/** var text = goog.string.trim( input.value );
* refreshes the view of the childen. if ( !text ) {
*/ return;
todomvc.listcontrol.prototype.refresh = function() { }
// Dispose and remove all the children. // Create new model
this.forEachChild(function( child ) { this.getModel().newModel({
child.dispose(); 'title': text
}); });
this.removeChildren( true );
// Create new controls for the models
goog.array.forEach( this.getModel().getModels(this.filter_),
function( model ) {
var newModelControl = new todomvc.todocontrol( model );
this.addChild( newModelControl ); input.value = '';
newModelControl.render( goog.dom.getElement('todo-list') );
}, this );
}; };
...@@ -15,7 +15,6 @@ goog.require('todomvc.templates'); ...@@ -15,7 +15,6 @@ goog.require('todomvc.templates');
*/ */
todomvc.todocontrol = function( model ) { todomvc.todocontrol = function( model ) {
goog.base( this, model ); goog.base( this, model );
}; };
goog.inherits( todomvc.todocontrol, mvc.Control ); goog.inherits( todomvc.todocontrol, mvc.Control );
...@@ -27,10 +26,7 @@ goog.inherits( todomvc.todocontrol, mvc.Control ); ...@@ -27,10 +26,7 @@ goog.inherits( todomvc.todocontrol, mvc.Control );
* @inheritDoc * @inheritDoc
*/ */
todomvc.todocontrol.prototype.createDom = function() { todomvc.todocontrol.prototype.createDom = function() {
var el = soy.renderAsFragment( todomvc.templates.todoItem, { var el = soy.renderAsElement( todomvc.templates.todoItem, null, null );
model: this.getModel().toJson()
}, null );
this.setElementInternal(/** @type {Element} */(el)); this.setElementInternal(/** @type {Element} */(el));
}; };
...@@ -45,31 +41,35 @@ todomvc.todocontrol.prototype.enterDocument = function() { ...@@ -45,31 +41,35 @@ todomvc.todocontrol.prototype.enterDocument = function() {
var model = this.getModel(); var model = this.getModel();
// Toggle complete // Toggle complete
this.click(function( e ) { this.autobind('.toggle', '{$completed}');
model.set( 'completed', e.target.checked );
}, 'toggle' );
// Delete the model // Delete the model
this.click(function( e ) { this.click(function( e ) {
model.dispose(); model.dispose();
}, 'destroy' ); }, '.destroy' );
// keep label inline with title
this.autobind( 'label', '{$title}')
var inputEl = this.getEls('.edit')[0]; var inputEl = this.getEls('.edit')[0];
// Dblclick to edit // Dblclick to edit
this.on( goog.events.EventType.DBLCLICK, function( e ) { this.on( goog.events.EventType.DBLCLICK, function( e ) {
goog.dom.classes.add( this.getElement(), 'editing' ); goog.dom.classes.add( this.getElement(), 'editing' );
inputEl.value = model.get('title');
inputEl.focus(); inputEl.focus();
}, 'view' ); }, '.view' );
// Save on edit // blur on enter
this.on( goog.events.EventType.KEYUP, function( e ) { this.on( goog.events.EventType.KEYUP, function( e ) {
if ( e.keyCode === goog.events.KeyCodes.ENTER ) { if ( e.keyCode === goog.events.KeyCodes.ENTER ) {
model.set( 'title', inputEl.value ); e.target.blur();
} }
}, 'edit' ); });
// finish editing on blur
this.on( goog.events.EventType.BLUR, function( e ) { this.on( goog.events.EventType.BLUR, function( e ) {
model.set( 'title', inputEl.value ); goog.dom.classes.remove( this.getElement(), 'editing' );
}, 'edit' ); });
// bind the title and the edit input
this.autobind('.edit', '{$title}');
}; };
PlastronJS @ 3a58c34c
Subproject commit 3a58c34c673ae0c5ba60eda5727f0d8332cb7fcf
...@@ -12,13 +12,37 @@ goog.require('todomvc.todomodel'); ...@@ -12,13 +12,37 @@ goog.require('todomvc.todomodel');
todomvc.listmodel = function() { todomvc.listmodel = function() {
var todosSchema = { var todosSchema = {
// number of completed
'completed': { 'completed': {
get: function() { get: function() {
return this.getModels(function( mod ) { return this.getModels( 'completed' ).length;
return mod.get('completed'); },
models: true
},
'allDone': {
get: function() {
return this.getLength() == this.getModels( 'completed' ).length;
},
set: function( done ) {
goog.array.forEach( this.getModels( 'none' ), function( model ) {
model.set( 'completed', done );
}); });
}, },
models: true models: true
},
// number of active models
'active': {
get: function() {
return this.getLength() - this.getModels( 'completed' ).length;
},
models: true
},
// the total
'total': {
get: function() {
return this.getLength();
},
models: true
} }
}; };
...@@ -28,15 +52,45 @@ todomvc.listmodel = function() { ...@@ -28,15 +52,45 @@ todomvc.listmodel = function() {
'schema': todosSchema, 'schema': todosSchema,
'modelType': todomvc.todomodel 'modelType': todomvc.todomodel
}); });
// fetch from localstorage
this.fetch();
// save on any changes
this.anyModelChange( this.save );
}; };
goog.inherits( todomvc.listmodel, mvc.Collection ); goog.inherits( todomvc.listmodel, mvc.Collection );
todomvc.listmodel.Filter = {
'none': function() {
return true
},
'active': function( model ) {
return !model.get('completed')
},
'completed': function( model ) {
return model.get('completed')
}
};
/**
* return models based on current filter or filter given
*
* @inheritDoc
*/
todomvc.listmodel.prototype.getModels = function(opt_filter) {
return goog.base(this, 'getModels',
todomvc.listmodel.Filter[ opt_filter || this.get( 'filter' ) ] );
};
/** /**
* @return {Object} todos as json. * @return {Object} todos as json.
*/ */
todomvc.listmodel.prototype.toJson = function() { todomvc.listmodel.prototype.toJson = function() {
return goog.array.map( this.getModels(), function( mod ) { return goog.array.map( this.getModels( 'none' ), function( mod ) {
return mod.toJson(); return mod.toJson();
}); });
}; };
...@@ -13,6 +13,7 @@ goog.require('mvc.Model.ValidateError'); ...@@ -13,6 +13,7 @@ goog.require('mvc.Model.ValidateError');
todomvc.todomodel = function( opt_options ) { todomvc.todomodel = function( opt_options ) {
goog.base( this, opt_options ); goog.base( this, opt_options );
// title must have a length, also format to remove spaces
this.setter( 'title', function( title ) { this.setter( 'title', function( title ) {
var updated = goog.string.trim( title ); var updated = goog.string.trim( title );
...@@ -23,6 +24,7 @@ todomvc.todomodel = function( opt_options ) { ...@@ -23,6 +24,7 @@ todomvc.todomodel = function( opt_options ) {
return updated; return updated;
}); });
// when a note title is no longer valid then remove it
this.errorHandler(function() { this.errorHandler(function() {
this.dispose(); this.dispose();
}); });
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -5,19 +5,5 @@ ...@@ -5,19 +5,5 @@
"output-wrapper": "(function(){%output%})();", "output-wrapper": "(function(){%output%})();",
"mode": "ADVANCED", "mode": "ADVANCED",
"level": "VERBOSE", "level": "VERBOSE",
"output-file": "js/compiled.js", "output-file": "js/compiled.js"
"define": {
"goog.LOCALE": "en_GB"
},
"checks": {
"checkRegExp": "WARNING",
"checkTypes": "WARNING",
"checkVars": "WARNING",
"deprecated": "WARNING",
"fileoverviewTags": "WARNING",
"invalidCasts": "WARNING",
"missingProperties": "WARNING",
"nonStandardJsDocs": "WARNING",
"undefinedVars": "WARNING"
}
} }
{namespace todomvc.templates} {namespace todomvc.templates}
/** /**
* @param model
*/ */
{template .todoItem} {template .todoItem}
<li {if $model['completed']}class="completed"{/if}> <li>
<div class="view"> <div class="view">
<input class="toggle" type="checkbox" {if $model['completed']}checked{/if}> <input class="toggle" type="checkbox">
<label>{$model['title']}</label> <label></label>
<button class="destroy"></button> <button class="destroy"></button>
</div> </div>
<input class="edit"> <input class="edit" type="text">
</li> </li>
{/template} {/template}
/** /**
* @param left * use template for the 's' logic.
* Used in autobind so gets data under model namespace.
*
* @param model
*/ */
{template .itemsLeft} {template .itemsLeft}
<strong>{$left}</strong> item{if $left != 1}s{/if} left <strong>{$model['active']}</strong> item{if $model['active'] != 1}s{/if} left
{/template} {/template}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment