Angular 2.0.0-alpha для тих, хто не в силах чекати


Зовсім недавно(5-6 березня) пройшла конференція ng-conf, і багато доповідей на ній було присвячено майбутнього релізу Angular 2, на декількох з них навіть показали альфа версію. Звичайно ж після прослуховування дуже захотілося випробувати його особисто. Якщо вам теж не терпиться — прошу під кат.

У цій статті ми розберемося, де взяти білд альфа версії Angular 2.0, створимо на ньому невеликий додаток — To-do list, і запустимо його в браузері без повної підтримки ECMAScript 6.

Angular 2 дуже сильно відрізняється від нинішньої версії, переосмислене практично всі. Багато з відмінностей випливають з того, що він на всю котушку використовує ES6 з анотаціями та типами, розробники називають цю мову AtScript. Тепер же, судячи з усього, команда Angular початку щільно співпрацювати з Microsoft і розробка буде вестися на TypeScript, і після виходу його версії 1.5, туди буде повністю включений AtScript.

Додаток Angular 2 тепер складається з компонентів і представляє з себе їх дерево. Ідея схожа на Web-components, навіть розмітка Angular 2 компонентів поміщається в Shadow DOM. Причому якщо ви зберетеся використовувати у своєму додатку Web-components, або, наприклад, Polymer — то синтаксис нічим не буде відрізнятися від використання ваших власних Angular 2 компонентів. Самі компоненти представляють із себе ES6 класи з анотаціями, ніякого спеціального синтаксису як, наприклад, для директив у версії 1.х не потрібно. Сервіси тепер теж стали звичайними класами, а завдяки підтримці типізації в AtScript, инъектировать їх можна за типом, без використання синтаксису .$inject або ngAnnotate.

Давайте зараз спробуємо створити наше перше To-do додаток і по ходу справи розберемося, що та як.

На сьогодні жоден браузер не підтримує весь той функціонал, який потрібен Angular 2 для роботи, тому нам знадобиться ціла купа інструментів:

Щоб всім бажаючим помацати альфу не доводилося ставити це все окремо, розробники зібрали quick start. Його можна встановити набравши в папці проекту
git clone https://github.com/angular/quickstart.git 

Тепер все необхідне лежить у нас у папці quickstart, а для краси додамо ще angular-material, але використовувати з нього будемо, зрозуміло, тільки css
bower install angular-material 

Тепер створимо в проекті файл index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>To Do</title>
<script src="/quickstart/dist/es6-shim.js"></script>
<link href="bower_components/angular-material/angular-material.css" rel="stylesheet" />
</head>
<body layout="row">
<style>
md-progress-circular.md-default theme.blue .md-inner .md-right .md-half-circle {
border-right-color: #039be5;
}

md-progress-circular.md-default theme.blue .md-inner .md-left .md-half-circle, md-progress-circular.md-default theme .md-inner .md-right .md-half-circle {
border-top-color: #039be5;
}

md-progress-circular.md-default theme.blue .md-inner .md-left .md-half-circle {
border-left-color: #039be5;
}

md-progress-circular.md-default theme.blue .md-inner .md-gap {
border-top-color: #039be5;
border-bottom-color: #039be5;
}
</style>
<app flex layout="row" layout-fill>
<div layout-fill layout="row" layout-align="center center" layout-margin>
<md-progress-circular md-mode="indeterminate" class="md-default-theme blue" style="-webkit-transform: scale(1);">
<div class="md-spinner-wrapper">
<div class="md-inner">
<div class="md-gap">
</div>
<div class="md-left">
<div class="md-half-circle"></div>
</div>
<div class="md-right">
<div class="md-half-circle"></div>
</div>
</div>
</div>
</md-progress-circular>
<h4>
    To do app is loading...
</h4>
</div>
</app>
<script>
System.paths = {
'angular2/*': 'quickstart/angular2/*.js',
'rtts_assert/*': 'quickstart/rtts_assert/*.js',
'app/*': 'app/*.js'
};
System.import('app/App');
</script>
</body>
</html>


Тут слід звернути увагу на три моменти:
Насамперед ми підключили файл es6-shim, в ньому дбайливо зібрано все вищеперелічене, що нам потрібно для запуску програми.
Далі звернемо увагу на елемент app. Це і буде наш головний компонент програми. Зараз у ньому міститься просто розмітка зі спиннером і написом To do app is loading… вона буде показуватися поки ангуляр не завантажить клас компонента і не замінить цю розмітку на його template.
В самому низу ми використовували бібліотеку system.js вона реалізує систему завантаження модулів ES6. Спочатку за допомогою System.paths ми налаштували шляху до .js файлів, потім з допомогою System.import завантажили файл з компонентом app, а він, у свою чергу, вже й потягне за собою всі потрібні бібліотеки та компоненти.

