Що нас чекає в Angular 2.0

Одним з найбільш очікуваних подій 2015 року, для фронт-енд розробників, крім виходу фінальної специфікації ES6, є поява нової версії одного з найпопулярніших фреймворків, AngularJS. Анонсовані зміни, настільки значні, що існує думка про те, що це по суті новий фреймворк написаний з нуля.

У зв'язку з цим я дозволю собі представити вам переклад великої статті “All About Angular 2.0", одного з розробників фреймворку Роба Ейзенберга, в якій він розкриває що чекає нас в наступній версії.

Всі, кому це може бути цікавим, ласкаво просимо під кат. І запасіться часом… :-)


Передумови для виникнення Angular 2.0
AtScript
Впровадження залежності (Dependency Injection, DI)
Шаблонизация і зв'язування даних (Templating and Databinding)
Маршрутизатор (The Router)

Преамбула
Хочете зрозуміти як стратегія буде реалізована в Angular 2.0? Ви в потрібному місці. У наступній статті я поясню основні можливості Angular 2.0 включаючи мотивацію яка стоїть за цими змінами. У міру читання, я постараюся розкрити свою точку зору і ідеї про процес створення Angular 2.0, включаючи важливі галузі в яких, як я думаю, все ще необхідні поліпшення.

Angular 1.3
Перш ніж почати говорити про майбутнє AngularJS, давайте на хвилинку подивимося на його поточну версію. Angular 1.3 є кращою версією AngularJS доступною на даний момент. І вона була випущена в реліз зовсім недавно. З безліччю багфіксів, розширенням можливостей і збільшенням продуктивності. Це один з сильних і зрілих фреймворків доступних сьогодні.

Але у вас ймовірно є безліч питань про майбутнє AngularJS. Коли вийде версія 2.0? Що станеться з 1.х? Чи можливо міграція з 1.х на 2.0?

Команда Angular працює набагато краще відповідаючи на подібні питання і ви можете допомогти їм у цьому.

Можу вам сказати що більше 1600 проектів в Google були побудовані з використанням Angular. Як ви можете бачити Google багато інвестував у поточну версію і в будь-якому разі буде змушений підтримувати її. Відповідаючи на питання на ngEurope Brad Green сказав що після виходу релізу Angular 2.0 можна очікувати що Angular 1.3 буде підтримуватися ще протягом 1.5 — 2 років. Ми так само зробили відповідні зміни в команді і керівництві, спрямовані на підтримку Angular 1.3. І тому навіть незважаючи на те, що ми інтенсивно працюємо над Angular 2.0, залишається віддана команда Angular 1.3. Цією командою керує Pete Bacon Darwin, якого, я впевнений ви знаєте за його значному внеску в AngularJS. Я б хотів надихнути вас на звернення до керівників проекту, з проханням про більш значних дій в цьому напрямку, включаючи офіційну підтримку старої версії.

Теж саме стосується питання міграції з Angular 1.х на Angular 2.0. Я думаю ми повинні і будемо працювати в цій галузі. Якщо це так само важливо для вас, дайте про це знати. Команді AngularJS повинна знати наскільки це важливо для вас, що б вони думали про це і планували все наперед.


Мотивація для виникнення Angular 2.0
Можливо ви думаєте: — А навіщо робити Angular 2.0 взагалі? Навіщо відразу стрибати до 2.0 і вносити стільки ключових змін? Чи це Не є примхою? Ну можна ще зрозуміти невеликі зміни, але до чого всі ці принципові зміни в Angular 2.0. Це виправдано? Воно того варто?

Я б хотів зупинитися на кількох моментах, перш ніж порину в деталі. Я сподіваюся це надасть основу розуміння подальших деталей і базу для осмисленого критики (частина з якої я маю намір запропонувати сам).

Продуктивність
Коли AngularJS був вперше створений, 5 років тому, він по суті не був призначений для програмістів. Це був інструмент більше націлений на верстальників, які потребували швидкому створенні HTML-форм. Протягом часу відбулися зміни засновані на різних вимогах і розробники взявши це інструмент стали створювати все більш і більш складні додатки. Команда Angular 1.х багато працювала над змінами дозволяють задовольняти потреби сучасних веб-додатків. Однак існує ряд обмежень впливає на можливі поліпшення. Частина цих обмежень пов'язані з продуктивністю і є результатом використовуваної моделі зв'язування даних та інфраструктури шаблонів. Для вирішення даних проблем потрібно зовсім новий підхід.

Зміни у Інтернеті
За минулі п'ять років, з моменту виходу AngularJS, в інтернеті відбулися значні зміни. Наприклад 5 років тому було можливо створити пристойний крос-браузерний сайт без допомоги jQuery. Однак сьогоднішні браузери не тільки інтенсивно вносять різні зміни в DOM, але й часто ці зміни дуже актуальних для фреймворків.

і інтернет продовжує змінюватися…

Всі ті масові зміни відбулися за останні кілька років, тільки бліда тінь того, що нас чекає в найближчі 1-3 роки. Протягом декількох місяців ES6 буде завершено. І є всі підстави думати, що в браузери в 2015 році забезпечать його повну підтримку. Поточні браузери вже підтримують деякі функції такі як модулі, класи, лямбды, генератори і т. д. Ці можливості фундаментально змінять досвід програмування на JavaScript. Але великі зміни не обмежені тільки JavaScript. На горизонті Web Components. Цей термін зазвичай пов'язаний з колекцією наступних W3C специфікацій:
— Custom Elements — можливість розширювати HTML налаштованим тегами.
— HTML Imports — можливість упаковки різних ресурсів (HTML, JS, CSS...).
— Template Element — можливість вставки інертного HTML документ.
— Shadow DOM — можливість інкапсуляції DOM, CSS.

Комбінація цих чотирьох можливостей дозволяє розробникам створювати декларативні компоненти (Custom Elements) які повністю інкапсулюються (Shadow DOM). Ці компоненти можуть описувати їх власні види (Template Element) і можуть легко пакуватися для передачі іншим розробникам (HTML Imports). Коли специфікації будуть підтримуватися всіма основними бразуреми, ми, ймовірно, побачимо вибух креативних рішень в області створення власних компонентів, які вирішують основні проблеми і недоліки в стандартному HTML (взято з внутрішньої документації проекту Databinding whit Web Components).
Це вже сьогодні можливо в Chrome, і інші браузери так само впроваджують ці зміни. Відмінне майбутні, чи не правда?
Тут є тільки одна проблема: більшість сьогоднішніх фреймворків не готові до цього, включаючи Angular 1.х. Більшість фреймворків мають систему зв'язування даних засновану на невеликій кількості HTML елементів з добре відомими евент і поведінкою. AngularJS повинен дозволити розробникам використовувати всі переваги Web Components, а для цього потрібна нова реалізація зв'язування даних (databinding).

Мобільні пристрої
Говорячи про п'яти минулих п'яти роках… боже мій, наскільки змінився комп'ютерний пейзаж. Мобільні телефони та планшети всюди! Незважаючи на те що AngularJS може використовуватися для створення мобільних аплікацій, він не створювався з цією метою. Це включає в себе все, починаючи від проблем з продуктивністю, можливістю маршрутизації, відсутності кешування предкомпилированных видів і навіть не краща підтримка евентов заснованих на дотиках.

