Poslední dobou se rozmáhají všelijaké javascriptové frameworky na bází MVC a tak jsem se rozhodl, že by nebylo od věci, některý z nich vyzkoušet. V různých diskusích a článcích jsem neustále narážel na AngularJS a Backbone.js, A jelikož mi přišel z těchto dvou na první pohled Angular sympatičtější, zkusil jsem si v něm s využitím různých externích zdrojů napsat jednoduchou todo aplikaci, o kterou bych se chtěl podělit.
Ale prvně, než začnu se samotnou aplikací. Co to vlastně ten AngularJS je, pro ty, kteří o něm slyšeli poprvé.
AngularJS je otevřený framework vyvíjený Googlem a komunitou. Je postaven na architektuře MVC, která je v dnešní době velice populární. A tak jako je snaha zpřehlednit vývoj aplikací psaných v PHP prostřednictvím různých frameworků, které se snaží programátorovi vnutit správný návyk, je snaha i u javascriptových aplikací jít tímto směrem. Navíc, obecně tyto frameworky, ale i Angular, se snaží spoustu úloh vyřešit za vás. Nemusíte třeba zdlouhavě řešit, jak budete aktualizovat informace v šablonách nebo jak budete muset problematicky měnit stavy prvků v závislosti na tom, jak se budou měnit data.
Pokud o Angularu chcete vědět více, určitě se podívejte na jeho stránky, kde najdete nejen spoustu tutoriálů, ale i podrobnou a dobře zpracovanou dokumentaci.
Ale teď se vraťme ke zmíněné todo aplikaci. Předesílám, že sám jsem začátečník, takže se někde mohu mýlit nebo být nepřesný.
Todo list
Jedná se o jednoduchý úkolníček, kde jste schopni přidávat záznamy, editovat je a případně mazat. U každého záznamu také nechybí možnost jej zaškrtnout, což indikuje, zda je úkol již dokončen. V případě, že jsou některé úkoly dokončeny, je možné je smazat hromadně.
Architektura
Zde je k dispozici přehledná tabulka, co naleznete v každém souboru.
Soubor | Popis |
---|---|
index.html | šablona aplikace |
css/screen,css | kaskádové styly aplikace |
js/app.js | obsahuje definici základního modulu |
js/todoCtrl.js | obsahuje definici controlleru aplikace |
js/todoStorage.js | obsahuje službu, která poskytuje rozhraní k ukládání úkolů do localStorage |
js/angular.min..js | minifikovaná verze AngularJS |
screen.css
CSS aplikace není třeba dále komentovat.
app.js
'use strict';
// definice modulu
var todoapp = angular.module('todoapp', []);
todoStorage.js
'use strict';
// tovarna vytvori sluzbu, ktera zajistuje persistenci todos prostrednoctvim localStorage
todoapp.factory('todoStorage', function () {
var STORAGE_ID = 'todos-angularjs';
return {
// vrati kolekci todos v JSON
get:function () {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
// aktualizuje todos
put:function (todos) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
}
};
});
Zde se vytváří služba, která poskytuje rozhraní pro práci s localStorage. Jedná se o jednoduché operace get a put, kde první vrací data ve formátu JSON a druhá naopak tyto data ukládá. Prostor, kam jsou data v localStorage uloženy je definován identifikátorem STORAGE_ID.
todoCtrl.js
'use strict';
// definice controlleru
todoapp.controller('TodoCtrl', function TodoCtrl($scope, todoStorage, filterFilter) {
// ziskame todos ze storage
var todos = $scope.todos = todoStorage.get();
$scope.newTodo = "";
$scope.editedTodo = null;
// pri detekci zmeny v todos, dojde k volani funkce, ktera prepocita remainingCount a aktualizuje todos ve storage
$scope.$watch('todos', function () {
$scope.remainingCount = filterFilter(todos, {completed:false}).length;
todoStorage.put(todos);
}, true);
// funkce pro pridani noveho zaznamu
$scope.addTodo = function () {
if (!$scope.newTodo.length) {
return;
}
todos.push({
title:$scope.newTodo,
completed:false
});
$scope.newTodo = '';
};
// vyvola se pri editaci zaznamu
$scope.editTodo = function (todo) {
$scope.editedTodo = todo;
};
// vyvola se pri ulozeni editovaneho zaznamu
$scope.doneEditing = function (todo) {
$scope.editedTodo = null;
if (!todo.title) {
$scope.removeTodo(todo);
}
};
// smaze zaznam
$scope.removeTodo = function (todo) {
todos.splice(todos.indexOf(todo), 1);
};
// odstrani vsechny zaznamy, ktere jsou dokonceny
$scope.clearDoneTodos = function () {
$scope.todos = todos = todos.filter(function (val) {
return !val.completed;
});
};
});
V této části je definován controller. Je mu poskytnutý #scope, což je de facto takový operativní prostor/rámec aplikace, z kterého čerpá informace i šablona. Dále je mu poskytnuta námi definovaná služba todoStorage a nakonec i filterFilter.
V těle controlleru získáme data z uložiště, nadefinujeme, které informace se mají aktualizovat při změně stavu (třeba když přidáme nový todo, smažeme apod.). Dále pak definujeme funkce, které voláme ze šablony a které manipulují s úkoly. Snažil jsem se patřičně okomentovat příslušné bloky kódu, aby bylo jasné, k čemu daná část slouží, pakliže by to nebylo zřejmé.
index.html
<!doctype html>
<html lang="en" ng-app="todoapp">
<head>
<meta charset="utf-8">
<title>Todo list AngularJS</title>
<link rel="stylesheet" href="css/screen.css">
</head>
<body>
<section id="todo-app" ng-controller="TodoCtrl">
<header>
<h1>Todo list</h1>
<form ng-submit="addTodo()">
<input id="new-todo" placeholder="New todo" ng-model="newTodo" autofocus>
</form>
</header>
<section id="main" ng-show="todos.length" ng-cloak>
<table id="todo-list">
<tr ng-repeat="todo in todos" ng-class="{completed: todo.completed, editing: todo == editedTodo}">
<td>
<input type="checkbox" ng-model="todo.completed">
</td>
<td>
<div class="body">
<span ng-dblclick="editTodo(todo)">{{todo.title}}</span>
</div>
<form ng-submit="doneEditing(todo)">
<input class="edit" ng-model="todo.title">
</form>
</td>
<td>
<a ng-click="removeTodo(todo)">Delete</a>
</td>
</tr>
<tr class="last">
<td>
</td>
<td>
<span>{{remainingCount}} open items</span>
</td>
<td>
<a ng-click="clearDoneTodos()">Delete completed</a>
</td>
</tr>
</table>
</section>
<footer>
<p>Double-click or enter to save/edit a todo.</p>
</footer>
</section>
<script src="js/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/todoCtrl.js"></script>
<script src="js/todoStorage.js"></script>
</body>
</html>
V této šabloně lze nalézt u některých tagů atributy začínající ng-, které Angular čte a podle nichž se šablonou operuje.
Na začátku u tagu section si lze všimnout této definice ng-controller=“TodoCtrl”, která říká, že zde začíná prostor, který přísluší todo controlleru.
Dále si lze u formuláře odvodit, že ng-submit=“addTodo()” znamená, že odeslaná data budou směřována k funkci addTodo() definované v todo controlleru, přičemž input ve formuláři disponuje označením ng-model=“newTodo”, který je pak přístupný ze $scope.
Dále za zmínku stojí tyto další atributy:
Atribut | Popis |
---|---|
ng-show | zobrazí prvek, pakliže obsah atributu je vyhodnocen jako pravdivý |
ng-repeat | jedná se o cyklus, který listuje kolekcí dat |
ng-class | přidruží příslušnou třídu k prvku, pakliže je podmínka vyhodnocena kladně (zápis odpovídá tvaru {třída:podmínka} – klasický JSON) |
ng-click | spustí akci při vyvolání události kliku |
ng-dblclick | spustí akci při vyvolání události dvojkliku |
Dále stojí také za zmínku {{remainingCount}}, který vypisuje počet nedokončených úloh. V controlleru se přepočítává vždy při nějaké změně a je definován v tělu vstupní funkce $watch.
Závěr
V budoucnu bych rád na této aplikaci ukázal i některé jiné konstrukce, kterýma AngularJS disponuje. Ale to někdy příště.
Celý projekt je možné prohlédnout a stáhnout na Githubu.