С чего начать изучение AngularJS

angularjs_logo

Этот пост предназначен для тех, кто только начинает знакомиться с javascript фреймворком AngularJs.

UPD: Пост обновлен 25.10.2015 и является актуальным для Angular1.4.

Обычно список литературы дается в конце, но в данном случае разумно было бы предоставить все полезные ссылки сразу же.

Итак, что полезного есть на официальном сайте:

Статьи на хабре(полистать, а потом вернуться полистать еще раз):

Готовый скелет(структура с файлами, директориями и веб-сервером), с которого можно начинать создания своего приложения берем тут: github( либо zip).

Преимущества использования AngularJS(от сюда):

  • Dependency injection в стандартной поставке
  • Возможности модульного тестирования в стандартной поставке
  • e2e тесты позволяют легко мокать запросы
  • Декларативность(использование HTML атрибутов по максимуму)
  • Отличное open source сообщество
  • Дружелюбность к REST
  • Scopes, bindings и watches

Немного видео(на английском):

Крайне рекомендую все ролики о AngularJS от egghead, очень хорошее инвестирование времени. К ним вы сможете еще не раз возвращаться и пересматривать.

Вспомогательные инструменты на данный момент по прежнему находятся в состоянии зародыша(либо выкидыша) но взглянуть все равно стоит:

Быстрый старт

Давайте теперь вместе проделаем шаги из “быстрого старта”.

Убеждаемся в том что у нас стоит:

Выкачиваем себе тренинг проект:

[shell gutter=”false”]
$ git clone –depth=14 https://github.com/angular/angular-phonecat.git
[/shell]

Мы используем специальный флаг –depth==14, чтобы получить только последние 14 коммитов.

Перейдем в директорию проекта:

[shell gutter=”false”]
$ cd angular-phonecat
[/shell]

Самонастройка(Bootstrapping)

В начале каждого пункта будет дана команда, используя которую можно откатиться до состояния кода конкретного пункта.

[shell gutter=”false”]
$ git checkout -f step-0
[/shell]

Запустим наш web-сервер:

[shell gutter=”false”]
$ npm start
[/shell]

Если все отработало без ошибок – можем переходить на  http://localhost:8000/app/index.html

Открываем файл app/index.html:

<!doctype html>
<html lang="en" ng-app>
<head>
 <meta charset="utf-8">
 <title>My HTML File</title>
 <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
 <link rel="stylesheet" href="css/app.css">
 <script src="bower_components/angular/angular.js"></script>
</head>
<body>
 <p>Nothing here {{'yet' + '!'}}</p>
</body>
</html>

Вот она наша первая песочница для AngularJs. Важные моменты, которые мы должны вынести из этого кусочка HTML:

<html lang="en" ng-app>

– специальный ng-app атрибут, указывающий на то, какой элемент в DOM будет корневым для приложения(Т.е. мы можем сказать, что AngularJs будет использовать весь документ, либо какую-то конкретную его часть).