Простота використання
Давайте будемо чесними… AngularJS не найлегша річ у вивченні. Та коли ви починаєте ви думаєте: Так це прекрасно. Це реально легко і чарівно!!!" Потім ви починаєте створювати свою аплікацію і говорите: «Боже… що це!!?? Я не розумію!». Я чую цю історію знову і знову. Навіть є забавний графік ілюструє це.
image
Більшість з складнощів виростає з оригінального дизайну фреймворка. Спочатку, не було кастомних директив. Для їх реалізації писали хардкод. І вже пізніше, вони з'явилися в API. Спочатку не було контролерів, потім… ну в загальному погляньте на графік.

Підсумок
Що б привести AngularJS у відповідність з поточним станом справ у інтернеті, стало абсолютно зрозуміло, що необхідно його принципова зміна. Фактично, без цих змін, AngularJS ризикує стати застарілим протягом року. І саме тому команда AngularJS відповідаючи на виклики часу, створює нову версію Angular. Мова йде, по суті, про переосмислення AngularJS з позицій сучасно інтернету.

Примітка
Навіть незважаючи на те що я не можу розкрити всіх деталей, я можу впевнено сказати що Angular 2.0 сильно відрізняється від 1.х. Хтось навіть може запитати якщо це той самий фреймворк. Думаю це хороше питання. Як я вже згадував раніше, я впевнений, що команді AngularJS необхідно взяти на себе тягар підтримки версії 1.х, а так само підготувати механізм міграції на 2.0 і керівництво для компаній приймають рішення про використання фреймворку сьогодні і всіх, хто планує рухатися далі з 2.0. І хоча зараз не існує подібних завдань у команди зосередженої на технологічній стороні питання, я думаю це необхідно і буде корисно і шанобливо по відношенню до спільноти AngularJS.


Тепер, коли зрозумілий бекграунд змін, давайте подивимося на ключові моменти.


AtScript
AtScript мова розширює ES6, який почали використовувати автори Angular 2.0. Він має синтаксис подібний TypeScript, для подання додаткових типів даних, який може перевірятися на стадії виконання, що краще ніж на стадії компіляції. Він так само розширює мова анотацій метаданих (metadata annotations).
Ось візьме коду:
import {Component} from 'angular'; 
import {Server} from './server';

@Component({selector: 'foo'})
export class MyComponent { 
constructor(server:Server) {
this.server = server;
}
}


Тут ми бачимо ES6 базовий код з невеликим додаванням AtScript. Оголошення Import і class відповідають вашим синтаксису ES6. Нічого особливого. Але погляньте на функцію конструктор. Зверніть увагу на те, що параметр server визначений разом з типом. У AtScript типи використовуються для створення перевірок в момент виконання (runtime). Зверніть також увагу на синтаксис @Component, над декларацією класу. Це і є анотація метаданих. Component є звичайним класом, як будь-який інший. Коли ми його створюємо з використанням анотації, компілятор генерує код є екземпляром анотації і зберігає його в область доступну для фреймворку. Пам'ятаючи це, давайте подивимося на приклад в чистому ES6:
import * as rtts from 'rtts'; 
import {Component} from 'angular'; 
import {Server} from './server';

export class MyComponent { 
constructor(server) {
rtts.types(server, Server);
this.server = server;
}
}

MyComponent.parameters = [{is:Server}]; 
MyComponent.annotate = [ 
new Component({selector: 'foo'})
];


RTTS абревіатура для RunTime Type System. Це невелика бібліотека націлена на перевірку типів на стадії виконання скрипта. В момент обробки коду компілятором, мінлива server буде оголошена типом Server. Ви так само можете створювати власні типи, для відповідності структурної типізації, або використовувати спеціальні правила. Але коли вам потрібно буде створити готовий реліз, компілятор може виключити ці оголошення типів з фінального коду для підвищення продуктивності.

Чудово те, що типізація є незалежною, оголошення типу та анотації можуть відслідковуватися. Обидва види оголошення можуть транслюватися в дуже просту ES5 сумісну структуру даних, що зберігається всередині функції MyComponent. Це дозволяє будь фреймворку або бібліотеці знаходити і використовувати ці дані. Протягом кількох років це було дуже корисно на таких платформах як .NET і Java. Це, так само, чимось нагадує можливості мета-програмування наявні в Ruby. Звичайно ж, анотації можуть використовуватися для мета-програмування в Angular 2.0, для більш простого створення директив. Подробиці пізніше.

Примітка
Мені особисто подобається AtScript, але я так само старий фанат TypeScript. Так що можна сказати, що я був вже підготовлений до AtScript. Я знаю що багато хто розробники противляться використання типів в JavaScript. Не можу їх звинувачувати. Я маю багатий досвід з різними система типізації. Іноді хороший, іноді не дуже. Цікавим моментом тут є те, що в AtScript ви можете використовувати синтаксис типів, як простий шлях для надання метаданих бібліотек. Я думаю що це одна з найбільш потужних можливостей AtScript, що дозволяє зберігати інформацію про типі в момент виконання, для надання фреймворку або мета-програмування. Я не буду здивований якщо і інших транслюються мовах (у яких перед компіляцією відбувається трансляція на іншу мову. Наприклад: TypeScript, CoffeeScript транслюються в JavaScript), так само з'являться подібні можливості.


Тим не менш у мене є пара зауважень.

Я б хотів бачити більше офіційне визнання AtScript. Це означає, що ця мова має бути не тільки всередині команди що працює над AngularJS. Він повинен жити власним життям, в якій команда AngularJS тільки важливий користувач. Це означає, що кілька розробників повинні фултайм працювати над розвитком AtScript, виправленням помилок, поліпшення генерованого коду, створення інструментів і т. п., і це повинен бути довготривалий план. Коли розробники вибирають мову, це важливе рішення. Я б хотів бачити що Google серйозно вибирає AtScript для майбутньої роботи.

Іншим питанням, яке виникає в зв'язку з цим, є Dart. Dart іншу мову так само розроблений в Google. І це не просто трансльований в JavaScript мову. Він має власну середовище виконання і бібліотеки класів. Як результат Dart має власний API для маніпуляцій з DOM, колекції, події, регулярні вирази та інше. Хоча ці API гарні самі по собі, вони не сумісні з існуючими JavaScript кодом. Через це розбіжності, між Dart та іншим світом, він спілкується через спеціальний інтерфейс обмежений. І незважаючи на технічну можливість використання JavaScript бібліотек, найчастіше це непрактично. У випадку з AngularJS, це призведе до неприйнятно низькою продуктивності. Ну, Google взяли і створив Angular Dart. Версію Angular повторену у Dart.

Проблем вирішена… так скоріше ні.

Тепер є дві версії AngularJS, в яких потрібно виправляти баги, додавати можливості, випускати релізи… написані в різних мовах і обслуговуються різними командами. Загалом одну проблему вирішили за рахунок появи іншої.

Можливо ви зараз здивовані: Яке все це має відношення до AtScript?

Для Angular 2 є ідея об'єднати Angular і Angular Dart. Замість двох команд, що працюють над двома різними версіями одного і того ж, краще мати одну команду робить роботу в одному напрямку. AtScript допоможе тут тому що він імплементований на вершині #Traceur, який легко розширюється. Тим самим команди отримають можливість використовувати AtScript для Javascript і Dart.

Чудово! І в чому проблема?

