Продвинутый
ngular JS
Современный веб
Три слона:
- HTML
- CSS
- JS
HTML
HTML – декларативный язык разметки документов.
HTML DOM – объектная модель документа
<html>
<head>
<meta>
<meta>
<title>Test title</title>
<link ...>
</head>
<body>
<div>
<img>
<h1></h1>
<p></p>
<div></div>
</div>
<script>
</script>
</body>
</html>
Список дел
<!DOCTYPE html>
<html>
<head>
<title>Список дел</title>
</head>
<body>
<div>
<header>Список дел на день</header>
<ul>
<li>Составить список дел на день</li>
<li>Выполнить первый пункт списка</li>
<li>Осознать, что два пункта уже выполнено</li>
<li>Отдохнуть</li>
</ul>
</div>
</body>
</html>
CSS
CSS – декларативный язык описания внешнего вида.
.articles-tree-block {
height: 100%;
overflow: hidden;
width: 30%;
float: left;
padding-left: 10px;
padding-top: 20px;
padding-bottom: 10px;
position: relative;
}
Добавим немного CSS
@CHARSET "UTF-8";
* { box-sizing: border-box; }
body {
text-align: center;
font-size: 16px;
background-color: #f5f5f5;
}
.todo-list {
margin-top: 30px;
display: inline-block;
}
.todo-list>header {
text-transform: uppercase;
font-weight: bold;
}
ul {
text-align: left;
list-style: none;
padding: 0px;
}
li {
border: 1px solid grey;
background-color: #FFFFFF;
padding: 5px;
margin: 4px;
}
li.checked {
text-decoration: line-through;
color: #b9b9b9;
}
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li class="checked">Составить список дел на день</li>
<li class="checked">Выполнить первый пункт списка</li>
<li>Осознать, что два пункта уже выполнено</li>
<li>Отдохнуть</li>
</ul>
</div>
JavaScript
Простой скриптовый язык для добавления динамики на страницы
function myFunction() {
var x, text;
// Get the value of the input field with id="numb"
x = document.getElementById("numb").value;
// If x is Not a Number or less than one or greater than 10
if (isNaN(x) || x < 1 || x > 10) {
text = "Input not valid";
} else {
text = "Input OK";
}
document.getElementById("demo").innerHTML = text;
}
Добавим немного JS
<body onload="init()">
<div class="todo-list">
<header>Список дел на день</header>
<ul id="tasks">
</ul>
</div>
</body>
<script src="app.js"></script>
var tasks = [{
text: 'Составить список дел на день',
checked: true
}, {
text: 'Выполнить первый пункт списка',
checked: true
}, {
text: 'Осознать, что два пункта уже выполнено'
}, {
text: 'Отдохнуть'
}];
function init() {
var list = document.getElementById('tasks');
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
var elem = document.createElement('li');
elem.textContent = task.text;
if (task.checked) {
elem.className = 'checked';
}
list.appendChild(elem);
}
}
Angular JS
JS-фреймворк, добавляющий дополнительные элементы в HTML, целиком и полностью ориентирован на выполнение на стороне клиента. Предназначен для создания одностраничных веб-приложений.
Особенности:
- Привязки данных
- Директивы – швейцарский нож для управления DOM
- Поддержка форм и их валидации
- Построение компонентой архитектуры
Основы ангуляра
-
Шаблоны – обычный HTML с дополнительной разметкой
-
Директивы – особые элементы разметки
-
Scope'ы – контекст, связывающий шаблон и контроллер
-
Контроллеры – js-функции, управляющие представлением
-
Сервисы – js-объекты, хранящие бизнес-логику
-
Представление – скомпилированный шаблон
-
Внедрение зависимостей – механизм связывания между собой сервисов, контроллеров и т. д.
-
Выражения – инструмент вставки данных из scope'а в шаблон
Что за?..
Всё очень просто.
Шаблон
Компилятор
Контроллер
Представление
Сервисы
Директивы
Выражения
Директивы
Специальный элемент ангуляра, предназначенный для манипуляций с DOM.
Некоторые встроенные директивы:
- ng-app
- ng-controller
- ng-repeat
<body ng-app="app" ng-controller="controller">
<ul>
<li ng-repeat="item in items">{{item}}</li>
</ul>
</body>
ng-app
Директива, определяющая область действия ангуляра. Все элементы-потомки связываются с соответствующим приложением ангуляра
<body ng-app="taskApp">
...
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
angular.module('taskApp', [])
После привязки в шаблонах можно использовать контроллеры, директивы и т. д. из приложения
ng-controller
Директива, привязывающая контроллер к элементу, создаёт общий scope.
Контроллер – js-функция, заполняющая модель данными и коллбэками
angular.module('taskApp', []).controller('taskController', controller)
function controller($scope) {
$scope.tasks = [{
text: 'Составить список дел на день',
checked: true
}, {
text: 'Выполнить первый пункт списка',
checked: true
}, {
text: 'Осознать, что два пункта уже выполнено'
}, {
text: 'Отдохнуть'
}];
}
<body ng-app="taskApp" ng-controller="taskController">
</body>
ng-repeat и выражения
Директива, которая создает новый элемент по шаблону для каждого элемента коллекции из модели
<body ng-app="taskApp" ng-controller="tasksController">
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li ng-repeat="task in tasks" ng-class="task.checked ? 'checked' : ''">
{{task.text}}
</li>
</ul>
</div>
</body>
Выражение – js-код внутри {{ }}, результат выполнения которого будет вставлен вместо выражения
Сервисы
Сервисы содержат бизнес-логику, не зависящую от представления, например, откуда берутся данные.
angular.module('taskApp', []).service('taskService', taskService)
.controller('tasksController', tasksController)
function tasksController($scope, taskService) {
$scope.tasks = taskService.getTasks();
}
function taskService() {
var tasks = [{
text: 'Составить список дел на день',
checked: true
}, {
text: 'Выполнить первый пункт списка',
checked: true
}, {
text: 'Осознать, что два пункта уже выполнено'
}, {
text: 'Отдохнуть'
}];
return {
getTasks: function() {
return tasks;
}
}
}
Привязка функций контроллера к элементам управления
Ряд директив для привязки событий интерфейса к функциям контроллера:
- ng-click
- ng-keypressed
- ng-changed
- ng-submit
- ng-change
Добавим отметку о выполнении задачи
<li ng-repeat="task in tasks" ng-class="{'checked': task.checked}">
<input type="checkbox" ng-checked="task.checked" ng-click="taskStateChanged(task)">
{{task.text}}
</li>
function tasksController($scope, taskService) {
$scope.taskStateChanged = taskStateChanged;
function taskStateChanged(task) {
taskService.taskStateChanged(task);
}
}
function taskService() {
return {
taskStateChanged: function(task) {
task.checked = !task.checked;
}
}
}
Добавим создание задачи
<form ng-submit="addTask()">
<input type="text" ng-model="newTaskText" placeholder="Добавить задачу">
</form>
angular.module('taskApp', [])
.service('taskService', taskService)
.controller('tasksController', tasksController)
function tasksController($scope, taskService) {
$scope.addTask = function() {
if ($scope.newTaskText) {
taskService.addTask($scope.newTaskText);
$scope.newTaskText = '';
}
}
}
function taskService() {
return {
addTask: function(text) {
var task = {
text: text,
checked: false
};
tasks.push(task);
}
}
}
Angular Routing
Ангуляр позволяет переключаться между различными представлениями на основе URL страницы
angular-route/#/
angular-route/#/1
$routeProvider
angular.module('taskApp', ['ngRoute'])
.config(routeConfig);
function routeConfig($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/taskList.html',
controller: 'tasksController'
})
.when('/:id', {
templateUrl: 'views/taskCard.html',
controller: 'taskController',
resolve: {
task: function($route, taskService) {
return taskService.getTask($route.current.params.id);
}
}
})
.otherwise({
redirectTo: '/'
});
}
Основные элементы роута:
- Шаблон
- Контроллер
- Resolve
ngRoute – модуль роутинга
Модификации html
1. Подключить библиотеку angular-route
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.js"></script>
2. Добавить директиву ng-view, которая показывает, в какой элемент будет вставлен шаблон текущего роута
<body ng-app="taskApp" ng-view>
</body>
3. Создать новые шаблоны
<h3>{{task.text}}</h3>
<div ng-if="task.checked" class="task-done">Выполнено!</div>
<div ng-if="!task.checked" class="task-undone">Не выполнено!</div>
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li ng-repeat="task in tasks" ng-class="task.checked ? 'checked' : ''">
<input type="checkbox" ng-checked="task.checked" ng-click="taskStateChanged(task)">
{{task.text}}
<button ng-click="openTask(task)">></button>
</li>
</ul>
<form ng-submit="addTask()">
<input type="text" ng-model="newTaskText" placeholder="Добавить задачу">
</form>
</div>
taskCard.html
taskList.html
Правим сервис и контроллеры
1. Добавляем в сервис задач метод получения задачи по id
getTask: function(id) {
for (var i = 0; i < tasks.length; i++) {
if (tasks[i].id == id) { return tasks[i]; }
}
return null;
}
2. Добавляем контроллер для представления задачи
angular.module('taskApp', ['ngRoute'])
.controller('taskController', taskController);
function taskController($scope, task) {
$scope.task = task;
}
Результат
Директивы
Их мощь может сравниться только с их сложностью.
Возможности:
- Можно использовать как элемент, атрибут, css-класс
- Можно прикреплять шаблон и контроллер
- Можно взаимодействовать с другими директивами
- Можно создавать изолированный scope
- Transclusion (что-то невменяемо крутое)
- Можно вмешиваться в процесс компиляции шаблона
- Можно манипулировать с DOM после компиляции шаблона
Объявление директивы
angular.module('taskApp')
.directive('tasks', taskDirective);
function taskDirective() {
return {
controller: tasksController,
templateUrl: 'views/taskList.html',
restrict: 'E'
}
}
directive – метод объявления директивы, принимает название и функцию, возвращающую объект определения директивы
Атрибуты директивы
- multiElement – для создания директив-интервалов
- priority – управляет порядком компиляции директив
- terminal – отмена компиляции менее приоритетных директив
- scope – параметры создания нового scope
- bindToController – для биндинга свойств scope к контроллеру
- controller – контроллер шаблона директивы
- require – директивы, необходимые для нашей директивы
- controllerAs – для указания псевдонима контроллера
- restrict – режимы использования директивы
- templateNamespace – тип разметки шаблона
- template – строка с шаблоном директивы
- templateUrl – URL шаблона директивы
- transclude – включение transclusion
- compile – функция для изменения процесса компиляции шаблона
- link – функция для обработки скомпилированного шаблона
restrict
restrict определяет, как можно будет использовать директиву в шаблонах.
Варианты использования:
- Как html-тэг – E
- Как html-аттрибут – A
- Как css-класс – C
- Внутри комментария – M
restrict: 'EAC' // использовать и как тэг, и как атрибут, и как css-класс
restrict: 'E' // только как тэг
restrict: 'EA' // как тэг или как аттрибут
<!-- Как тэг -->
<bar-chart></bar-chart>
<!-- Как атрибут -->
<canvas bar-chart></canvas>
<!-- Как css-класс -->
<canvas class="bar-chart"></canvas>
<!-- Как комментарий -->
<!-- directive: bar-chart -->
<canvas></canvas>
template и templateUrl
angular.module('taskApp')
.directive('tasks', taskDirective);
function taskDirective() {
return {
templateUrl: 'views/taskList.html',
restrict: 'E'
}
}
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li ng-repeat="task in tasks">
{{task.text}}
</li>
</ul>
</div>
<tasks></tasks>
+
+
<tasks>
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li ng-repeat="task in tasks">
{{task.text}}
</li>
</ul>
</div>
</tasks>
=
Иерархия скоупов
controller
Контроллер директивы похож на обычный контроллер, но обладает рядом особенностей:
- обладает дополнительными локальными переменными
- может обрабатывать дополнительные события
- может использоваться для взаимодействия директив
angular.module('taskApp')
.directive('tasks', taskDirective);
function taskDirective() {
return {
controller: tasksController,
templateUrl: 'views/taskList.html',
restrict: 'E'
}
}
function tasksController($scope, taskService, $location) {
$scope.tasks = taskService.getTasks();
$scope.openTask = openTask;
$scope.addTask = addTask;
}
controllerAs
controllerAs – механизм, используемый для чёткого отделения свойств контроллера от остальных объектов scope
<div class="todo-list">
<header>Список дел на день</header>
<ul>
<li ng-repeat="task in ctrl.tasks">
{{task.text}}
<button ng-click="ctrl.openTask(task)">
</button>
</li>
</ul>
<form ng-submit="ctrl.addTask()">
<input type="text" ng-model="newTask">
</form>
</div>
angular.module('taskApp')
.directive('tasks', taskDirective);
function taskDirective() {
return {
controller: tasksController,
controllerAs: 'ctrl',
templateUrl: 'views/taskList.html',
restrict: 'E'
}
}
function tasksController(taskService, $location) {
var self = this;
self.tasks = taskService.getTasks();
self.addTask = addTask;
function addTask() {
if (self.newTask) {
var task = taskService.addTask(self.newTask);
self.tasks.push(task);
self.newTask = '';
}
}
}
Изолированный scope
Недостатки наследования scope:
- возможные коллизии имён
- неявное прокидывание данных
- нарушение инкапсуляции
Решение – изолированный scope директивы
Для активации – атрибут директивы scope
scope: {
tasks: '<',
openTask: '&',
addTask: '&'
}
Виды биндинга:
- @ – односторонний биндинг текста
- < – односторонний биндинг js-объекта
- & – биндинг callback-функции
- = – двусторонний биндинг
Пример изолированного scope
return {
controller: tasksController,
controllerAs: 'ctrl',
templateUrl: 'views/taskList.html',
restrict: 'E',
scope: {
tasks: '<',
openTask: '&',
addTask: '&'
},
bindToController: true
}
function tasksController() {
var self = this;
self.add = function() {
self.addTask({
text: self.newTask
});
self.newTask = null;
};
self.open = function(task) {
self.openTask({
task: task
});
};
}
.when('/', {
template: '<tasks tasks="ctrl.tasks" add-task="ctrl.add(text)"'
+ 'open-task="ctrl.open(task)"></tasks>',
controller: tasksViewCtrl,
controllerAs: 'ctrl'
})
function tasksViewCtrl(taskService, $location) {
var self = this;
self.tasks = taskService.getTasks();
self.add = function(text) {
// adding task
};
self.open = function(task) {
$location.path(task.id);
}
}
link
link – функция для выполнения операций после компиляции шаблона.
Обычно используется для модификации DOM и привязки функций к событиям DOM-элементов
var cm;
function link(scope, elem, attrs, controller) {
var cmConfig = {
mode: attrs.mode || 'text/plain',
theme: 'dracula',
lineWrapping: true,
lineNumbers: true,
autofocus: true
};
cm = CodeMirror(elem[0].children[0].children[1], cmConfig);
cm.on("change", digest);
controller.cm = cm;
controller.digest = digest;
function digest() {
if (!$rootScope.$$phase) {
scope.$digest();
}
}
}
Директивы?
Компоненты!
Введены: Angular 1.5
Цель: упростить разработку UI-компонентов
Способ: ввести новое API, в которое добавить
необходимый минимум из API директив
Особенности: изолированный scope
автоматический controllerAs
bindToController всегда равен true
может быть только элементом
Объявление
.component('tasks', {
controller: tasksController,
controllerAs: 'ctrl',
templateUrl: 'views/taskList.html',
bindings: {
tasks: '<',
openTask: '&',
addTask: '&'
}
});
Метод component, принимающий название компонента и объект определения компонента
Атрибуты
- controller – контроллер шаблона
- controllerAs – псевдоним контроллера, по умолчанию $ctrl
- template – строка с шаблоном
- templateUrl – URL шаблона
- bindings – внешние зависимости компонента
- require – требуемые директивы
- transclude – Transclusion (и тут!)
Директивы или компоненты?
Директивы – для операций с DOM
Компоненты – для связи шаблона с контроллером
Что же в результате?
Небольшой компонент, представляющий собой список задач. Вся логика по получению и изменению задач вынесена наружу. Компонент легко переносится между разными приложениями.
angular.module('taskApp')
.component('tasks', {
controller: tasksController,
controllerAs: 'ctrl',
templateUrl: 'views/taskList.html',
bindings: {
tasks: '<',
openTask: '&',
addTask: '&'
}
});
function tasksController() {
var self = this;
self.add = function() {
self.addTask({
text: self.newTask
});
self.newTask = null;
};
self.open = function(task) {
self.openTask({
task: task
});
};
}
Экстра
Несколько фич ангуляра, которые могут помочь при разработке
Отслеживание изменений
$scope.$watch(obj, callback)
Ангуляр отслеживает изменение переданного объекта
function ctrl($scope) {
var self = this;
$scope.$watch(getOriginData, dataChanged);
function getOriginData() {
return self.data;
}
function dataChanged() {
self.myData = filter(data);
}
}
Использование геттеров и сеттеров в контроллерах с механизмом controllerAs
function Ctrl() {
}
Ctrl.prototype = {
set data(data) {
this.myData = this.filter(data);
}
}
Два типа отношений scope'ов
Родитель-потомок:
Иерархия scope'ов, образуемая с помощью свойств parent и children scope'ов
Прототипное наследование:
каждый scope в качестве прототипа имеет своего родителя
Обычные scope'ы – оба типа отношений
Изолированные scope'ы – только отношения родитель-потомок
Система событий ангуляра
Методы:
- $broadcast – отправить сообщение всем дочерним scope'ам
- $emit – отправить сообщение всем родительским scope'ам
- $on – подписывает на конкретное сообщение
$emit
$broadcast
$http
Сервис ангуляра по выполнению AJAX-запросов
Каждый метод возвращает Promise
Методы:
- $http.get
- $http.head
- $http.post
- $http.put
- $http.delete
- $http.jsonp
- $http.patch
angular.module('taskApp')
.service('taskService', taskService);
function taskService($http) {
this.getTasks = function() {
return $http.get('/rest/tasks/').then(returnData);
};
this.addTask = function(task) {
return $http.post('/rest/tasks/', task).then(returnData);
};
this.getTask = function(id) {
return $http.get('/rest/tasks/' + id).then(returnData);
};
this.updateTask = function(task) {
return $http.put('/rest/tasks/' + id, task).then(returnData);
};
function returnData(resposne) {
return response.data;
}
}
Слайды с мастер-класса
Слайды с мастер-класса
body (ng-app) : $rootScope
div (ng-view) : $scope
Свойства: tasks, changeState, open, add
li (ng-repeat) : $scope
Свойство: task
li (ng-repeat) : $scope
Свойство: task
li (ng-repeat) : $scope
Свойство: task
li (ng-repeat) : $scope
Свойство: task
li (ng-repeat) : $scope
Свойство: task
Спасибо за внимание!
Angular advanced
By walkingdev
Angular advanced
- 960