Commit a7abc6cf authored by Igor Minar's avatar Igor Minar

AngularJS: add angularjs-perf version

this one has better performance at the cost of complexity

for a toy app like this one the difference is insignificant,
but it might be good to see the two versions and compare them
and based on that be able to make the trade-offs in real apps

the angularjs version does deep watching of the todos array object.
that means that it keeps an in memory copy of the whole array that
is used for dirty checking in order to detect model mutations. For
toy app like this it's totally fine and in fact encouraged practice
since you are trading off a little bit of memory and performance
for simplicity.

However in a large project where you are dealing with array of 100s
or 1000s of large objects you definitely don't want to use this
approach. Instead you want to use a different way of doing the same
thing, which requires more code but is a lot more efficient.
parent 7ded351d
<!doctype html>
<html lang="en" ng-app="todomvc">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
<style>[ng-cloak] {display: none}</style>
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp" ng-controller="TodoCtrl">
<header id="header">
<h1>todos</h1>
<form id="todo-form" ng-submit="addTodo()">
<input id="new-todo" placeholder="What needs to be done?" ng-model="newTodo" autofocus>
</form>
</header>
<section id="main" ng-show="todos.length" ng-cloak>
<input id="toggle-all" type="checkbox" ng-model="allChecked" ng-click="markAll(allChecked)">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<li ng-repeat="todo in todos | filter:statusFilter" ng-dblclick="editTodo(todo)" ng-class="{completed: todo.completed, editing: todo == editedTodo}">
<div class="view">
<input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="todoCompleted(todo)">
<label>{{todo.title}}</label>
<button class="destroy" ng-click="removeTodo(todo)"></button>
</div>
<form ng-submit="doneEditing(todo)">
<input class="edit" ng-model="todo.title" todo-blur="doneEditing(todo)" todo-focus="todo == editedTodo">
</form>
</li>
</ul>
</section>
<footer id="footer" ng-show="todos.length" ng-cloak>
<span id="todo-count"><strong>{{remainingCount}}</strong>
<ng-pluralize count="remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize>
</span>
<ul id="filters">
<li>
<a ng-class="{selected: location.path() == '/'} " href="#/">All</a>
</li>
<li>
<a ng-class="{selected: location.path() == '/active'}" href="#/active">Active</a>
</li>
<li>
<a ng-class="{selected: location.path() == '/completed'}" href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" ng-click="clearDoneTodos()" ng-show="remainingCount < todos.length">Clear completed ({{todos.length - remainingCount}})</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo.</p>
<p>Credits: <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>, <a href="http://ericbidelman.com">Eric Bidelman</a>, <a href="http://jacobmumm.com">Jacob Mumm</a></p>
</footer>
<script src="../../assets/base.js"></script>
<script src="js/libs/angular/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/todoCtrl.js"></script>
<script src="js/services/todoStorage.js"></script>
<script src="js/directives/todoFocus.js"></script>
<script src="js/directives/todoBlur.js"></script>
</body>
</html>
/**
* The main TodoMVC app module.
*
* @type {angular.Module}
*/
var todomvc = angular.module('todomvc', []);
/**
* The main controller for the app. The controller:
* - retrieves and persist the model via the todoStorage service
* -
* exposes the model to the template and
*/
todomvc.controller( 'TodoCtrl', function TodoCtrl( $scope, $location, todoStorage, filterFilter ) {
var todos = $scope.todos = todoStorage.get();
$scope.newTodo = "";
$scope.remainingCount = filterFilter(todos, {completed: false}).length;
$scope.editedTodo = null;
if ( $location.path() === '' ) $location.path('/');
$scope.location = $location;
$scope.$watch('location.path()', function( path ) {
$scope.statusFilter = (path == '/active') ?
{ completed: false } : (path == '/completed') ?
{ completed: true } : null;
});
$scope.$watch('remainingCount == 0', function( val ) {
$scope.allChecked = val;
});
$scope.addTodo = function() {
if ($scope.newTodo.length === 0) return;
todos.push({
title: $scope.newTodo,
completed: false
});
todoStorage.put(todos);
$scope.newTodo = '';
$scope.remainingCount++;
};
$scope.editTodo = function( todo ) {
$scope.editedTodo = todo;
};
$scope.doneEditing = function( todo ) {
$scope.editedTodo = null;
if ( !todo.title ) $scope.removeTodo(todo);
todoStorage.put(todos);
};
$scope.removeTodo = function( todo ) {
$scope.remainingCount -= todo.completed ? 0 : 1;
todos.splice(todos.indexOf(todo), 1);
todoStorage.put(todos);
};
$scope.todoCompleted = function( todo ) {
todo.completed ? $scope.remainingCount-- : $scope.remainingCount++;
todoStorage.put(todos);
};
$scope.clearDoneTodos = function() {
$scope.todos = todos = todos.filter(function( val ) {
return !val.completed;
});
todoStorage.put(todos);
};
$scope.markAll = function( done ) {
todos.forEach(function( todo ) {
todo.completed = done;
});
$scope.remainingCount = done ? 0 : todos.length;
todoStorage.put(todos);
};
});
/**
* Directive that executes an expression when the element it is applied to loses focus.
*/
todomvc.directive('todoBlur', function() {
return function( scope, elem, attrs ) {
elem.bind('blur', function() {
scope.$apply(attrs.todoBlur);
});
};
});
/**
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true.
*/
todomvc.directive('todoFocus', function( $timeout ) {
return function( scope, elem, attrs ) {
scope.$watch(attrs.todoFocus, function( newval ) {
if ( newval ) {
$timeout(function() {
elem[0].focus();
elem[0].select();
}, 0, false);
}
});
};
});
/**
* Services that persists and retrieves TODOs from localStorage.
*/
todomvc.factory( 'todoStorage', function() {
var STORAGE_ID = 'todos-angularjs-perf';
return {
get: function() {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
put: function( todos ) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
}
};
});
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