Разработка веб-клиента
на 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);
}
}
}
$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;
}
Результат
$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;
}
}
Компоненты
Введены: Angular 1.5
Цель: упростить разработку UI-компонентов
Способ: ввести новое API, в которое добавить
необходимый минимум из API директив
Особенности: изолированный scope
автоматический controllerAs
биндинг свойств scope'а к объекту контроллера
может быть только элементом
Объявление
.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 (и тут!)
ControllerAs
Контроллер
$scope
tasks
addTask
Контроллер
$scope
tasks
addTask
Объект контроллера
<ul>
<li ng-repeat="task in tasks">
{{task.text}}
</li>
</ul>
<form ng-submit="addTask()">
</form>
<ul>
<li ng-repeat="task in ctrl.tasks">
{{task.text}}
</li>
</ul>
<form ng-submit="ctrl.addTask()">
</form>
ctrl
Компонент списка задач
Небольшой компонент, представляющий собой список задач. Вся логика по получению и изменению задач вынесена наружу. Компонент легко переносится между разными приложениями.
angular.module('taskApp')
.component('tasks', {
controller: tasksController,
controllerAs: 'ctrl',
templateUrl: 'views/taskList.html',
bindings: {
tasks: '<',
add: '&',
stateChanged: '&'
}
});
function tasksController() {
var self = this;
self.addTask = function() {
self.add({
text: self.newTaskText
});
self.newTaskText = null;
};
self.taskStateChanged = function(task) {
self.stateChanged({
task: task
});
}
}
<body ng-app="taskApp"
ng-controller="tasksViewCtrl as ctrl">
<tasks tasks="ctrl.tasks" add="ctrl.add(text)"
state-changed="ctrl.stateChanged(task)">
</tasks>
</body>
Экстра
Несколько фич ангуляра, которые могут помочь при разработке
Два типа отношений scope'ов
Родитель-потомок:
Иерархия scope'ов, образуемая с помощью свойств parent и children scope'ов
Прототипное наследование:
каждый scope в качестве прототипа имеет своего родителя
Обычные scope'ы – оба типа отношений
Изолированные scope'ы – только отношения родитель-потомок
Система событий ангуляра
Методы:
- $broadcast – отправить сообщение всем дочерним scope'ам
- $emit – отправить сообщение всем родительским scope'ам
- $on – подписывает на конкретное сообщение
$emit
$broadcast
Спасибо за внимание!
Java-Script Base
By walkingdev
Java-Script Base
- 1,085