Пам'ятаєте я писав що Dart має іншу модель DOM та інші. Не так то просто транслювати подібний код. Як результат, процес складання в Angular 2.0, буде в реальності складніше ніж можна подумати. Потрібно створити різні версії API для Javascript і Dart, які потім буде використовувати компілятор. Це ще та завдання.
І це підвищує бар'єр для тих хто захоче внести свій внесок у Angular 2.0. Хоча слід зазначити, що можливо це тільки проблема поточної, ранній стадії. І, можливо, будуть знайдені інші рішення. Я знаю що багато хто з вас внесли свій великий вклад у розвиток AngularJS. Команда Angular це цінує і думає над тим, як це може бути покращено. Однак, у теперішній час, це далеко від ідеалу.

Важливе зауваження!
То що Angular 2.0 пишеться з використанням AtScript не означає що ви теж будете змушені його використовувати. Свої Angular 2.0 додатка, ви можете легко писати на TypeScript, ES6, ES5, CoffeeScript… або тому що ви віддаєте перевагу. Звичайно краще всього використовувати AtScript для розробки на Angular 2.0, із-за його здатності автоматично створювати метадані з примітивів мови, але в кінці кінців вони транслюються і в простій ES5. Фінальний ES5, в цьому випадку, концептуально подібний DDO з Angular 1.x, але він все одно краще ніж просто directive-specific технологія.



Впровадження залежності (Dependency Injection, DI)
Базової можливістю Angular 1.х було впровадження залежностей. Через DI ви могли легко побачити принцип «розділяй і володарюй» у розробці ПЗ. Складні завдання можуть бути концептуализированны з точки зору їх ролі та обов'язків. Потім вони можуть бути представлені у вигляді об'єктів, спільне використання яких призводить до досягнення кінцевої мети. Великі (або маленькі) системи, які йдуть цим шляхом, можуть монтуватися на льоту, шляхом використання DI фреймворка. Такі системи зазвичай простіше тестувати, оскільки фінальний дизайн виходить більш модульним і дозволяє простіше ізолювати компоненти. Все це було можливо в Angular 1.х, але тим не менше було кілька проблем.

Перша проблема, мучающая Angular 1.х пов'язана з минификацией. В момент коли минификатор проводив заміну імен на більш короткі, він спотворював назва залежності, в результаті чого вона переставала працювати. Зміни API додали можливість зробити код більш дружнім для минификации, але при цьому загубилася вихідна елегантність.
Інші проблеми Angular 1.х, пов'язані з відсутніми можливостями, звичайними для серверних DI фреймворків, доступних у світі .NET і Java. Наприклад, lifetime/scope child control і injection.

Анотації
У AtScript ми представили основний механізм для зв'язування метаданих з функцією. Також, AtScript формат метаданих стійкий до минификации і легко пишеться і в ES5. Це робить його ідеальним кандидатом для підтримай DI бібліотек, з інформацією необхідною для створення екземплярів об'єктів.

Коли DI потрібен екземпляру класу (або при виклику функції), він досліджує їх для отримання асоційованих метаданих. Ось приклад в AtScript:
MyComponent.parameters = [{is:Server}]; 


У новому DI шукається значення властивості parametrs, яке буде використано для визначення залежностей і спроби викликати. В даному випадку, це один параметр типу Server. В результаті буде створений екземпляр об'єкту Server і поміщений в функцію перед її викликом. Ви так само можете використовувати спеціальну Inject анотацію для використання разом з DI. Це скасує використання parametrs. Це так само легко підтримується, навіть якщо ви використовуєте мову, який автоматично не створює метадані. Ось приклад на чистому ES5:
MyComponent.annotate = [new Inject(Server)];


Середовище виконання сприймає це як параметри. Потрібно відзначити, що зараз ви можете використовувати що завгодно injection token. Наприклад, ось так:
MyComponent.annotate = [new Inject('my-string-token')]; 


Відразу як ви сконфигурируете DI, з чим можна пов'язати з 'my-string-token', все це буде працювати прекрасно. Тим не менш рекомендується використовувати примірник конструктора як було показано вище.

Примірник області видимості (Instance Scope)
У Angular 1.х всі екземпляри класу в DI були синглтонами. Це залишилося дефолтними і в Angular 2.0.
Для того що б отримати іншу поведінку в 1.х, потрібно було використовувати Services, Providers, Constants… Це призводило до плутанини.
На щастя, новий DI має нові, більш потужні, можливості. З'явився контроль області видимості. Отже, якщо вам потрібно, DI створить новий екземпляр об'єкта. Всякий раз, коли вам це потрібно, потрібно всього лише зробити наступні:
@TransientScope
export class MyClass { ... } 


Це стає ще більш потужним, коли ви створюєте ваш власний ідентифікатор області видимості для використання в комбінації з child injectors…

Вставка спадкоємця (Child injectors)
Child injectors це велика нова можливість. Child injectors успадковує від батька всі сервіси, але має можливість перевизначати їх на своєму рівні. Коли це можливість комбінується з власною областю видимості ви можете легко викликати деякі типи об'єктів у вашій системі, що автоматично перевизначити їх в різних областях видимості. Це дуже потужна можливість. Як приклад цього, новий маршрутизатор має Child Routers. Кожен Child Router, всередині створює власний Child injector. Це дозволяє кожній частині маршрутизатора успадковувати сервіси від батьківського маршрутизатора або ігнорувати ці сервіси на основі різних сценаріїв навігації.

Примітка
Настроювані області видимості і Child injectors, передбачаються як досить складні і просунуті у використанні можливості. Я не очікую що більшість додатків буде це використовувати. Однак, оскільки це вже всередині Angular, це доступна і вам, якщо вам потрібні подібні можливості.


і ще…

Деякі інші можливості в новому DI, такі як провайдери (providers) (кастомні функції які забезпечують значення для вставки), лінива вставка (lazy injection) (визначає, що ви хочете вставити, але не зараз, а пізніше) і promise-based async injection(вставки і обіцянки до яких ви можете отримати доступ асинхронно запросивши відповідну залежність)

Коментар
Мені особисто подобається новий DI. Знову ж таки, я трохи упереджений тут, оскільки використовую DI вже багато років, і це завжди було центральним компонентом у UI фреймворках які я створював. Новий DI відіграє важливу роль у Angular 2.0. Можливості, такі як Child injectors, дають величезну різницю у порівнянні зі старими рішеннями. Тепер коли це можливість є, вона може використовуватися і для шаблонізації і для маршрутизації. І там і там є потреба створювати власну область видимості і ізолювати різні сервіси.


Однією з найважливіших речей, які будуть видалені Angular 2.0 є: $scope. Однак, незважаючи на те що $scope буде видалений, деякі його можливості збережуться. Ці можливості будуть переміщені і поліпшені ставши частиною дизайну фреймворка. Ви можливо будете захоплені зненацька в видаленням $scope, але новий дизайн спростить речі і всередині Angular і для розробників. Я пишу це тут, оскільки нові можливості DI, такі як Child injectors, перетинаються з попередніми можливостями з $scope. У цьому випадку, новий DI викладає на стіл краще рішення. Загалом, це не тільки вирішить внутрішні потреби самого Angular, але й відкриє нові можливості для вас.

На жаль, життя, це не прогулянка в рожевому саду, з молодою коханкою. Давайте поговоримо від проблеми. Мова піде про модулях. Ви можете здивується, яким боком тут це. Але в плани для Angular 2.0 входить адаптація ES6 стандартів для модулів. У поточній версії Angular був використаний свій специфічний підхід для обробки модулів. П'ять років тому, коли Angular розроблявся, не було ніяких стандартів на цей рахунок. Сьогодні речі змінилися і є більш чистий підхід до вирішення цієї задачі. Це ключова зміна і вимагає переробки коду від кожного хто вирішити мігрувати на нову версію Angular. Це звичайно важко, що таке критичне зміна має бути зроблено, але Angular може стати недоречним, якщо ця проблема не буде вирішена зараз.