Nothing here {{'yet' + '!'}}`

Двойная фигурная кавычка используется для шаблонов: внутри можно указать переменную, выражение(подробнее в части о шаблонах).

Нам предлагают поиграться и заменить эту строчку на что-то такое:

 <p>1 + 2 = {{ 1 + 2 }}</p>

Теперь проясним что происходит при инициализации AngularJs, которая происходит автоматически по умолчанию:

  1. создается специальный объект Инжектор($injector), по средствам которого получаются все остальные сущности
  2. Инжектор создает $rootScope, что является контекстом для модели нашего приложения
  3. фреймворк “компилирует” DOM начиная с ngApp используя при этом rootScope.

После того как приложение выполнило этап самонастройки оно ждет событий от браузера, по которым, в случае необходимости, будут реагировать модели.

MVC

[shell gutter=”false”]
$ git checkout -f step-2
[/shell]

Представление (View)

В AngularJS view компонент представляет из себя отображение модели через HTML шаблон. Это означает, что при изменение модели, представление тоже будет меняться.

Зайдем в app/index.htm и посмотрим на содержание body:

<li ng-repeat="phone in phones">
  <span>{{phone.name}}</span>
  <p>{{phone.snippet}}</p>
</li>

phones – переменная(массив объектов), которую мы задали в контроллере(о нем позже)

ng-repeat – директива, которая запускает перебор коллекции(аналог forEach)

{{phone}} – элемент коллекции

{{phone.name}}, {{phone.snippet}} – свойства элемента коллекции

Т.е. другими словами: сколько  элементов будет в массиве phones – столько <li> выведет view.

Поэкспериментируем с шаблоном и выведем что-то такое:

 <p>Total number of phones: {{phones.length}}</p>

или такое(так тоже можно использовать директиву ng-repeat):

<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>

Контроллер (Controller)

Еще раз посмотрим на app/index.htm и обратим внимание на атрибут тега body:

<body ng-controller="PhoneListCtrl">

– в директиве ng-controller мы указываем, какой контролер должен обрабатывать данную область.

PhoneListCtrl  – это имя метода, который мы можем найти тут app/js/controllers.js:

[javascript]
function PhoneListCtrl($scope) {
$scope.phones = [
{"name": "Nexus S",
"snippet": "Fast just got faster with Nexus S."},
{"name": "Motorola XOOM™ with Wi-Fi",
"snippet": "The Next, Next Generation tablet."},
{"name": "MOTOROLA XOOM™",
"snippet": "The Next, Next Generation tablet."}
];
}
[/javascript]

В нашем случае мы просто заносим в $scope коллекцию объектов, чтобы потом ее отобразить во вью.

$scope, $rootScope

В AngularJS $scope и $rootScope – это ключевые сущности для передачи данных между моделью и отображением посредством контроллера. При этом $rootScope – это корневой scope, к которому имеют доступ все, а  $scope – scope определенного контроллера. Т.е. в нашем примере мы занесли данные только в scope нашего контроллера(PhoneListCtrl), а если бы мы хотели, чтобы другие контроллеры тоже получили доступ, то нам необходимо было использовать $rootScope. Более подробно можно почитать тут. ( также в тему: Передача данных между сущностями AngularJS )

Теперь, когда мы знаем что такое Скоуп можно поэкспериментировать и с ним записать другие данные и попытаться отобразить их в представлении:

$scope.hello = "Hello, World!"
$rootScope.hello = "Hello, Universe!"

!Внимание. Чтобы использовать $rootScope его также нужно добавить как зависимость в контроллере:

function PhoneListCtrl($scope, $rootScope)

Unit-тесты в AngularJS

Посмотрим пример теста в test/unit/controllersSpec.js:

[javascript]
describe(‘PhoneCat controllers’, function() {

describe(‘PhoneListCtrl’, function(){

it(‘should create "phones" model with 3 phones’, function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);

expect(scope.phones.length).toBe(3);
});
});
});
[/javascript]

Кто разбирался с Jasmine, нет ничего особо сложного: в данном тесте мы проверяем контроллер и хотим убедиться что он занес в коллекцию 3 записи.

Запуск тестов используя karma.

Для запуска тестов выполняем команду:

$ npm test

И после этого у нас должен запуститься тест-сервер и открыться 2 браузера(Chrome и Firefox). В двух браузерах будут выполнены тестовые манипуляции, после чего вы должны увидеть в у себя в консоле примерно следующее:

Chrome 46.0.2490 (Mac OS X 10.11.0): Executed 1 of 1 SUCCESS (0.045 secs / 0.039 secs)
Firefox 41.0.0 (Mac OS X 10.11.0): Executed 1 of 1 SUCCESS (0.033 secs / 0.031 secs)
TOTAL: 2 SUCCESS

Теперь можете попробовать что-то изменить в коде. Заметили? Тесты были запущены заново, т.е. после каждого изменения karma будет выполнять тесты. Файлы, за изменением которых нужно наблюдать, можно выставить в конфиге( test/karma.conf.js)

Продолжение следует… 

UPD: AngularJs Basics – прекрасно структурированный сайт документации

все части рубрики “С чего начать изучение AngularJS”: