I have been watching AngularJS for a while recently, and I will summarize it at some time.
Official website address: http://angularjs.org/
Recommend a few tutorials first
1. AngularJS introductory tutorial is relatively basic and is a translation of the official Tutorial.
2. Seven steps from AngularJS novices to Experts is also relatively basic, and an online music playback website was created.
3. The AngularJS development guide tutorial is quite comprehensive, but I feel that the translation is a bit obscure and difficult to understand.
After reading these tutorials, I felt that AngularJS also knew a little, so I wanted to do something with it, so I analyzed the todomvc written by AngularJS.
Todomvc official website address: http://todomvc.com/
The project directory is as follows:
There are two folders in bower_components, among which the angular folder is used to be the same as the angular.js file. The todomvc-common folder contains the unified css/js of all todo projects (just used to generate the left content and has nothing to do with the project) and pictures.
The js folder is a big header, and the corresponding controller (controller)/directive (instructions)/service (service) and app.js are placed inside.
The test folder contains the code for testing and does not analyze.
index.html is the project's view page.
Let's take a look at app.js
The code copy is as follows:
/*global angular */
/*jshint unused:false */
'use strict';
/**
* The main TodoMVC app module
*
* @type {angular.Module}
*/
var todomvc = angular.module('todomvc', []);
It defines a module todomvc
Take a look at todoStorage.js under services
The code copy is as follows:
/*global todomvc */
'use strict';
/**
* Services that persists and retrieves TODOs from localStorage
*/
todomvc.factory('todoStorage', function () {
// todos unique identifier for JSON string storage
var STORAGE_ID = 'todos-angularjs';
return {
// Take out todos from localStorage and parse it into a JSON object
get: function () {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
// Convert the todos object into a JSON string and store it in localStorage
put: function (todos) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
}
};
});
The todoStorage service method is created using the factory method. The essence of this service method is to return two methods get and put. Both use the features of JSON2 and HTML5. get takes the content of todos from localStorage and parses it into JSON, put converts todos into JSON string and stores it in localStorage.
Let’s take a look at the two command files below directives.
todoFocus.js
The code copy is as follows:
/*global todomvc */
'use strict';
/**
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true
*/
todomvc.directive('todoFocus', function todoFocus($timeout) {
return function (scope, elem, attrs) {
// Add listening to the value of the todoFocus property
scope.$watch(attrs.todoFocus, function (newVal) {
if (newVal) {
$timeout(function () {
elem[0].focus();
}, 0, false);
}
});
};
});
In the parameter returning function, elem is an array of elements containing the instruction, and attrs is an object composed of all attributes, attribute names, etc. of the element.
Two AngularJS methods are used
$watch(watchExpression, listener, objectEquality) Register a listener callback. Whenever the watchExpression changes, the listener callback will be executed.
$timeout(fn[, delay][, invokeApply]) When the value of timeout is reached, the fn function is executed.
todoFocus.js creates the todoFocus directive. When an element has a todoFocus property, the directive will add a listener to the value of the todoFocus property of the element. If the value of the todoFocus property is changed to true, $timeout(function () {elem[0].focus();}, 0, false); the delay time is 0 seconds, so elem[0].focus() will be executed immediately.
todoEscape.js
The code copy is as follows:
/*global todomvc */
'use strict';
/**
* Directive that executes an expression when the element it is applied to gets
* an `escape` keydown event.
*/
todomvc.directive('todoEscape', function () {
var ESCAPE_KEY = 27;
return function (scope, elem, attrs) {
elem.bind('keydown', function (event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.todoEscape);
}
});
};
});
todoEscape.js creates the todoEscape directive. When the Escape key is pressed, the expression of attrs.todoEscape is executed.
Take a look at the big head, todoCtrl.js in the controllers folder. This file is a little longer, so I just wrote comments.
The code copy is as follows:
/*global todomvc, angular */
'use strict';
/**
* The main controller for the app. The controller:
* - retrieves and persists the model via the todoStorage service
* - exposes the model to the template and provide event handlers
*/
todomvc.controller('TodoCtrl', function TodoCtrl($scope, $location, todoStorage, filterFilter) {
// Get todos from localStorage
var todos = $scope.todos = todoStorage.get();
// Record new todo
$scope.newTodo = '';
// Record edited todo
$scope.editedTodo = null;
// Execute the method when the value of todos changes
$scope.$watch('todos', function (newValue, oldValue) {
// Get the number of unfinished todos
$scope.remainingCount = filterFilter(todos, { completed: false }).length;
// Get the number of completed todos
$scope.completedCount = todos.length - $scope.remainingCount;
// $scope.allChecked is true if and only if $scope.remainingCount is 0
$scope.allChecked = !$scope.remainingCount;
// When the new value of todos and the old value are not equal, store todos into localStorage
if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
todoStorage.put(todos);
}
}, true);
if ($location.path() === '') {
// If $location.path() is empty, set to /
$location.path('/');
}
$scope.location = $location;
// Execute the method when the value of location.path() changes
$scope.$watch('location.path()', function (path) {
// Get state filter
// If path is '/active', the filter is { completed: false }
// If path is '/completed', the filter is { completed: true }
// Otherwise, the filter is null
$scope.statusFilter = (path === '/active') ?
{ completed: false } : (path === '/completed') ?
{ completed: true } : null;
});
// Add a new todo
$scope.addTodo = function () {
var newTodo = $scope.newTodo.trim();
if (!newTodo.length) {
return;
}
// Add a todo to todos, the completed property defaults to false
todos.push({
title: newTodo,
completed: false
});
// Empty
$scope.newTodo = '';
};
// Edit a todo
$scope.editTodo = function (todo) {
$scope.editedTodo = todo;
// Clone the original todo to restore it on demand.
// Save todo before editing and prepare for restoring editing
$scope.originalTodo = angular.extend({}, todo);
};
// Edit todo to complete
$scope.doneEditing = function (todo) {
// Empty
$scope.editedTodo = null;
todo.title = todo.title.trim();
if (!todo.title) {
// If the title of todo is empty, remove the todo
$scope.removeTodo(todo);
}
};
// Restore todo before editing
$scope.revertEditing = function (todo) {
todos[todos.indexOf(todo)] = $scope.originalTodo;
$scope.doneEditing($scope.originalTodo);
};
// Remove todo
$scope.removeTodo = function (todo) {
todos.splice(todos.indexOf(todo), 1);
};
// Clear completed todos
$scope.clearCompletedTodos = function () {
$scope.todos = todos = todos.filter(function (val) {
return !val.completed;
});
};
// Mark all todo status (true or false)
$scope.markAll = function (completed) {
todos.forEach(function (todo) {
todo.completed = completed;
});
};
});
Finally, let’s take a look at index.html, our analysis of this file one by one.
The code copy is as follows:
<!doctype html>
<html lang="en" ng-app="todomvc" data-framework="angularjs">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AngularJS • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
<style>[ng-cloak] { display: none; }</style>
</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 track by $index" ng-class="{completed: todo.completed, editing: todo == editedTodo}">
<div>
<input type="checkbox" ng-model="todo.completed">
<label ng-dblclick="editTodo(todo)">{{todo.title}}</label>
<button ng-click="removeTodo(todo)"></button>
</div>
<form ng-submit="doneEditing(todo)">
<input ng-trim="false" ng-model="todo.title" todo-escape="revertEditing(todo)" ng-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="clearCompletedTodos()" ng-show="completedCount">Clear completed ({{completedCount}})</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> and
<a href="http://igorminar.com">Igor Minar</a>
</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/angular/angular.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/todoEscape.js"></script>
</body>
</html>
First of all, we will introduce the corresponding JS at the bottom, so I won’t say much about this.
The code copy is as follows:
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/angular/angular.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/todoEscape.js"></script>
Define style[ng-cloak], which contains the ng-cloak attribute and is not visible.
The code copy is as follows:
<style>[ng-cloak] { display: none; }</style>
Let’s look at the html to add todo. The bound model is newTodo. The method of submit is addTodo() in todoCtrl.js. A todo will be added. Click Enter, and the submission event will be triggered by default, which will trigger the addTodo() method and add a todo to todos.
The code copy is as follows:
<form id="todo-form" ng-submit="addTodo()">
<input id="new-todo" placeholder="What needs to be done?" ng-model="newTodo" autofocus>
</form>
Look at the html showing todos
The code copy is as follows:
<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 track by $index" ng-class="{completed: todo.completed, editing: todo == editedTodo}">
<div>
<input type="checkbox" ng-model="todo.completed">
<label ng-dblclick="editTodo(todo)">{{todo.title}}</label>
<button ng-click="removeTodo(todo)"></button>
</div>
<form ng-submit="doneEditing(todo)">
<input ng-trim="false" ng-model="todo.title" todo-escape="revertEditing(todo)" ng-blur="doneEditing(todo)" todo-focus="todo == editedTodo">
</form>
</li>
</ul>
</section>
The section uses the ngShow method to determine whether it is displayed based on the length of todos. The ng-cloak attribute is added to prevent the pages that AngularJS are not processed at the beginning. You can remove the refresh and try it.
The checkbox with id is toggle-all is bound to the allChecked model, click to trigger markAll(allChecked), pass the value of allChecked, and mark all todos.
Use ngRepeat loop to generate li tags, todo in todos | filter:statusFilter track by $index, loop todos, filter with statusFilter, and trace with $index. ngClass binds two classes, {completed: todo.completed, editing: todo == editedTodo}, if todo.completed is true, add completed class, and if todo==editedTodo, add editing class. class is bound to todo.completed for toggle's checkbox. The label displayed in the todo title binds a double-click event. Double-click to trigger editTodo (todo). editTodo will assign todo to editedTodo, and then trigger the todoFocus instruction in the form below. At this time, the input in the form can be visible. Press Esc to trigger revertEditing(todo). Before reverting to editing, press Enter or lose focus to trigger doneEditing(todo) to save the edited todo. class binds the click event to destroy button, click to trigger removeTodo(todo), and delete the todo.
Finally, look at the html displayed by todos statistics
The code copy is as follows:
<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="clearCompletedTodos()" ng-show="completedCount">Clear completed ({{completedCount}})</button>
</footer>
The ng-pluralize tag implements the display item left when the number of remainingCounts is 1, otherwise the display items left.
The ul tag with id is filters is selected according to the content of location.path().
id adds a click event to the clear-completed button, triggering clearCompletedTodos(), clearing all completed todos.
Let’s stop here today’s notes. They are all personal experiences. I hope you can like them.