Є ще один камінь спотикання пов'язаний з DI, особливо якщо ви пишіть на ES5. Angular 2.0 воліє дизайн заснований на класах з анотаціями та метаданими. Синтаксис класів і анотацій не особливо хороший в ES5. Загалом, його там взагалі немає. Ви можете реалізувати всі використовуючи прототипи і інше, але це не так чисто як у AtScript або навіть в ES6 або TypeScript, які підтримують класи і статичні члени класів. Я здивуюся якщо це може бути покращено для розробників не готових перейти на ES6. Можливо, додаткова бібліотека дасть вам простий шлях створення класів з метаданими? Можливо це трохи схоже на DDO об'єкт в Angular 1.х, але більш загальне, що дозволить створити клас з метаданими. Я б хотів почути міркування про цю ідею і інші ідеї, які у вас можливо є, як згладити розробку в ES5 і поліпшити процес міграції.


Шаблонизация і зв'язування даних (Templating and Databinding)
Ми підійшли до дійсно цікавим речам: шаблонізації і зв'язування даних. Я збираюся обговорити їх у тандемі. Хоча система зв'язування даних технічно стоїть окремо від шаблонізації, в реальному досвіді, при створенні програми вони використовуються спільно. Таким чином розглядати їх пліч-о-пліч має більший сенс.

Давайте почнемо з розуміння як вид (View) потрапляє на екран. По суті ви починаєте з фрагмент HTML-коду. Він буде знаходиться усередині елемента. Цей фрагмент обробляється компілятором шаблонизатора. Компілятор аналізує фрагмент, визначає в ньому директиви, пов'язує висловлювання, події і т. п. Всі ці дані витягуються з DOM в структуру даних, яка може бути використана в кінці кінців для створення екземпляра шаблону. Як частина цієї фази відбувається деяка обробка даних, така як обробка виразів наприклад. Кожен вузол містить спеціальні інструкції пов'язаний з відповідним класом. Результат даного процесу кешується так що б не виникала необхідність повторної обробки. Ми називає це результат: ProtoView. Після того як ви отримали ProtoView ви можете використовувати його для створення View. Коли ProtoView створює View, для всіх директив, ідентифікованих на попередньому кроці, створюються їх примірники для вставки в DOM. Watchers встановлюються на пов'язаних виразах. Конфігуруються перехоплювачі подій. Структури даних, які були отримані в попередньому процесі, у фазі компіляції, дозволяють нам робити все дуже швидко. Після того як ви отримали View, ви можете відобразити його додавши під ViewPort. Як розробник, ви не побачите велику частину всього цього, ви просто створите шаблон і він буде працювати. Але я хотів розписати цей процес на більш високому рівні, перш ніж перейти до деталей.

Динамічне завантаження (Dynamic Loading)
Однією з дуже відсутніх можливостей у Angular 1.х була динамічне завантаження коду. Якщо ви хотіли додати нові директиви або контролери на льоту, це було дуже складно або навіть неможливо. Це просто не підтримувалося. У Angular 2.0, ми створюємо деякі речі з нуля, пам'ятаючи про асинхронності. І коли ми компілюємо шаблон, це по суті асинхронний процес.

Тепер, мені потрібно згадати про деталі компіляції шаблону і я відштовхнуся від мого спрощеного пояснення вище. Коли ви компілює шаблон ви не тільки забезпечуєте компілятору шаблон, але і ви так само надаєте визначення компонентів (Component definition). Component definition містить метадані про те що саме, директиви, фільтри тощо використане в шаблоні. Це гарантує те, що всі необхідні залежності, будуть завантажені перш ніж вони будуть оброблені в процесі компіляції. Із-за того що ми базуємо наш код на ES6 специфікації, просте згадування в залежності Component definition викличе модуль завантаження і завантажить її, якщо вона ще не була завантажена. Таким чином, використовуючи можливості ES6 для модулів, ми безкоштовно отримуємо динамічну завантаження все що нам знадобиться.

Директиви (Directives)
Перш ніж ми копнемо синтаксис шаблонів, ми повинні поглянути на директиви як на спосіб розширення HTML. У Angular 1.х DDO (Directive Definition Object) використовувався для створення директив. Це здається був один з найбільших джерел для страждань розробників.

Що якщо ми зробимо директиви простіше?

Ми поговорили про модулях, класах і анотаціях. Що якщо ми використовуємо ці основні конструкції для створення директив? І звичайно ж ми це зробили.

У Angular 2.0 існує три основних типи директив.
  • Component Directive — створює кастомный компонент об'єднує View і Controller. Ми можемо використовувати його як власних HTML елемент. А так же маршрутизатор може створювати маршрут до компонентів.
  • Decorator Directive — розширення існуючих HTML-елементів додатковим провидінням. Класичний приклад ng-sh-ow.
  • Template Directive — Трансформація HTML багаторазово використовується шаблон. Творець директиви може контролювати де і як шаблон буде оброблений і вставлений в DOM. Прикладом можуть служити ng-if і ng-repeat.


Можливо, ви вже чули про смерть контролерів у Angular 2.0. Ну, це не зовсім правда. В реальності контролери стали частиною того, що ми називаємо Component. Component складається з вигляду (View) і контролер (Controller). Вид це ваш HTML шаблон і контролер визначає JavaScript логіку. Замість того що б визначати контролер через API як це зроблено в Angular 1.х, у 2.0 ви можете створити звичайний клас з деякою анотацією. Ось приклад частині компоненти реалізує контролер (як виглядай Вид розглянемо трохи пізніше):
@ComponentDirective({
selector:'tab-container',
directives:[NgRepeat]
})
export class TabContainer { 
constructor(panes:Query<Pane>) {
this.panes = panes;
}

select(selectedPane:Pane) { ... }
}


Можна відзначити кілька моментів.

Перший, контролер компоненти це просто клас. Конструктор автоматично вставить залежності. Завдяки тому що тут використаний Child injector, клас може отримати доступ до будь-якого сервісу вище в DOM ієрархії, а так само службам локальним для даного елемента. Наприклад, в даному випадку, Query injector. Це спеціальна колекція, яка автоматично синхронізується з спадкоємцями Pane (панель) елемента і дає вам знати, коли щось додається або вилучається.
Це дозволить вам обробляти подібну логіку як в $link коллбек з Angular 1.х, але там це обробляється іншим способом ніж через конструктор класу.

Тепер, погляньте на @ComponentDirective анотацію. Це ідентифікує клас як компоненту і надає метадані які потрібні компілятор для його вставки. Наприклад selector:'tab-container', це CSS селектор який буде використаний для пошуку в HTML. Елемент збігся з цим селектором буде повернутий до TabContainer. Так само, directives:[NgReapet] вказує на залежність використовується в шаблоні. Я поки це не демонстрував. Трохи пізніше ми побачимо це коли поговоримо про синтаксис.

Важливою деталлю є те, що шаблон буде прямо пов'язаний з класом. Це означає, що будь-які властивості і методи класу можуть бути доступні безпосередньо в шаблоні. Це подібно «controller as» синтаксису з Angular 1.2. Але при цьому немає необхідності установки $scope між класом і шаблоном. Як результат спрощення Angular зсередини і спрощення синтаксису для розробників на ньому.