Тепер створимо папку app і покладемо в неї файл App.js
import {Component, Template, bootstrap, Foreach} from 'angular2/angular2';
import {TodoStore} from 'app/TodoStore';
import {ItemEditor} from 'app/ItemEditor';

@Component({
selector: 'app',
componentServices: [
TodoStore
]
})
@Template({
url: 'app/todo.html',
directives: [Foreach, ItemEditor]
})
class App {
constructor(store:TodoStore) {
this.store=store;
}
}

bootstrap(App);


Спочатку ми імпортуємо класи Component, Шаблон, з Foreach angular2.js, звідти ж імпортуємо функцію bootstrap, вже знайому тим, хто у версії 1.x не використовують директиву ng-app, вона ініціалізує головний компонент програми, слідом ми імпортуємо класи з TodoStore app/todoStore.js (це буде сервіс, відповідальний за роботу зі списком завдань і його зберігання в localStorage браузера) і ItemEditor з app/item-editor.js (це буде компонент — директива, що відображає елемент списку). Ми їх поки не написали, тому розглянемо їх пізніше.
Далі ми створюємо клас App, йому в конструктор за допомогою ін'єктора передається об'єкт класу TodoStore, який ми присвоюємо властивості store.

При оголошенні класу ми використовували анотації Component та Template з angular2.js:
Component — анотація, яка і робить клас компонентом. Властивість selector — це селектор, що дозволяє виявити елемент в розмітці шаблону, у нас це елемент app. Властивість componentServices — це список класів для ін'єктора.
Template — допомагає вказати для компонента шаблон. Властивість url вказує шлях, де буде знаходитися шаблон, властивість directives вказує список директив, які використовуються в розмітці шаблону.
В кінці файлу ми використовуємо функцію bootstrap, щоб ініціалізувати наш головний компонент.

Давайте тепер створимо файл app/TodoStore.js
export class TodoStore {
constructor() {
this.load();
}
save() {
window.localStorage['todoItems']=JSON.stringify(this.items);
}
addItem(name, checked){
this.items.push(new Item(name,checked));
}
clear() {
this.items.length=0;
}
load() {
this.items=[];
let that=this;
let itemsStr=window.localStorage['todoItems'];
if(itemsStr) {
JSON.parse(itemsStr).forEach((e) => {
that.addItem(e.name, e.checked);
});
}
}
}


class Item {
constructor (name, checked){
this.name = name;
this.checked=checked || false;
}
toggleCheck() {
this.checked=!this.checked;
}
}


У класу TodoStore є методи save(), load(), clear(), і addItem(name, checked).
Сам список завдань буде зберігатися у властивості items. Метод save() зберігає значення властивості items в localStorage. Метод clear() очищає властивість items. Метод load() завантажує з localStorage всі елементи списку і додає їх в items з допомогою методу addItem. Метод addItem(name, checked) приймає ім'я пункту списку, створює новий об'єкт Item і додає його в Items.

Слідом йде оголошення класу Item, конструктор приймає name — ім'я пункту списку і checked — відмічений як виконаний. Ще у класу є метод toggleCheck(), який змінює значення checked на протилежне.

Далі створюємо файл app/todo.html з шаблоном головного компонента
<style>
@import "../bower_components/angular-material/angular-material.css";

.md-primary {
background-color: #039be5;
color: white;
}

md-toolbar {
background-color: #CFD8DC;
}

md-input-container {
padding-bottom: 0;
}
</style>
<div flex layout="column" layout-fill layout-margin>
<md-toolbar class="md-primary">
<h2 class="md-toolbar-tools">
<span>To do list</span>
</h2>
</md-toolbar>
<md-content layout="row" layout-align="center start" flex>
<div flex="50" flex-md="80" flex-sm="100">
<div layout="row" layout-margin flex>
<button flex class="md-button md-primary md-raised md-default-theme" (click)="store.save()">Save</button>
<button flex class="md-button md-primary md-raised md-default-theme" (click)="store.load()">Load</button>
<button flex class="md-button md-primary md-raised md-default-theme" (click)="store.clear()">Clear</button>
</div>
<div layout="row" layout-margin flex>
<md-input-container class="md-input-has-value" flex="60">
<input #newitem class="md-input" placeholder="New to do item" />
</md-input-container>
<button flex class="md-button md-primary md-raised md-default-theme" (click)="store.addItem(newitem.value)">Add new item</button>
</div>
<md-list>
<md-item *foreach="#item in store.items">
<md-item-content>
<item-editor [item]="item"></item-editor>
</md-item-content>
</md-item>
</md-list>
</div>
</md-content>
</div>

