[Из песочницы] AngularJS – перевод курса от CoodSchool
Данная публикация является переводом оригинального курса CoodSchool с небольшими дополнениями, которые показались мне уместными в данном контексте. Публикация рассчитана на тех, кто только начинает знакомится с Angular.ВведениеAngularJS — популярная JavaScript библиотека, предназначенная, главным образом, для создания одностраничных веб-приложений с динамически обновляемым содержимым. Библиотека была написано словацким программистом Мишкой Хевери, который, работая в Google, получил от своего шефа задание выучить JavaScript. Он решил, что лучший способ изучить язык — это создать на его основе собственный фреймворк. Angular (звучит как «Ангула» и дословно переводится как «Угловатый») использует концепцию разделения данных и их представления, известную как MVC (M — model (данные), V — view (представление), С — controller (контролер, управляющая прослойка между ними обеспечивающая логику работы приложения).Разные источники описывают назначения контролера либо так, что контролер реагирует только на действия пользователя и вносит изменения в модель, а потом эти изменения проявляются в интерефейсе без его участия. Либо так, что контролер не только вносит изменения в модель, но и отвечает за их получение от модели вместе с последующей обработкой по необходимости. Angular придерживается второго концепта.Angular не зависит от внешyих библиотек и написан на чистом JavaScript (об этом часто упоминается как о плюсе). При этом, как правило, его рекомендуют использовать в связке с Bootstrap CSS. Так же Angula не ограничивает совместное использование с JQuery, например, для визуальных эффектов. В принципе, и любые друге JS библиотеки, нацеленные на представление (дизайн), например, React от команды Facebook, могут использовать совместно с Angular без особого конфликта между собой.
1. Знакомимся с основными сущностями фреймворка: модуль, контролер, директива, выражение Базовой структурой в Angular является модуль. По сути это контейнер для взаимосвязанных функций. Поэтому создание приложения на Angular начинается с создания базового модуля, в котором мы указываем имя приложения. Прежде всего создадим файл app.js, который будет содержать следующий код: var app = angular.module ('AppName', [ ]); AppName — имя нашего приложения. Оно, конечно, может быть любое другое на ваш вкус. В квадратных скобках [ ] указываются зависимости на другие модули, необходимые для работы нашего модуля. Если их нет, указываем просто пустой массив. Подробнее об этом позже.Контролер [Controler]Далее в модуле мы определяем контролер. Пока он один, но их может быть и много. Просто называем каждые другим именем.
app.controller ('StoreController', function (){ this.data = {name: 'Gem', // это те данные которые price: 3.49} // предоставляет контролер }); Теперь несколько важных действий внутри HTML кода (которые свяжут наш модуль с дизайном). Для этого создадим файл index.html, в котором прежде всего объявим, что мы используем модуль с выбранным нами именем. Теперь Angular знает, какой модуль использовать внутри данного HTML. Так же подключим саму библиотеку и файл с нашим приложением. Выражение [Expression]Внутри HTML в фигурных скобках {{}} указывается переменная. Эта переменная предварительно должна быть определена в контролере. Так же внутри {{}} мы можем указать не только переменную, но и выражение. Например {{'Цена: '+store.data.price+'$'}}.Обратите внимание, что выражения нельзя использовать внутри атрибута src тега IMG, поскольку браузер пытается загрузить изображение раньше, чем опеределен JS. Поэтому в Angular используется специальная директива ng-src:
Директива [Directive]Деректива специальный атрибут, который используется непосредственно внутри HTML тегов и обрабатывается Angular в соответствии с определенной для данной дерективы логикой. Например, внутри блока, где нам нужно отображать данные, мы указываем имя контролера (в данном случае StoreController), который будет нам эти самые данные предоставлять. Делается это при помощи встроенной дерективы ng-controller.
2. Фильтры, дерективы и чистый код Фильтры используются в выражениях и директивах и предназначены для изменения формата данных.{{ data | filter[: option1][: option2] }} е
date: {{'1388123412323' | date:'MM/dd/yyyy @ h: mma'}} // 12/27/2013 @ 12:50AM uppercase: {{'octagon gem' | uppercase}} // OCTAGON GEM limitTo {{'My Description' | limitTo:8}} // My Descr 
Теперь несколько слов о чистоте кода. Рассмотрим следующий пример:
{{product.description}}Description
app.controller («PanelController», function (){ // инициализация this.tab = 1;
// установка выбранного табулятора this.selectTab = function (setTab) { this.tab = setTab; };
// проверка если текущий табулятор выбран
this.isSelected = function (checkTab){
return this.tab === checkTab;
};
});
Логика теперь находится внутри контролера и осталось только соответственно изменить HTML:
{{product.description}}Description
Таким образом, данные с уровня представления (View) передаются на уровень хранения данных (Model) и обратно.
Так же нужно отметить, что при использовании дерективы ng-model происходит валидация данных, установка специальных классов по итогам валидации и множество других полезных событий.
Теперь свяжем форму с контролером, который обеспечит её обработку. Для начала создадим сам контролер, содержащий метод addReview, который должен быть вызван при отправке формы.
app.controller («ReviewController», function (){ this.review = {}; // объект для данных формы this.addReview = function (product) { product.reviews.push (this.review); // добавим данные из формы в массив результатов this.review = {}; // очистим форму }; }); После чего внесем необходимые коррективы в форму.
Теперь добавим валидацию. Прежде всего отметим поля формы как обязательные при помощи аргумента required, а так же добавим условие, чтобы данные сохранялись только для валидной формы. Для этого в тег Form добавим опцию novalidate, которая выключит дефолтную HTML валидацию и добавим условие reviewForm.$valid, благодаря которому метод addReview будет вызываться только у валидной формы.
Все поля определенного типа, указанного при помощи аттрибута type, будут проверятся на предмет корректного заполнения. При этом Angular будет добавлять к полю определенный CSS класс в зависимости от состояния валидации.
Так, например, для типа email установленные классы будут меняться следующим образом:
.ng-pristine .ng-invalid // начальное состояние (чисто, невалидно).ng-dirty .ng-invalid // невалидный е-мейл (заполнено, невалидно).ng-dirty .ng-valid // валидный е-мейл (заполнено, валидно)
Соответственно мы можем установить для данных классов любые подходящие нам CSS правила. Так, например, вокруг некорректно заполненного поля будет красная рамка, а вокруг корректно заполненного зеленая.
.ng-dirty.ng-invalid { border-color: #FA787E; // красная рамка } .ng-dirty.ng-valid { border-color: #78FA89; // зеленая рамка } В настоящий момент для поля Input поддерживаются следующие валидаторы:
time date datetime-local week month number text email (соответствие минимальным требованиям) url (соответствие минимальным требованиям) checkbox radio 4. Создание собственных деректив Одной из великолепных особенностей Angular является возможность создания собственных деректив. Другими словами, можно создавать собственные HTML-теги, которые могут иметь собственную логику и заменять собой целые куски HTML. Можно, например, создать тег , вставив который мы получим на странице редактор текста.Это позволяет писать выразительный код, который полностью передает структуру нашего приложения. И конечно, это позволяет сильно улучшить повторное использование кода.
В принципе, что касается повторного использования фрагментов HTML кода, Angular имеет в своем распоряжении директиву ng-include, которая позволяет вынести часть кода в отдельный файл. Выглядит это следующим образом:
Обратите внимание на одиночные кавычки вокруг имени. Они указывают Angular, что это строка с именем, а не переменная, содержащая имя файла.
Соответственно в файле product-title.html будет находится код, который Angular вставит как содержимое тега H3. Например:
{{product.name}} ${{product.price}} Как видно, вставляемый таким образом код может содержать выражения, которые будут обработаны после вставки такого фрагмента в общий код.
Но вернемся обратно к созданию собственных деректив. Делается это следующим образом:
app.directive ('productTitle', function (){
return {
restrict: 'E', // E — означает что деректива предназначена для Element
templateUrl: 'product-title.html'
};
});
Теперь в мы можем использовать это как HTML тег. Обратите внимание, что имя директивы productTitle в HTML превратится в product-title.
В случае, когда речь идет о виджете, нам не обойтись без контролера, который будет содержать всю необходимую логику. Ангулар позволяет реализовать это несколькими способами. Прежде всего можно просто добавить аттрибут ng-controller:
Но более правильно будет использовать свойство controller внутри функции, создающей нашу директиву:
app.directive ('productPanels', function (){ return { restrict: 'E', templateUrl: 'product-panels.html', controller: function (){ … // логика }, controllerAs: 'panels' // название контролера }; }); 5. Зависимости и Сервисы (Dependencies and Services) Зависимости это модули которые необходимы для обеспечения функциональности создаваемого нами нового модуля. Их так же можно рассматривать как способ улучшить структуру приложения, распределив самостоятельные части логики между различными модулями. Рассмотрим следующий пример: // файл app.js (function (){ var app = angular.module ('store', []); app.controller ('StoreController', function (){ . . . }); app.directive ('productTitle', function (){ . . . }); app.directive ('productGallery', function (){ . . . }); app.directive ('productPanels', function (){ . . . }); })(); Логично было бы вынести директивы в самостоятельный модуль или даже для каждой создать свой собственный модуль, в зависимости от необходимой гибкости.
Но в данном случае, чтобы избыточно не усложнять структуру приложения, вынесем их общий модуль.
// файл products.js (function (){ var app = angular.module ('store-products', []); app.directive ('productTitle', function (){ . . . }); app.directive ('productGallery', function (){ . . . }); app.directive ('productPanels', function (){ . . . }); })(); Теперь укажем модуль store-products как зависимость для модуля store: // файл app.js (function (){ var app = angular.module ('store', [store-products]); app.controller ('StoreController', function (){ . . . }); })(); Последним шагом нужно не забыть подключить наш новый файл: // файл index.html
. . . . . . В итоге мы разделили модуль на основе их функциональности.app.js — содержит топ-левел модуль подключаемый при помощи директивы ng-app; products.js — содержит всю функциональность для продуктов и только продуктов.
Так же в качестве зависимостей мы можем указывать встроенные в Ангулар сервисы, которые предоставляют различный стандартный функционал. Имена встроенных сервисов начинаются с $.
В качестве примера наиболее популярных сервисов можно привести следующие:
$http — коммуникация со сервером посредством XMLHttpRequest;$log — логирование сообщений в консоль браузера;$filter — фильтрация данных в массиве; … полный список.
Пример включения зависимостией (dependency injection) в контролер.
app.controller ('SomeController', [ '$http', '$log', function ($http, $log){ var store = this; store.products = []; $http ({ method: 'GET', url: '/products.json' }).success (function (data, status, headers, config) { $log.info (status); store.products = data; }) } ]);