Тепер Давайте поглянемо на Decorator Directive. Як щодо NgShow?
@DecoratorDirective({
selector:'[ng-show]',
bind: { 'ngShow': 'ngShow' },
observe: {'ngShow': 'ngShowChanged'}
})
export class NgShow { 
constructor(element:Element) {
this.element = element;
}

ngShowChanged(newValue){
if(newValue){
this.element.style.display = 'block';
}else{
this.element.style.display = 'none';
}
}
}


Тут ми можемо бачити трохи більше аспектів. Насамперед, знову використаний клас з анотацією. Конструктор пов'язує клас з декодируемым HTML елементом. Компілятор розуміє, що мова йде про декорації тому що це Decorator Directive і розуміє що це потрібно застосувати до будь-якого елементу який збігається з CSS селектором selector:'[ng-show]'.

Так само є кілька інших цікавих властивостей у даній анотації.

bind: {'ngShow': 'ngShow'} використовується для зв'язування властивості класу з HTML атрибутом. Не всі властивості класу проявляться у вигляді HTML атрибутів. Якщо ви хочете що б властивість було зв'язуючим з HTML, ви визначаєте це в bind метаданих.

observe: {'ngShow': 'ngShowChanged'} говорить системі зв'язування що ви хочете отримувати повідомлення, коли ngShow властивість зміниться та буде викликаний метод ngShowChanged. Зверніть увагу що ngShowChanged реагує на зміни, що в свою чергу змінять відображення HTML-елемента, до якого він приєднаний. (Зауважимо, що це дуже простою реалізація, тільки для демонстраційних цілей).

ОК, як же виглядає Tеmplate Directive? Як щодо того, щоб поглянути на NgIf?
@TemplateDirective({
selector: '[ng-if]',
bind: {'ngIf': 'ngIf'},
observe: {'ngIf': 'ngIfChanged'}
})
export class NgIf { 
constructor(viewFactory:BoundViewFactory, viewPort:ViewPort) {
this.viewFactory = viewFactory;
this.viewPort = viewPort;
this.view = null;
}

ngIfChanged(value) {
if (!value && this.view) {
this.view.remove();
this.view = null;
}

if (value) {
this.view = this.viewFactory.createView();
this.view.appendTo(this.viewPort);
}
}
}


Сподіваюся вам зрозумілий зміст цієї Tеmplate Directive анотації. Насамперед йде реєстрація директиви та надання необхідних метаданих для установки властивостей і спостерігачів, так як в попередньому прикладі для NgShow. Tеmplate Directive має доступ до визначених спеціальним службам які можуть бути вставлені в конструкторі. Першою є ViewFactory. Як згадувалося раніше Tеmplate Directive перетворює HTML зазначений в шаблоні. Шаблон автоматично компілюється і ви отримуєте доступ до ViewFactory в Tеmplate Directive. Виклик createView API створює екземпляр шаблону. Ви отримуєте доступ до ViewPort який представляє область DOM з якого був витягнутий шаблон. Ви можете використовувати це для додавання або видалення примірника шаблону з DOM. Зверніть увагу як ngIfChanged реагує на зміни у вашому шаблоні і додає або видаляє це ViewPort. Якщо ви реалізуєте замість це NgRepeat ви повинні багаторазово створити екземпляр шаблону і так само надати специфічні дані в createView API і потім ви зможете додати безліч примірників у ViewPort. Це основи.

Тепер ви ознайомилися з канонічними прикладами трьох типів директив. Я сподіваюся що це роз'яснило речі в плані того, як ви можете розширити HTML новим поведінкою.

Тим не менш, є важлива річ, яку я до сих пір не повністю роз'яснив. Це контролери.

Як ви створюєте контролер для вашої аплікації? Припустимо, ви хочете налаштувати маршрутизатор так щоб відбувався виклик контролера і відображався Вигляд. Як ви це зробите? Проста відповідь, це використовувати Component Directive.

У Angular 1.х директиви і контролери були різними речами. З різних API і можливостями. У Angular 2.0 ми прибрали DDO і зробили директиви основними на класах, де ми змогли об'єднати директиви і контролери всередині моделі компоненти. Тепер коли ви налаштовуєте маршрутизатор, ви просто вказуєте маршрут до Component Directive (яка містить вигляд і контролер, по суті, так само як і раніше).

Наприклад, якщо ви створюєте гіпотетичний контролер для редагування клієнтів, це може виглядати так:
@ComponentDirective
export class CustomerEditController { 
constructor(server:Server) {
this.server = server;
this.customer = null;
}

activate(customerId) {
return this.server.loadCustomer(customerId)
.then(response => this.customer = response.customer);
}
}


Тут немає реально нічого нового. Ми просто вставили гіпотетичну службу server і використовуємо її для завантаження даних про клієнта в момент коли ми активували маршрутизатор. Єдине, що тут може бути цікаво, те що вам не потрібен селектор або інші метадані. Причиною того є що цей компонент не використовується як елемент. Він був динамічно створений маршрутизатором і динамічно вставлений в DOM. Як результат ми опустили непотрібні деталі.

Отже, ви знаєте як створити Component Directive, як створити еквівалент Angular 1.х контролера для використання з маршрутизатором. Це було дуже непросто в Angular 1.х, але тепер у нас є класи та метадані та робота з директивами значно спрощується і стає значно простіше створювати свої «контролери» таким способом.

Примітка
Я б хотів зазначити, що приклади коду директив показаних вище засновані на комбінації попередньою версією коду і чорнових специфікацій. Вони повинні розглядати швидше як демонстрація, але не як точний синтаксис директив який ще знаходиться в розробці. Компілятор шаблонів і мова зв'язування дуже мінлива частина Angular 2.0 в даний момент, з досить частими змінами дизайну.

Синтаксис шаблонів (Template Syntax)
Отже, ми зрозуміли, як працює основний процес компіляції, що він може завантажувати код асинхронно, як писати директиви і як їх підключати, як контролери вписуються в цей пазл, але ми досі не розглянули актуальне шаблон. Давайте зробимо це зараз, розглянувши шаблон для гіпотетичного TabContainer, приклад якого я показував раніше. Я включив тут знову код директиви для зручності:
@ComponentDirective({
selector:'tab-container',
directives:[NgRepeat]
})
export class TabContainer { 
constructor(panes:Query<Pane>) {
this.panes = panes;
}

select(selectedPane:Pane) { ... }
}


<template> 
<div class="border">
<div class="tabs">
<div [ng-repeat|pane]="panes" class="tab" (^click)="select(pane)">
<img [src]="pane.icon"><span>${pane.name}</span>
</div>
</div>
<content></content>
</div>
</template> 


Будь ласка, стримуйте жах, який ви відчуваєте, дивлячись на цей код. Так, це дійсний HTML відповідний специфікації. Але ні, це ще не наш фінальний синтаксис зв'язування. Але давайте використаємо це, як приклад для початкової точки, для більш широкої дискусії.

Ключем розуміння синтаксису зв'язування даних, є ліва частина атрибута декларації. Пам'ятаючи це, давайте поглянемо на тег зображення.
<img [src]="pane.icon"><span>${pane.name}</span> 


Коли ми бачимо ім'я атрибута оточене [], це говорить нам, що права частина, значення атрибута є пов'язаною виразом (binding expression).
Коли ми бачимо вираз оточене ${}, це говорить нам, що даний вираз має интерполироваться в контент як рядок. (Це подібний синтаксис який використаний у ES6 для інтерполяції рядків)