Тут у нас є три кнопки, які викликають методи сховища store.save(), store.load() і store.clear(). Ми прив'язуємося до події click кожної кнопки за допомогою атрибута (click). У нинішній версії ангуляра часом досить важко з першого погляду відрізнити в розмітці, який атрибут використовується для прив'язки до події, а який для прив'язки даних, тепер же все буде видно відразу, круглі дужки () потрібні, щоб реагувати на події елемента або компонента, а атрибут в квадратних дужках [] здійснює прив'язку до даних, мається на увазі, що значення атрибута — це вираз, який буде обчислено, а його результат буде присвоєно атрибуту елемента, якщо атрибут вказано взагалі без всяких лапок — то мається на увазі, що в ньому просто текстове значення.
Далі звернемо увагу на input з атрибутом #newitem таким чином ми тепер можемо задавати елементу ім'я, по якому він буде доступний, як у події click кнопки нижче, де ми беремо значення input'а — newitem.value і передаємо його в store.addItem().
Слідом йде список завдань, який ми вивели з допомогою нової директиви *foreach(це заміна ng-repeat), тут, як і в input'е вище, ми написали #item, щоб звертатися далі до кожного елементу списку по імені item.
Кожен елемент списку ми збираємося виводити за допомогою директиви item-editor, якої ми прив'язуємо елемент списку з допомогою атрибута [item].

Тепер створимо файл app/ItemEditor.js з компонентом ItemEditor, який буде відповідати за відображення елементів списку.
import {Component, Template} from 'angular2/angular2';

@Component({
selector: 'item-editor',
bind: {
'item': 'item'
}
})
@Template({
inline: `
<style>

@import "../bower_components/angular-material/angular-material.css";
md-checkbox.md-checked .md-icon {
background-color: #039be5;
color: white;
}
</style>
<md-checkbox [class.md-checked]="item.checked" (click)="item.toggleCheck()">
<div class="md-label">
<div class="md-container">
<div class="md-icon"></div>
</div>
<span>
{{item.name}}
</span>
</div>
</md-checkbox>
` 
})
export class ItemEditor {

}


Тут в @Component ми вказали селектор 'item-editor', це елемент, який використовується в *foreach у todo.html. Слідом зазначено властивість bind, воно вказує, що потрібно прив'язати значення атрибута item до властивості item класу компонента(щось на зразок bindToController).
В @Template на цей раз замість url для прикладу зазначено властивість inline, воно служить для вказівки тексту шаблону прямо в коді. Це знижує кількість необхідних http запитів, тому після виходу Angular 2 найімовірніше з'явиться якій-небудь плагін для Gulp, який буде змінювати url на inline, на заміну нинішньому gulp-angular-templatecache.

В самому шаблоні можна побачити атрибут [class.md-checked] — це лаконічна заміна ng-class. А нижче виводиться ім'я елемента списку за допомогою вже став рідним синтаксису з фігурними дужками {{item.name}}.

Ось і все, тепер можна запустити проект(перевіряв тільки в Хромі) і подивитися, як працює майбутній Angular 2.0.

Як ми бачимо, розробники повністю переосмислили концепцію Angular.js і нас тепер чекає абсолютно новий фреймворк з новим, більш лаконічним синтаксисом. Завдяки абсолютно новій архітектурі, створену з розрахунком на великі додатки, продуктивність рендеринга, головний бич версій 1.x, зростає в багато разів(наочне порівняння можна подивитися в кінці доповіді Дейва Сміта Angular + React = Speed ). Можливість використовувати типізацію відкриває перспективи розвитку IDE для просунутої підтримки доповнення коду та статичного аналізу(як мінімум, Microsoft заявляє підтримку Angular в Visual Studio одним з пріоритетів). А нововведення ES6 зроблять розробку ще швидше і приємніше.

Це практично і все, що мені вдалося дізнатися про Angular 2.0 на сьогодні, будемо далі стежити за новинами, а поки ось кілька цікавих посилань по темі:
Канал ng-conf на YouTube з масою цікавих доповідей
Офіційний сайт Angular 2.0
Проект Angular 2 на GitHub

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.