Знакомство с контроллерами и директивами в Angular.js
В этой статьи мы напишем простой контроллер на Angular.js и заставим его выводить данные в эту таблицу при помощи встроенных в Angular директив. Более того, мы оживим эту форму так, чтобы она действительно добавляла новые строки в таблицу транзакций.
Контроллеры в Angular.js являются связующим звеном между html-разметкой и "моделью". В Angular за модель отвечает сразу 4 разных вида объектов, которые мы рассмотрим позже. Опустим пока что модель и будем хранить все данные прямо в контроллере.
Задачей контроллера, как и в других MVC фреймворках, является получение данных из модели и вывод их пользователю, или же, в другом направлении, получение данных из представления и отправка их в модель.
В первую очередь, удалим src/app/main.controller.js
, src/app/main.controller.spec.js
, src/app/main.html
и src/components/navbar/
, которые для нас сгенерировал Yeoman.
Пишем первый контроллер
Наш первый контроллер будет называться TransactionsCtrl
. Согласно соглашениям о наименовании в Angular.js, имя контроллера строится как "имя + Ctrl".
Создадим файл src/app/main/transactions.controller.js
. В этом файле определим сам контроллер:
//src/app/main/transactions.controller.js
angular.module("ngmkdev").controller('TransactionsCtrl', function($scope) {
});
Заметьте, что нам не нужно подключать свежесозданный файл вручную при помощи тега <script>
. Когда мы запустим gulp serve
, Gulp начнёт мониторить изменения в файлах и папках и обновлять временный index.html
внутри папки .tmp/
(он, кстати, и загружается когда вы открываете в браузере localhost:4567
). А когда придёт время собирать финальную версию приложения, то команда gulp build
сделает тоже самое и вернёт нам необходимый index.html
с ужатыми и подключенными нужными библиотеками и файлами приложения.
На данном этапе мы не будем разбивать вёрстку приложения на несколько файлов и напрямую отредактируем src/index.html
(поэтому мы и удалили папку components/navbar/
и main.html
).
В файле index.html заменим <div ui-view></div>
на следующий простой макет:
<!-- src/index.html -->
<div ui-view class="container">
<div class="col-xs-2">
<h2>Деньги</h2>
<h3 class="money-ok">
500.0
</h3>
<ul class="nav nav-pills nav-stacked">
<li>
<a href="#"><i class="glyphicon glyphicon-th-list"></i> Транзакции</a>
</li>
<li>
<a href="#"><i class="glyphicon glyphicon-tasks"></i> Настройки</a>
</li>
</ul>
</div>
<div class="col-xs-6">
<h2>Транзакции</h2>
<br>
</div>
</div>
Обратите внимание: мы ещё не потратили ни секунды на ручное подключение Boostrap, но зато сразу перешли к его использованию. Многие рутинные процессы разработки автоматизированы: нам нужно только сверстать сам макет. О подключении файлов позаботится Gulp, а о красивом внешнем виде – Bootstrap.
Теперь нужно определить этот контроллер на каком-нибудь html-элементе в нашем приложении, чтобы внутри него был доступ к этому контроллеру и мы могли использовать данные из контроллера внутри html.
Лучше всего для этого подходит центральный блок с транзакциями. Определим контроллер на родительском блоке при помощи директивы ng-controller
:
<!-- src/index.html -->
<div class="col-xs-6" ng-controller="TransactionsCtrl as transactions_ctrl">
<h2>Транзакции</h2>
<br>
</div>
Директивы в Angular.js отвечают за добавление новой функциональности html-разметке. Именно через директивы идёт связь вашего js-кода с интерфейсом. ng-controller
– одна из самых простых и важных директив. Она связывает часть html кода с конкретным контроллером, делает доступным $scope
этого контроллера внутри этого html элемента.
Что такое $scope?
$scope
– это объект, через который мы можем получить в html коде доступ к данным из модели или контроллера. В Angular.js реализована сложная система обновления и наследования скоупов. Самым главным является $rootScope
, который определяется на всём приложении и доступен из любой части кода. Контроллеры создают свой $scope
, который доступен внутри html-элемента, на котором этот контроллер определён.
Начиная с версии 1.2, в Angular.js появился новый синтаксис для доступа к контроллеру из html: указав as transactions_ctrl
на $scope
этого элемента определяется новый объект, соответствующий контроллеру. Всё, что мы определим в TransactionsCtrl
контроллере через this
будет доступно через transactions_ctrl
. Таким образом решается проблема вложенных $scope
– мы всегда знаем с каким именно скоупом мы работаем.
Посмотрим это поведение на примере. Определим для контроллера аттрибут transactions следующим образом:
//src/app/main/transactions.controller.js
angular.module('ngmkdev').controller('TransactionsCtrl', function($scope) {
this.transactions = [
{ amount: 500.00, date: "08/08/2014", description: "Подписка на журнал" },
{ amount: 150.00, date: "07/08/2015", description: "Кокаин" }
]
});
И выведем транзакции при помощи ещё одной директивы – ng-repeat
, ответственной за вывод коллекций. Её синтаксис говорит сам за себя.
<!-- src/index.html -->
<div class="col-xs-6" ng-controller="TransactionsCtrl as transactions_ctrl">
<h2>Транзакции</h2>
<br>
<table class="table table-striped">
<thead>
<tr>
<th>Сумма</th>
<th>Дата</th>
<th>Описание</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="transaction in transactions_ctrl.transactions">
<td>{{transaction.amount}}</td>
<td>{{transaction.date}}</td>
<td>{{transaction.description}}</td>
<td></td>
</tr>
</tbody>
</table>
</div>
Чтобы вывести что-нибудь из контроллера на экран пользователю используются фигурные скобки. Angular.js не использует существующие шаблонизаторы вроде Handlebars или Mustache. Вместо этого прямо в фреймворк встроен свой шаблонизатор.
Теперь в браузере вы должны увидеть таблицу из двух транзакций:
Но нам, конечно, нужно больше – нам необходим способ добавления новых транзакций. Для этого сверстаем простенькую форму, вставим её сразу после заголовка <h2>Транзакции</h2>
:
<!-- src/index.html -->
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">$</div>
<input class="form-control" type="text" placeholder="Сумма">
</div>
</div>
<div class="form-group">
<label class="sr-only"></label>
<input type="text" class="form-control" placeholder="Описание">
</div>
<button type="submit" class="btn btn-default">Добавить</button>
</form>
Добавим заготовку для новой транзакции в контроллер:
// src/app/main/transactions.controller.js
// ...
this.resetTransaction = function() {
this.newTransaction = {
amount: 0.0,
date: "01/02/1993",
description: null
}
}
this.resetTransaction();
// …
Сразу после инициалиации контроллера текущая транзакция будет равна заготовке из функции resetTransaction()
.
Теперь необходимо связать форму с объектом newTransaction
. Для этого мы будем использовать директиву ng-model
, которая связывает элемент формы с каким-нибудь атрибутом контроллера. Изменённые инпуты примут вид:
<!-- src/index.html -->
<!-- ... -->
<input class="form-control"
type="text"
placeholder="Сумма"
ng-model="transactions_ctrl.newTransaction.amount">
<!-- ... -->
<input type="text"
class="form-control"
placeholder="Описание"
ng-model="transactions_ctrl.newTransaction.description">
<!-- ... -->
Остаётся теперь добавить обработку отправки формы. К счастью, и для этого в Angular.js есть встроенная директива ng-submit
. Форма принимает вид:
<!-- src/index.html -->
<form class="form-inline" ng-submit="transactions_ctrl.addTransaction()">
<!-- ... -->
</form>
Добавим метод addTransaction()
в TransactionsCtrl
:
// src/app/main/transactions.controller.js
// ...
this.addTransaction = function() {
this.transactions.push(this.newTransaction);
this.resetTransaction();
}
// ...
Всё готово! Открываем браузер, заполняем форму, жмём энтер или кнопку на форме и наша таблица автоматически обновляется: новая транзакция появляется внизу таблицы. Обратите внимание: мы нигде не писали код обновления таблицы. Angular.js сам понимает, что при изменении массива transactions нужно обновить соответствующий этому массиву html-код. Это одна из самых классных фич Angular.js – автоматическое обновление и двусторонняя связь между html-кодом и кодом приложения.
Теперь дорогому читателю предлагается перейти от чтения туториала к конкретной практике. Вот некоторые задачи, которые вы можете решить самостоятельно в пределах этого учебного приложения:
- Избавьтесь от указанных прямо в коде транзакций.
- Отсортировать транзакции по убыванию даты транзакции – сверху самые последние, чем ниже тем старее.
Форкните репозиторий с приложением и реализуйте эти задачи. Весь код этой статьи находится в коммите 12aacf6 В следующей статье мы научимся использовать сервисные объекты и узнаем про dependency injection в Angular.js.