Обидва ці зв'язування одне-спрямовані від model/controller до view.

Тепер подивимося на цей моторошний див:
<div [ng-repeat|pane]="panes" class="tab" (^click)="select(pane)">


ng-repeate це TemplateDirective. Можна сказати, що ми пов'язуємо його з виразом оскільки він оточений []. Крім того, так само є | і слово «pane». Це означає що локальне ім'я змінної яке використовується в шаблоні є «pane».

Тепер подивимося на (^click). Круглі дужки використовуються для позначення виразу обробника подій. Якщо всередині дужок знаходиться ^, це означає що ви прикріплюєте розробник прямо до DOM вузла, ніж ми дозволимо події спливти і воно буде оброблено на верхньому рівні документа.

Я зараз утримаюся від висловлення власної думки з приводу всього, що це і висловлю його нижче, коли буду коментувати цю секцію. Давайте поки проігноруємо, що ви або я думаємо про те, чому був обраний такий синтаксис.

Web Components змінили все. Це ще один приклад зміни виходять за звичні рамки. Більшість фреймворків, заснованих на зв'язаних даних, використовують обмежений набір елементів HTML і розуміють спеціальне поведінку лише у деяких елементів, таких як input і подібних. Однак, в світі Web Components, не може бути зроблено жодних припущень. Розробник, ніяк не пов'язаний з Angular, може створити свій власний з будь-яким числом властивостей і подій, що йому будуть необхідні. На жаль, не існує способу проаналізувати Web Component і витягти якісь метадані які можна використовувати для зв'язування даних.
Немає ніякого способу дізнатися, які події актуальні на даний момент. Гляньте на це:
<x-foo bar="..." baz="..."></x-foo>


Дивлячись на bar і baz чи можете ви визначити це є подією або властивістю? Немає… і на жаль Angular теж, тому що специфікація Web Components не включає поняття для само-опису (self-describing) компонентів. Це сумно, тому що система зв'язування даних не може сказати, що саме потребує зв'язування, а що ні. Для вирішення цієї проблеми, нам треба загальна система зв'язування даних з синтаксисом який дозволить розробнику сказати що є подією, а що властивість для зв'язування.

Це не єдина трудність. Так само необхідно, щоб ця інформація була представлена так що б вона не порушувала сам Web Component. Це означає, що Web Component не можна дозволити побачити це вираз. Це може зламати його. Можна тільки дозволити побачити результат визначення виразу. Це актуально не тільки для Web Components, але також і для будь-яких вставок. Розглянемо це:
<img src="{{some.expression}}"> 


Цей код викличе помилковий запит намагається знайти "{{some.expression}}" зображення. Це не те, чого ми очікували. Нам не потрібно щоб img бачив сам вираз, а лише результат його визначення. Angular 1.х вирішує це використовуючи ng-src, кастомний директиву.

Тепер повернемося до Web Components… це було б катастрофою якби для кожного атрибута Web Component ви були змушені створювати власну директиву. Я не думаю що ми хочемо це. Нам потрібно вирішити проблему більш загальним способом.

Для досягнення цієї мети, є дві можливості. Перша, видалити атрибут з DOM у момент компіляції шаблону. Це запобіжить реагування Web Component на вміст вираження. Однак, зробивши це, інспектор DOM не покаже ніяких слідів прив'язки до атрибуту. Це може зробити налагодження більш складною.
Іншою можливістю є кодування атрибута так, що Web Component не розпізнає його. Це дозволить Angular бачити вираження, але запобігає їх розпізнання Web Component. Це дозволить залишити нам атрибут елемента після компіляції, що дозволить бачити його інспектору DOM. Це безумовно краще для налагодження.

Як ви можете бачити вище, наша команда у справжні час виступає за другий підхід, а саме кодування імен атрибутів. Таке кодування так само необхідно, що б була видна різниця між властивостями і подіями. Синтаксис показаний вище, є однією з декількох точок зору як зробити це.

Це частина викликала сильну дискусію протягом місяців всередині команди. Вищенаведений синтаксис не був одностайно прийнятий усіма, але це було рішення більшості. Є дуже багато міркувань при розробці синтаксису зв'язування. Якщо це вам цікаво, я рекомендую прочитати досить обширний документ на цю тему тут.

Тепер, коли ви ознайомилися з тим, як створювати шаблони, зв'язування і директиви, все об'єдналося в одне ціле.

Коментар
Я хочу сказати дуже багато чого щодо того, що я тільки що вам розповів. Важко навіть вирішити, з чого почати.

Давайте почнемо з компілятора.

На початку ми розглянули поверхневий процес компіляції шаблону. Хоча ще купа речей робиться у цій галузі, я, в цілому, дуже задоволений розробленим компілятором.Там використано досить багато кльових речей, що дозволяє зберегти використання невеликого обсягу пам'яті, зменшити кількість сміття та надати супер швидкий примірник шаблону. Це чудово. Звичайно залишилися ще деякі можливості для удосконалень, але те, що зроблено вже солідно. Хоча ми не говорили про dirty checking (механізм, за допомогою якого відбувається оновлення зв'язаних даних) його реалізація так само містить ряд нових ідей, які, ймовірно, приведуть до поліпшення продуктивності і в примірниках шаблонів та в самому механізмі dirty checking. Все це добре. А можливість динамічного завантаження, просто прекрасна. Це дуже відсутня можливість Angular 1.х, критична для великих програм. І я щасливий, що це є одним з основних вимог нового дизайну фреймворка.

Тепер поговоримо про директиви.

Новий механізм створює директиви використовуючи класи, краща обробка залежностей та анотації дуже приємні речі. Цей шлях простіше ніж той, який потрібний в Angular 1.х. На жаль, це дуже значна зміна для розробників 1.х. Це так само може бути трохи складніше для тих, хто обмежений ES5 і не хоче або не може використовувати ES6, TypeScript або AtScript. Раніше, в цій статті я згадував можливість використовувати невеликих бібліотек для роботи з ES5 підтримують створення анотованих класів. Подібний API може виглядати трохи краще ніж DDO об'єкт. Можливо це так само полегшить процес перенесення коду з Angular 1.х на 2.0. Ви можете спробувати зробити це зараз 1.3 і потім це допоможе потім простіше перейти на 2.0… уявіть це різновид абстрактного шару при міграції. Хоча я і не впевнений. Я б хотів почути вашу думку про це. Я знаю, що для багатьох з вас це може бути проблемою.

Інша річ, яка турбує мене в директивах, то що анотації трохи багатослівні. Подивіться ще раз на NgShow директиву. Ви бачите, як багато разів NgShow чи варіант цього повторюється? Це виглядає трохи дурним на мій погляд. Так само подивіться на CustomerEditControler. Чому нам потрібно все це в анотації ComponentDirective? Чому нам просто не використовувати звичайний клас, оскільки маршрутизатор вже знає що це.

Я багато сперечався всередині команди, щодо переваги конвенцій над конфігураціями. Це те, що зробило Rails популярним і вплинуло на багато сучасні фреймворки. Тому я думаю, що це правильний шлях. Я б хотів бачити як конвенції при створенні директиви ліквідують шаблони. Без цього я б не відчував що нова система створення директив на спрощена настільки, наскільки це потрібно. Є відчуття що складнощі мають з DDO просто перемістилися в інше місце і знаходяться зараз в анотаціях. Що думає ви? Вам подобаються конвенції? Як ви думаєте, вони можуть зробити директиви простіше (при умови що ви завжди можете перевизначити конвенцію анотацією)?

