Commit bf3f3e56 authored by Sindre Sorhus's avatar Sindre Sorhus

Merge pull request #184 from loicknuchel/master

Add knockoutjs with require.js in labs
parents b5adebe0 b32ef3c9
# Knockout.js + Require.js • [TodoMVC](http://todomvc.com)
[loicknuchel](https://twitter.com/loicknuchel) adapt /architecture-examples/knockoutjs app with require.js
<!doctype html>
<html lang="en">
<head>
<!-- To move this project from labs to replace in this page "../../../assets" with "../../assets" -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Knockout.js • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
<script data-main="js/main" src="../../../assets/require.min.js"></script>
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" type="text" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add"
placeholder="What needs to be done?" autofocus>
</header>
<section id="main" data-bind="visible: todos().length">
<input id="toggle-all" type="checkbox" data-bind="checked: allCompleted">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: todos">
<li data-bind="css: { completed: completed, editing: editing }">
<div class="view" data-bind="event: { dblclick: $root.editItem }">
<input class="toggle" type="checkbox" data-bind="checked: completed">
<label data-bind="text: title"></label>
<button class="destroy" data-bind="click: $root.remove"></button>
</div>
<input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selectAndFocus: editing, event: { blur: $root.stopEditing }" >
</li>
</ul>
</section>
<footer id="footer" data-bind="visible: completedCount() || remainingCount()">
<span id="todo-count">
<strong data-bind="text: remainingCount">1</strong>
<span data-bind="text: getLabel( remainingCount )"></span> left
</span>
<button id="clear-completed" data-bind="visible: completedCount, click: removeCompleted">Clear completed (<span data-bind="text: completedCount"></span>)</button>
</footer>
</section>
<footer id="info">
<span data-bind="visible: todos().length">Double-click to edit a todo.</span>
<p>Original Knockout version from <a href="https://github.com/ashish01/knockoutjs-todos">Ashish Sharma</a></p>
<p>Rewritten to use Knockout 2.0 and standard template by <a href="http://knockmeout.net">Ryan Niemeyer</a></p>
<p>Patches/fixes for cross-browser compat: <a href="http://twitter.com/addyosmani">Addy Osmani</a></p>
<p>Adapt app with require.js modules: <a href="https://twitter.com/loicknuchel">Loïc Knuchel</a></p>
</footer>
<script src="../../../assets/base.js"></script>
</body>
</html>
\ No newline at end of file
// Author: Loïc Knuchel <loicknuchel@gmail.com>
define({
ENTER_KEY: 13,
localStorageItem: 'todos-knockout-require'
});
\ No newline at end of file
define([
'knockout',
'config/global'
], function(ko, g){
'use strict';
// a custom binding to handle the enter key (could go in a separate library)
ko.bindingHandlers.enterKey = {
init: function( element, valueAccessor, allBindingsAccessor, data ) {
var wrappedHandler, newValueAccessor;
// wrap the handler with a check for the enter key
wrappedHandler = function( data, event ) {
if ( event.keyCode === g.ENTER_KEY ) {
valueAccessor().call( this, data, event );
}
};
// create a valueAccessor with the options that we would want to pass to the event binding
newValueAccessor = function() {
return {
keyup: wrappedHandler
};
};
// call the real event binding's init function
ko.bindingHandlers.event.init( element, newValueAccessor, allBindingsAccessor, data );
}
};
// wrapper to hasfocus that also selects text and applies focus async
ko.bindingHandlers.selectAndFocus = {
init: function( element, valueAccessor, allBindingsAccessor ) {
ko.bindingHandlers.hasfocus.init( element, valueAccessor, allBindingsAccessor );
ko.utils.registerEventHandler( element, 'focus', function() {
element.select();
} );
},
update: function( element, valueAccessor ) {
ko.utils.unwrapObservable( valueAccessor() ); // for dependency
// ensure that element is visible before trying to focus
setTimeout(function() {
ko.bindingHandlers.hasfocus.update( element, valueAccessor );
}, 0 );
}
};
});
\ No newline at end of file
define(function(){
'use strict';
// trim polyfill
if ( !String.prototype.trim ) {
String.prototype.trim = function() {
return this.replace( /^\s+|\s+$/g, '' );
};
}
});
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
// Author: Loïc Knuchel <loicknuchel@gmail.com>
// Require.js allows us to configure shortcut alias
require.config({
paths: {
knockout: 'libs/knockout-2.1.0'
}
});
require([
'knockout',
'config/global',
'viewmodels/todo',
'extends/handlers',
'extends/native'
], function(ko, g, TodoViewModel){
'use strict';
// var app_view = new AppView();
// check local storage for todos
var todos = ko.utils.parseJson( localStorage.getItem( g.localStorageItem ) );
// bind a new instance of our view model to the page
ko.applyBindings( new TodoViewModel( todos || [] ) );
});
define([
'knockout'
], function(ko){
'use strict';
// represent a single todo item
var Todo = function( title, completed ) {
this.title = ko.observable( title );
this.completed = ko.observable( completed );
this.editing = ko.observable( false );
};
return Todo;
});
\ No newline at end of file
define([
'knockout',
'config/global',
'models/Todo'
], function(ko, g, Todo){
'use strict';
// our main view model
var ViewModel = function( todos ) {
var self = this;
// map array of passed in todos to an observableArray of Todo objects
self.todos = ko.observableArray( ko.utils.arrayMap( todos, function( todo ) {
return new Todo( todo.title, todo.completed );
}));
// store the new todo value being entered
self.current = ko.observable();
// add a new todo, when enter key is pressed
self.add = function() {
var current = self.current().trim();
if ( current ) {
self.todos.push( new Todo( current ) );
self.current( '' );
}
};
// remove a single todo
self.remove = function( todo ) {
self.todos.remove( todo );
};
// remove all completed todos
self.removeCompleted = function() {
self.todos.remove(function( todo ) {
return todo.completed();
});
};
// edit an item
self.editItem = function( item ) {
item.editing( true );
};
// stop editing an item. Remove the item, if it is now empty
self.stopEditing = function( item ) {
item.editing( false );
if ( !item.title().trim() ) {
self.remove( item );
}
};
// count of all completed todos
self.completedCount = ko.computed(function() {
return ko.utils.arrayFilter( self.todos(), function(todo) {
return todo.completed();
} ).length;
});
// count of todos that are not complete
self.remainingCount = ko.computed(function() {
return self.todos().length - self.completedCount();
});
// writeable computed observable to handle marking all complete/incomplete
self.allCompleted = ko.computed({
//always return true/false based on the done flag of all todos
read: function() {
return !self.remainingCount();
},
// set all todos to the written value (true/false)
write: function( newValue ) {
ko.utils.arrayForEach(self.todos(), function( todo ) {
// set even if value is the same, as subscribers are not notified in that case
todo.completed( newValue );
});
}
});
// helper function to keep expressions out of markup
self.getLabel = function( count ) {
return ko.utils.unwrapObservable( count ) === 1 ? 'item' : 'items';
};
// internal computed observable that fires whenever anything changes in our todos
ko.computed(function() {
// store a clean copy to local storage, which also creates a dependency on the observableArray and all observables in each item
localStorage.setItem( g.localStorageItem, ko.toJSON( self.todos ) );
}).extend({
throttle: 500
}); // save at most twice per second
};
return ViewModel;
});
\ No newline at end of file
......@@ -93,6 +93,9 @@
<li>
<a href="architecture-examples/plastronjs/index.html" data-source="https://github.com/rhysbrettbowen/PlastronJS" data-content="PlastronJS is an mvc framework built on top of the Closure Library and built to compile with projects that use the Closure Compiler.">PlastronJS *</a>
</li>
<li>
<a href="dependency-examples/knockoutjs_require/index.html" data-source="" data-content="This project is an adaptation of /architecture-examples/knockoutjs with require.js.">Knockout + Require.js *</a>
</li>
</ul>
<ul class="nav nav-pills">
<li>
......
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