На жаль, це не єдина реально серйозна з наявних проблем. Серйозну проблему так само можна побачити в ComponentDirective. Цікаво, ви бачите її? Ви звернули увагу, що тут порушено принцип Separation Presentation? Давайте ще раз поглянемо на приклад з TabContainer:
@ComponentDirective({
selector:'tab-container',
directives:[NgRepeat]
})
export class TabContainer { 
constructor(panes:Query<Pane>) {
this.panes = panes;
}

select(selectedPane:Pane) { ... }
}


Ви бачите що TabContainer зобов'язаний, в його метаданих, перерахувати всі директиви використовуються в шаблоні? Це пряма зв'язок між TabContainer (контролером) і деталями реалізації View. Раніше я згадував що це необхідний для того, що б компілятор знав що необхідно завантажити перш ніж компілювати шаблон. Але це руйнує основну вигоду від концепції використання MVC, MVVM або іншого підходу розділяє рівні презентації та логіки. Що б ви не думали, що це просто теорія, дозвольте мені зазначити деякі з наслідків цього:
  • не буде можливо використовувати ng-include. Компілятор обробляє ComponentDirective в порядку компіляції HTML. Це не дозволить включити ваш власний HTML View.
  • буде боляче якщо вам потрібно кілька потенційних презентацій (views) для одного компоненту. Уявіть що у вас є компонент і ви хочете мати для нього різні презентації (views) для телефону та комп'ютера. Ви повинні об'єднати всі директиви, фільтри і т. д., які ви будете використовувати в обох презентаціях і упевниться, що всі вони представлені в метаданих одного компонента. Це нічний кошмар для подальшої підтримки коду. Ви більше не можете нічого видалити зі списку залежностей, не перевіривши всі презентації.
  • неможливо мати безліч runtime презентацій для одного компоненту. Уявіть що ви налаштували маршрутизатор з набором шляхів. Декілька шляхів можуть використовувати один контролер, але вам необхідні різні презентації. Ви просто не можете зробити це. Вже вибачте.
  • скоєно неможливо отримати спеціальну композицію екранів. Це робить UI заснованим на управління даними, більш складним у майбутньому. Ви не можете зробити різні комбінації презентацій і контролерів (view models). Це обмежує можливості повторного використання і перешкоджає композиційним підходів при створенні UI. Це змушує вас створювати підкласи контролера для того що б отримати різні його презентації.


На щастя, дизайн ще знаходиться в стадії безлічі змін і вирішити дану проблему поки досить просто. Дозволити шаблонами бути повністю автономними дозволивши їм визначати власний імпорт. Прибрати метадані з директиви і помістивши їх у шаблон.
Ви можете використовувати власний елемент як наприклад:
<ng-import src="ngRepeat"></ng-import>


Компілятор тут знайде все необхідне, що потрібно завантажити, перш ніж він обробить шаблон. Це так. Всі вище згадані проблеми будуть вирішені.

ОК, тепер, коли ми розглянули компілятор і директиви…

Я думаю, що це привело нас до синтаксису шаблонів.

Давайте будемо чесними. Коли багато хто з вас побачили синтаксис шаблонів, вас ймовірно вирвало. Не всіх, але багатьох з вас. Я особисто був проти цього синтаксису, але це було рішенням більшості. (Що б зрозуміти чому так стало, вам потрібно прочитати ось цей документ Не бійтеся! Там було декілька технічних проблем, опис яких змусило синтаксис зазначений вище стати фактичним синтаксисом. Зараз команда повернулася до живої дискусії про кращому синтаксисі. Багато членів співтовариства приєдналися і запропонували власні ідеї. Я представлю мої власні рекомендації тут для того, що ви могли їх прокоментувати.

Ось який базовий синтаксис я пропоную:
  1. property="{{expression}}" — односторонні зв'язування даних від моделі до властивості елемента, зазначеного подвійними фігурними дужками {{}}
  2. on-event="{{expression}}" — додає обробник події який виконає вираз, позначений on — префіксом.
  3. ${expression} — рядок що інтерполюється в HTML контент і атрибути (заснована на ES6 синтаксисі)


Це все. Потім, під капотом, нам потрібно видалити вирази з DOM що б запобігти різні проблеми з WebComponents. І тільки в режимі налагодження ми повинні повернути їх назад використовуючи префікс такий як bind — таким чином вони будуть виявлені при інспектуванні DOM без впливу на WebComponents. І хоча тут виникає різниця між тим що ви пишете і що бачите в DOM, але я думаю це розумний підхід для чистого і більше стандартизованого синтаксису зв'язування.
Не всі погодяться зі мною. Що думаєте ви?

Це пропозиція допомагає вирішити проблеми зв'язування і так само допомагає з зворотною сумісністю. Можливо ми повинні трохи більше думати про зворотної сумісності. Ми могли б дозволити використовувати {{expression}} для інтерполяції рядків в HTML, але ви повинні підписатися під цим. Це застаріє тільки в наступному тисячолітті. Оптимальним же методом буде завжди ${expression}, але це зажадає більш підготовленого шляхи міграції. Так само можливо потрібно створити набір додаткових директив, які можуть загубитися, для того що б були доступні ng-click і подібні речі, які теж не скоро застаріють. Додатково, ми повинні представити документацію, яка представить старе і нове-пліч боці і допоможе людям перевести шаблони вчасно.

Це базис моєї пропозиції. Хоча є й інші пропозиції. Я звичайно ж упереджений. Хотілося б знати, що ви думаєте. Зворотна сумісність для вас важлива? Чи ви Будете віддавати перевагу синтаксис {{}} або ви віддасте перевагу синтаксис який кодує імена атрибутів. Тут багато можливостей для дискусії.

Ну, тепер то всі наші проблеми вирішені? Немає. Залишився ще слон в посудній лавці.

Мова йде про направлення зв'язування!

Я не знаю, якщо ви звернули увагу на це, але тут не прозвучало жодного слова про двосторонньому зв'язуванні (two-way databinding), цілої статті. Насправді, ні один з прикладів приведених вище не включав спосіб специфікації різних опцій зв'язування, таких як спрямованість, тригери, debouce і т. п. Ну і як тепер зв'язати елемент і передати дані в модель? Як створити власний WebComponent який потребує оновлення вашої моделі?

Існує запекла дискусія всередині Angular команди, про те якщо 2.0 потребує двосторонньому зв'язування чи ні. Якщо ви читали публічні документ описують дизайн або стежили за презентацією на ngEurope або Q&A, ви це зрозуміли. Я сильний прихильник двостороннього зв'язування. Для мене це виглядає як частина душі Angular. Я поки не бачив елегантною альтернативи і поки його не побачу, до тих пір буду виступати за збереження двостороннього зв'язування даних.

Ви навіть може бути здивовані, чому це взагалі розглядається.

Я чув деякі пояснення пов'язані з дотриманням DAG потоками даних. Це ідея нещодавно стала популярною завдяки ReactJS. Але, по правді кажучи, ви не можете повністю забезпечити це. Я можу порушити це просто використовуючи агрегатор подій (event aggregator). Цей патерн дуже поширений в композитних аплікаціях. Я думаю можна навчити людей що таке DAG і допомогти їм використовувати це, але не можна їх примушувати. Це тільки робить їх роботу більш складною.

Я думаю єдиною велика проблема щодо зв'язування в Angular це dirty checking. З моменту коли dirty checking використаний, кожна зміна має перевірятися двічі. Причина в тому, що якщо відбулася зміна воно може привести в свою чергу до іншим пов'язаним змін, як побічного ефекту. Тому потрібна повторна перевірка того що все в порядку. Тепер, якщо є зміни після повторної перевірки, то повинна бути третя… і так далі. Це те, що називають стабілізацією моделі. Так, це мука для системи. Але відмова від двостороннього зв'язування не вирішить цієї проблеми. Зв'язування даних потужний інструмент і люди тут продовжують робити помилки. Але я думаю, з ними можна впоратися. Я знаю це правда для багатьох з вас.

Можливо ви не згодні зі мною і думаєте «нарешті позбавлення від двостороннього зв'язування». Люди думаючі так безумовно є. Однак, я підозрюю, що багато користувачів Angular, Durandal, Knockuot, Ember та інших подібних фреймворків, погодяться зі мною. На щастя команда не зациклилася на одному. Вона намагається розглянути всі можливості. Таким чином, не варто особливо турбуватися. Тим не менше, якщо вам подобається двостороннє зв'язування, ви повинно допомогти мені. Я думаю буде відмінно, якщо й інша частина команди Angular дізнається як сильно ви любите двостороннє зв'язування.

З іншого боку, якщо ви думаєте, що двостороннє зв'язування безглузда ідея, запрошую вас допомогти нам знайти альтернативу. Досі я не бачив гарних альтернатив, але можливо у вас є якась відмінна ідея. В цьому випадку я запрошую поділитись нею з нами. Якщо ми працюючи разом знайдемо щось дійсно краще… це буде чудово.



Маршрутизатор (The Router)

Давайте тепер поговоримо про маршрутизатор…

Примітка. Якщо ви втомилися від читання і хотіли б краще подивитися відео про маршрутизатор, ви можете знайти його на презентації на ngEurope.

Основи
Для того що б Angular 2.0 був гідним фреймворком він потребує хорошому рішення для маршрутизації. На початку 2014 року Brian Ford почав процес збору і про наявних рішеннях в області маршрутизації як всередині так і за межами Angular співтовариства. Ми вивчили безліч готових рішень і об'єднали це з запитами, які надходять з спільноти. Після того, як був складений і обговорено відповідний документ, я взяв на себе спробу реалізувати це. Після певного числа ітерацій ми думаємо у нас вийшло щось кльове.

Природно, новий маршрутизатор обробляє всі основні сценарії, які б ви очікували від якого або маршрутизатора.
Ось швидкий список основних можливостей…
  • Проста заснована на JSON конфігурація
  • Додаткові угоди по конфігурації
  • Статичні, параметризрвані і splat шаблони
  • підтримка рядки запитів
  • Використання пуш станів (push state) і змін хешу (hashchange)
  • Навігаційна модель (для генерації навігаційного UI)
  • Оновлення Title біля документів
  • обробка 404 помилки
  • маніпуляції з історією
і інше…

Вкладений маршрутизатор (Child Routers)
Можливо ви звикли до того, що б налаштувати карту маршрутизації для всього вашого додатки наперед. Але, з новим маршрутизатором ви отримали додаткову гнучкість у цьому питанні. Фактично ви можете мати свій маршрутизатор у кожного компонента до якого потрібно перейти. Ми назвали це Child Routers і це дозволяє вам приховати (инкапсулировать) різні області застосування. Якщо ви працюєте над великим проектом з декількома командами або ви універсал-одинак який любить все тримати по поличках, ви полюбите цю можливість. Тепер ви може розробляти кожну частину вашого застосування як міні-додаток, з власним маршрутизатором. Потім, ви просто підключаєте його до основного додатком і вказуєте відносний шлях до компоненту і це все буде працювати. Якщо ви хочете побачити щось веселе, подивіться на демонстрацію рекурсивного роутера на моєї презентації.

Вікно активації (Screen Activation)
Іноді, під час навігації, у вас виникає необхідність отримати контроль над процесом. Наприклад, користувач залишає сторінку з збереженими даними і вам потрібно переконатися що це так і повинно бути. Або, перш ніж перейти на третій крок, вам потрібно переконається що є дані з попередніх двох кроків і зробити редирект тому при необхідності. Для обробки такого типу сценаріїв ми реалізували життєвий цикл навігації в якому ваші контролери можуть впливати на процес навігації. Ось лист перехоплених подій (hooks) життєвого циклу:
  1. canActivate — дозволяє/забороняє навігацію у новий контролер.
  2. activate — відповідь на успішний перехід до нового контролера.
  3. canDeactivate — дозволяє/забороняє навігацію з поточного контролера.
  4. deactivate — відповідь на успішний вихід зі старого контролера.


Функції зворотного виклику з префіксом can* дозволяють вам контролювати навігацію, повертаючи логічні значення. Ви так само можете повернути обіцянку (promice) як значення, що дозволить вам виконувати асинхронні операції як частина цього процесу. Крім того, ви може повертати спеціальні навігаційні команди (наприклад Redirect) які дозволять вам контролювати процес на низькому рівні.

Як і можна припустити це все працює і з вкладеним маршрутизатором.

Дизайн (Design)
Ми багато працює що б зробити дизайн підключається наскільки це взагалі можливо. Вся логіка процесу навігації побудована по конвеєрному принципом. Це значить що ви можете додати ваш власний крок у будь-ланка конвеєра або навіть видалити дефолтні кроки. Наприклад, якщо вам не подобається Screen Activation, ви можете видалити це. Всі змодельовано як четырехшаговый конвеєр, по одному кроку на кожну фазу життєвого циклу. Іншою важливою можливістю конвеєра є те, що кожен крок асинхронний. Відповідно, якщо вам потрібно зробити запит до сервера для авторизації користувача або завантажити попередні дані для контролера, ви може зробити це як крок конвеєра і виключити таким чином цей код з контролера, за непотрібністю.

Коментар
Для мене важко в даному випадку бути не упередженим, оскільки я займався реалізацією маршрутизатора. Я думаю що це хороший перший пас" для нового маршрутизатора. Там ще є деякі відсутні можливості, але в цілому я думаю що загальний дизайн маршрутизатора є дуже гарним.


Як бонус ми збираємося перенести його і в Angular 1.3

Висновок

Спасибі за ваш час витрачений на читання цієї статті. На момент публікації (листопад 2014) це сама велика стаття про Angular 2.0.

Я спробував викласти міркування про основні особливості і дизайн нової версії. Що так само включає значну частку моєї особистої думки. Дизайн все ще розвивається і ми поки що перебуваємо на ранній стадії розвитку. Таким чином, я сподіваюся основна частина планованих змін буде зроблена до релізу. Є звичайно «невідомі», такі як двостороннє зв'язування даних, де команда ще не в якому напрямку рухатися правильно. Ми намагаємося розглянути всі точки зору. Можливо ви придумали щось нове і дивне в цьому напрямку!? Будь ласка, будьте терплячі і пам'ятайте що як учасники спільноти ви запрошені взяти участь у вирішенні цих проблем. Я благаю вас, будьте добрі і ввічливі коли робить це, але, будь ласка, поділіться з нами думками, ідеями та думками.

Спасибі!

Rob Eisenberg
Широко визнаний експерт в області розробки UI. Творець Caliburn.Micro і Durandal фреймворку і член команди Angular 2.0


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

0 коментарів

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