Angular 2 не так уж и плох, а если задуматься то и просто хорош. Алексей Охрименко ( IPONWEB ) 1
Здоровье прежде всего 2
Алексей Охрименко Tweeter: @Ai_boy Gitter: aiboy 3
IPONWEB 4
5 import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
6 import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
7 import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
Глава №1 - И была рука 8
KSS 9
KSS 10 Babel
KSS 11 TypeScript?
KSS 12 Angular 2
Глава №2 - Горы отвращения 13
Angular 2 is Beta* 14 * - now RC1
ASP.Net MVC - Developer Preview 2 15
16
17 ([]) [] () ([]) [] () ([]) [] ([]) [] () ([] ([]) [] ([]) [] () ([])
18
19 /** * Simplest possible template in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
20 /** * Simplest possible template in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
21 /** * Simplest possible template in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
22 node.value = _getValue(node.ctx, node.getAttribute('ng-value')); } // ng-selected will set selected attribute depending on true-finess of value if (node.getAttribute('ng-selected')) { var selected = _getValue(node.ctx, node.getAttribute('ng-selected')); if (selected) { node.setAttribute('selected', 'yes'); } } // ng-text will assign text to node no need for escaping if (node.getAttribute('ng-text')) { node.innerText = _getValue(node.ctx, node.getAttribute('ng-text')); } // ng-class will simply assign class from defined property if (node.getAttribute('ng-class')) { var classVal = _getValue(node.ctx, node.getAttribute('ng-class')); if (classVal) { node.className += ' ' + classVal; } } // ng-show shows elements depending on true-finess of the value if (node.getAttribute('ng-show')) {
23 // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) { var child = node.children[0]; var array = _getValue(node.ctx, node.getAttribute('ng-loop')) || []; node.removeChild(child); array.forEach((item) => { child = child.cloneNode(true); child.ctx = item; node.appendChild(child); }); } // ng-value will assign value to node if (node.getAttribute('ng-value')) { node.value = _getValue(node.ctx, node.getAttribute('ng-value')); } // ng-selected will set selected attribute depending on true-finess of value if (node.getAttribute('ng-selected')) {
24 // ng-change will add "change" event handler if (node.getAttribute('ng-change')) { // closure to rescue ((node)=> { node.addEventListener('change', (event) => { eventHandlerObject[node.getAttribute(‘ng-change')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } // ng-click will add "click" event handler if (node.getAttribute('ng-click')) { // closure to rescue ((node)=> { node.addEventListener('click', (event) => { eventHandlerObject[node.getAttribute(‘ng-click')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } } return container; } function _getValue(ctx, attrVal) { if (attrVal === 'self') { return ctx; }
25 } // ng-hide shows elements depending on false-iness of the value if (node.getAttribute('ng-hide')) { var isHidden = _getValue(node.ctx, node.getAttribute('ng-hide')); if (isHidden) { node.style.display = 'none'; } } // ng-change will add "change" event handler if (node.getAttribute('ng-change')) { // closure to rescue ((node)=> { node.addEventListener('change', (event) => { eventHandlerObject[node.getAttribute(‘ng-change')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } // ng-click will add "click" event handler if (node.getAttribute('ng-click')) { // closure to rescue ((node)=> { node.addEventListener('click', (event) => { eventHandlerObject[node.getAttribute(‘ng-click')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); }
26 [] () [()]
27 [property]=‘value’ -> property=‘value’ () [()]
28 [property]=‘value’ -> property=‘value’ (event)=‘handler()’ -> on-event=‘handler()’ [()]
29 [property]=‘value’ -> property=‘value’ (event)=‘handler()’ -> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
30 bind-property=‘value’ -> property=‘value’ (event)=‘handler()’ -> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
31 bind-property=‘value’ -> property=‘value’ on-event=‘handler()’ -> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
32 bind-property=‘value’ -> property=‘value’ on-event=‘handler()’ -> on-event=‘handler()’ bindon-prop=‘value’ -> on-change=‘update()’ -> target=‘value’
33 <hero-detail *ngIf="currentHero" [hero]="currentHero"/>
34 <hero-detail template="ngIf:currentHero" [hero]="currentHero"/>
35 <template [ngIf]="currentHero"> <hero-detail [hero]="currentHero"></hero-detail> </template>
System.js & JSPM & System.js Builder 36 http://plnkr.co/
System.js & JSPM & System.js Builder 37 <title>angular2 playground</title> <link rel="stylesheet" href="style.css" /> <script src="https://code.angularjs.org/2.0.0-beta.17/ angular2-polyfills.js"></script> <script src="https://code.angularjs.org/tools/system.js"></ script> <script src="https://code.angularjs.org/tools/ typescript.js"></script> <script src="config.js"></script> <script> System.import('app') .catch(console.error.bind(console)); </script> </head>
System.js & JSPM & System.js Builder 38 <title>angular2 playground</title> <link rel="stylesheet" href="style.css" /> <script src="https://code.angularjs.org/2.0.0-beta.17/ angular2-polyfills.js"></script> <script src="https://code.angularjs.org/tools/system.js"></ script> <script src="https://code.angularjs.org/tools/ typescript.js"></script> <script src="config.js"></script> <script> System.import('app') .catch(console.error.bind(console)); </script> </head>
39 System.config({ //use typescript for compilation transpiler: 'typescript', //typescript compiler options typescriptOptions: { emitDecoratorMetadata: true }, //map tells the System loader where to look for things map: { app: "./src", '@angular': 'https://npmcdn.com/@angular', 'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6' }, //packages defines our app package packages: { app: { main: './main.ts', defaultExtension: 'ts' }, '@angular/core': { main: 'core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'platform-browser.umd.js', defaultExtension: 'js' }, rxjs: { defaultExtension: 'js' } } });
40 System.config({ //use typescript for compilation transpiler: 'typescript', //typescript compiler options typescriptOptions: { emitDecoratorMetadata: true }, //map tells the System loader where to look for things map: { app: "./src", '@angular': 'https://npmcdn.com/@angular', 'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6' }, //packages defines our app package packages: { app: { main: './main.ts', defaultExtension: 'ts' }, '@angular/core': { main: 'core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'platform-browser.umd.js', defaultExtension: 'js' }, rxjs: { defaultExtension: 'js' } } }); 44 строчки конфига
Не хуже чем Webpack • Официальный Angular QuickStart репозиторий • Angular CLI • Yoman / Slush - генераторы
TypeScript 42
TypeScript 43
TypeScript 44
TypeScript 45 • .Net, Java, Scala background • SOLID, Design Patterns • 1.8.10 • Poor documentation - «google search ftw»
Глава №3 - Добыча 46
Размер 47 Development Production Size in KB 0 50 100 150 200 250 300 350 400 React Angular 1 Angular 2
Размер - Angular 2 Router 48
Размер - Angular 2 Router 49
Размер - Angular 2 Router 50
CSS 51 import {Component} from '@angular/core' @Component({ selector: 'my-app', providers: [], styles: [` h2 { color: red; } `], template: ` <div> <h2>Hello {{name}}</h2> </div> `, directives: [] }) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' } }
CSS 52 import {Component} from '@angular/core' @Component({ selector: 'my-app', providers: [], styles: [` body { color: red; } `], template: ` <div> <h2>Hello {{name}}</h2> </div> `, directives: [] }) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' } }
CSS 53
Speed - templates 54
55 import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
56 new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}) ] ; var renderType_PROJECTNAMEAppComponent = null; function _View_PROJECTNAMEAppComponent0(viewUtils,parentInjector,declarationEl) { var self = this; jit_DebugAppView1.call(this, _View_PROJECTNAMEAppComponent0,renderType_PROJECTNAMEAppComponent,jit_ViewType_COMPONENT2,viewUtils,parentInjec rationEl,jit_ChangeDetectionStrategy_CheckAlways3,nodeDebugInfos_PROJECTNAMEAppComponent0); } _View_PROJECTNAMEAppComponent0.prototype = Object.create(jit_DebugAppView1.prototype); _View_PROJECTNAMEAppComponent0.prototype.createInternal = function(rootSelector) { var self = this; var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement); self._el_0 = self.renderer.createElement(parentRenderNode,'h1',self.debug(0,0,0)); self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,4)); self._text_2 = self.renderer.createText(parentRenderNode,'n',self.debug(2,2,5)); self._expr_0 = jit_uninitialized4; self.init([],[ self._el_0, self._text_1, self._text_2 ] ,[],[]); return null; }; _View_PROJECTNAMEAppComponent0.prototype.detectChangesInternal = function(throwOnChange) { var self = this; self.detectContentChildrenChanges(throwOnChange); self.debug(1,0,4);
57 new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}) ] ; var renderType_PROJECTNAMEAppComponent = null; function _View_PROJECTNAMEAppComponent0(viewUtils,parentInjector,declarationEl) { var self = this; jit_DebugAppView1.call(this, _View_PROJECTNAMEAppComponent0,renderType_PROJECTNAMEAppComponent,jit_ViewType_COMPONENT2,viewUtils,parentInjec rationEl,jit_ChangeDetectionStrategy_CheckAlways3,nodeDebugInfos_PROJECTNAMEAppComponent0); } _View_PROJECTNAMEAppComponent0.prototype = Object.create(jit_DebugAppView1.prototype); _View_PROJECTNAMEAppComponent0.prototype.createInternal = function(rootSelector) { var self = this; var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement); self._el_0 = self.renderer.createElement(parentRenderNode,'h1',self.debug(0,0,0)); self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,4)); self._text_2 = self.renderer.createText(parentRenderNode,'n',self.debug(2,2,5)); self._expr_0 = jit_uninitialized4; self.init([],[ self._el_0, self._text_1, self._text_2 ] ,[],[]); return null; }; _View_PROJECTNAMEAppComponent0.prototype.detectChangesInternal = function(throwOnChange) { var self = this; self.detectContentChildrenChanges(throwOnChange); self.debug(1,0,4);
58 self.renderer.createElement self.renderer.createText
59 Как построить DOM Роман Дворнов
Speed immutability 60 import { Component, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
Speed immutability 61 import { Component, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
Speed immutability 62 import { Component, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
Speed immutability 63 import { Component, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: ‘isOdd’, is_pure: false }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
Speed immutability 64 import { Component, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list = this.list.splice().filter((i) => i % 2) }) } }
Speed - zone.js 65 Zone.fork().run(function () { zone.inTheZone = true; setTimeout(function () { console.log('in the zone: ' + !!zone.inTheZone); }, 0); }); console.log('in the zone: ' + !!zone.inTheZone); ————————————————————————————— 'in the zone: false' 'in the zone: true'
Speed - zone.js 66 Zone.fork().run(function () { zone.inTheZone = true; setTimeout(function () { console.log('in the zone: ' + !!zone.inTheZone); }, 0); }); console.log('in the zone: ' + !!zone.inTheZone); ————————————————————————————— 'in the zone: false' 'in the zone: true'
TypeScript OOP 67 class GenericService<T> { items: Array<T> = [] addItem(item: T) { this.items.push(item) } } interface User { id: number, name: string } interface Creatives { type: string, value: string }
TypeScript OOP 68 var s = new GenericService<User>(); s.addItem({ id: 1, name: 'asda' }); s.addItem({ type: ‘asda' // will fail })
Глава №4 - Первые потери 69 … а вот этого я не ожидал
Потеря почти всей кодовой базы 70
Promise -> RXJS 71
Promise -> RXJS 72 ЗДЕСЬ БЫЛ PROMISE
Promise -> RXJS 73 import {Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
Promise -> RXJS 74 import {Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
Promise -> RXJS 75 import {Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
RXJS 76
RXJS 77 interface IObservable<T> { IDisposable Subscribe(IObserver observer); } interface IObserver<T> { void OnCompleted(); void OnNext(T value); void OnError(Exception e); }
ngResources 78 var User = $resource('/user/:userId', {userId:'@id'}); User.get({userId:123}, function(user) { user.abc = true; user.$save(); });
ngResources 79 var User = $resource('/user/:userId', {userId:'@id'}); User.get({userId:123}, function(user) { user.abc = true; user.$save(); });
Встроенные паттерны канули в небытие! 80 1) component 2) directive 3) filter 4) service 5) provider 6) constant 7) config 8) run 9) module
Встроенные паттерны канули в небытие! 81 1) component 2) template 3) directive 4) route 5) pipe 6) service *
Формы 82 1) [(ngModel)]
Формы 83 1) [(ngModel)] 2) ng-valid | ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched
Формы 84 1) [(ngModel)] 2) ng-valid | ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched 3) FormModel + FormBuilder
Формы 85 1) [(ngModel)] 2) ng-valid | ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched 3) FormModel + FormBuilder 4) Валидация не стала легче
Глава №5 - Happy End 86
Мы уже переехали на Angular 2? 87
НЕТ 88
Почему? 89 1) Потому что Angular 1 не так уж и плох, а если задуматься …
Почему? 90 1) Потому что Angular 1 не так уж и плох, а если задуматься … 2) Потому что React 15 не так уж и плох, а если задуматься …
Почему? 91 1) Потому что Angular 1 не так уж и плох, а если задуматься … 2) Потому что React 15 не так уж и плох, а если задуматься … 3) Потому что Ember не так уж и плох, а если задуматься …
Почему? 92 1) Кодовая база 2) Уровень вхождения 3) Незаконченность*
Почему? 93 1) Кодовая база 2) Уровень вхождения 3) Незаконченность*
Есть ли надежда? 94
Наши шаги 95 1) TypeScript OOP - e2e tests 2) Angular 1.4.x -> 1.5.x 3) AutoNgConverter
О чем мы не поговорили? 96
97 Progressive Web Apps Native - Ionic Framework, - NativeScript - React Native. Desktop - Electron Universal - node.js, - .NET, - PHP Dependency Injection Angular CLI IDEs Testing - patched Karma, Protractor Animation Accessibility Developer Tools
98 -Redux ( ngrx / ng2-redux ) -FLUX -MV* ( MVC, MVP, MVVM ) -MALEVICH ( COD.js )
Приятного аппетита! Tweeter: #Ai_boy Gitter: aiboy 99 http://bit.ly/1XP0dEh

Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей Охрименко (IPONWEB)

  • 1.
    Angular 2 нетак уж и плох, а если задуматься то и просто хорош. Алексей Охрименко ( IPONWEB ) 1
  • 2.
  • 3.
  • 4.
  • 5.
    5 import { Component} from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
  • 6.
    6 import { Component} from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
  • 7.
    7 import { Component} from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1 (click)='onClick()'> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
  • 8.
    Глава №1 -И была рука 8
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
    Глава №2 -Горы отвращения 13
  • 14.
    Angular 2 isBeta* 14 * - now RC1
  • 15.
    ASP.Net MVC -Developer Preview 2 15
  • 16.
  • 17.
    17 ([]) [] () ([])[] () ([]) [] ([]) [] () ([] ([]) [] ([]) [] () ([])
  • 18.
  • 19.
    19 /** * Simplest possibletemplate in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
  • 20.
    20 /** * Simplest possibletemplate in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
  • 21.
    21 /** * Simplest possibletemplate in AngularJs-ISH style * * @param {String} template - template string * @param {Object} ctx - template context * @param {Object} eventHandlerObject - object that will be used as "this" in event handling * @returns {Node} returns dom node element */ export default function angularish(template, ctx, eventHandlerObject) { var node; var container = document.createElement('div'); container.innerHTML = template; var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, null, false); while (node = walker.nextNode()) { // inheritance of context node.ctx = node.ctx || node.parentNode.ctx || ctx; // ng-scope allows you to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) {
  • 22.
    22 node.value = _getValue(node.ctx,node.getAttribute('ng-value')); } // ng-selected will set selected attribute depending on true-finess of value if (node.getAttribute('ng-selected')) { var selected = _getValue(node.ctx, node.getAttribute('ng-selected')); if (selected) { node.setAttribute('selected', 'yes'); } } // ng-text will assign text to node no need for escaping if (node.getAttribute('ng-text')) { node.innerText = _getValue(node.ctx, node.getAttribute('ng-text')); } // ng-class will simply assign class from defined property if (node.getAttribute('ng-class')) { var classVal = _getValue(node.ctx, node.getAttribute('ng-class')); if (classVal) { node.className += ' ' + classVal; } } // ng-show shows elements depending on true-finess of the value if (node.getAttribute('ng-show')) {
  • 23.
    23 // ng-scope allowsyou to change scope of the node (new scope can be any property of old scope) if (node.getAttribute('ng-scope')) { node.ctx = _getValue(node.ctx, node.getAttribute('ng-scope')); } // ng-loop will repeat first child (TODO: reapeat content) and assign correct context if (node.getAttribute('ng-loop')) { var child = node.children[0]; var array = _getValue(node.ctx, node.getAttribute('ng-loop')) || []; node.removeChild(child); array.forEach((item) => { child = child.cloneNode(true); child.ctx = item; node.appendChild(child); }); } // ng-value will assign value to node if (node.getAttribute('ng-value')) { node.value = _getValue(node.ctx, node.getAttribute('ng-value')); } // ng-selected will set selected attribute depending on true-finess of value if (node.getAttribute('ng-selected')) {
  • 24.
    24 // ng-change willadd "change" event handler if (node.getAttribute('ng-change')) { // closure to rescue ((node)=> { node.addEventListener('change', (event) => { eventHandlerObject[node.getAttribute(‘ng-change')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } // ng-click will add "click" event handler if (node.getAttribute('ng-click')) { // closure to rescue ((node)=> { node.addEventListener('click', (event) => { eventHandlerObject[node.getAttribute(‘ng-click')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } } return container; } function _getValue(ctx, attrVal) { if (attrVal === 'self') { return ctx; }
  • 25.
    25 } // ng-hide showselements depending on false-iness of the value if (node.getAttribute('ng-hide')) { var isHidden = _getValue(node.ctx, node.getAttribute('ng-hide')); if (isHidden) { node.style.display = 'none'; } } // ng-change will add "change" event handler if (node.getAttribute('ng-change')) { // closure to rescue ((node)=> { node.addEventListener('change', (event) => { eventHandlerObject[node.getAttribute(‘ng-change')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); } // ng-click will add "click" event handler if (node.getAttribute('ng-click')) { // closure to rescue ((node)=> { node.addEventListener('click', (event) => { eventHandlerObject[node.getAttribute(‘ng-click')] .bind(eventHandlerObject)(node.ctx, event); }, true); })(node); }
  • 26.
  • 27.
  • 28.
  • 29.
    29 [property]=‘value’ -> property=‘value’ (event)=‘handler()’-> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
  • 30.
    30 bind-property=‘value’ -> property=‘value’ (event)=‘handler()’-> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
  • 31.
    31 bind-property=‘value’ -> property=‘value’ on-event=‘handler()’-> on-event=‘handler()’ [(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
  • 32.
    32 bind-property=‘value’ -> property=‘value’ on-event=‘handler()’-> on-event=‘handler()’ bindon-prop=‘value’ -> on-change=‘update()’ -> target=‘value’
  • 33.
  • 34.
  • 35.
  • 36.
    System.js & JSPM& System.js Builder 36 http://plnkr.co/
  • 37.
    System.js & JSPM& System.js Builder 37 <title>angular2 playground</title> <link rel="stylesheet" href="style.css" /> <script src="https://code.angularjs.org/2.0.0-beta.17/ angular2-polyfills.js"></script> <script src="https://code.angularjs.org/tools/system.js"></ script> <script src="https://code.angularjs.org/tools/ typescript.js"></script> <script src="config.js"></script> <script> System.import('app') .catch(console.error.bind(console)); </script> </head>
  • 38.
    System.js & JSPM& System.js Builder 38 <title>angular2 playground</title> <link rel="stylesheet" href="style.css" /> <script src="https://code.angularjs.org/2.0.0-beta.17/ angular2-polyfills.js"></script> <script src="https://code.angularjs.org/tools/system.js"></ script> <script src="https://code.angularjs.org/tools/ typescript.js"></script> <script src="config.js"></script> <script> System.import('app') .catch(console.error.bind(console)); </script> </head>
  • 39.
    39 System.config({ //use typescript forcompilation transpiler: 'typescript', //typescript compiler options typescriptOptions: { emitDecoratorMetadata: true }, //map tells the System loader where to look for things map: { app: "./src", '@angular': 'https://npmcdn.com/@angular', 'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6' }, //packages defines our app package packages: { app: { main: './main.ts', defaultExtension: 'ts' }, '@angular/core': { main: 'core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'platform-browser.umd.js', defaultExtension: 'js' }, rxjs: { defaultExtension: 'js' } } });
  • 40.
    40 System.config({ //use typescript forcompilation transpiler: 'typescript', //typescript compiler options typescriptOptions: { emitDecoratorMetadata: true }, //map tells the System loader where to look for things map: { app: "./src", '@angular': 'https://npmcdn.com/@angular', 'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6' }, //packages defines our app package packages: { app: { main: './main.ts', defaultExtension: 'ts' }, '@angular/core': { main: 'core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'platform-browser.umd.js', defaultExtension: 'js' }, rxjs: { defaultExtension: 'js' } } }); 44 строчки конфига
  • 41.
    Не хуже чемWebpack • Официальный Angular QuickStart репозиторий • Angular CLI • Yoman / Slush - генераторы
  • 42.
  • 43.
  • 44.
  • 45.
    TypeScript 45 • .Net, Java,Scala background • SOLID, Design Patterns • 1.8.10 • Poor documentation - «google search ftw»
  • 46.
    Глава №3 -Добыча 46
  • 47.
    Размер 47 Development Production Size in KB 050 100 150 200 250 300 350 400 React Angular 1 Angular 2
  • 48.
  • 49.
  • 50.
  • 51.
    CSS 51 import {Component} from'@angular/core' @Component({ selector: 'my-app', providers: [], styles: [` h2 { color: red; } `], template: ` <div> <h2>Hello {{name}}</h2> </div> `, directives: [] }) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' } }
  • 52.
    CSS 52 import {Component} from'@angular/core' @Component({ selector: 'my-app', providers: [], styles: [` body { color: red; } `], template: ` <div> <h2>Hello {{name}}</h2> </div> `, directives: [] }) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' } }
  • 53.
  • 54.
  • 55.
    55 import { Component} from '@angular/core'; @Component({ moduleId: module.id, selector: 'project-name-app', template: ` <h1> {{title}} </h1> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { title = 'project-name works!'; }
  • 56.
    56 new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}), newjit_StaticNodeDebugInfo0([],null,{}) ] ; var renderType_PROJECTNAMEAppComponent = null; function _View_PROJECTNAMEAppComponent0(viewUtils,parentInjector,declarationEl) { var self = this; jit_DebugAppView1.call(this, _View_PROJECTNAMEAppComponent0,renderType_PROJECTNAMEAppComponent,jit_ViewType_COMPONENT2,viewUtils,parentInjec rationEl,jit_ChangeDetectionStrategy_CheckAlways3,nodeDebugInfos_PROJECTNAMEAppComponent0); } _View_PROJECTNAMEAppComponent0.prototype = Object.create(jit_DebugAppView1.prototype); _View_PROJECTNAMEAppComponent0.prototype.createInternal = function(rootSelector) { var self = this; var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement); self._el_0 = self.renderer.createElement(parentRenderNode,'h1',self.debug(0,0,0)); self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,4)); self._text_2 = self.renderer.createText(parentRenderNode,'n',self.debug(2,2,5)); self._expr_0 = jit_uninitialized4; self.init([],[ self._el_0, self._text_1, self._text_2 ] ,[],[]); return null; }; _View_PROJECTNAMEAppComponent0.prototype.detectChangesInternal = function(throwOnChange) { var self = this; self.detectContentChildrenChanges(throwOnChange); self.debug(1,0,4);
  • 57.
    57 new jit_StaticNodeDebugInfo0([],null,{}), new jit_StaticNodeDebugInfo0([],null,{}), newjit_StaticNodeDebugInfo0([],null,{}) ] ; var renderType_PROJECTNAMEAppComponent = null; function _View_PROJECTNAMEAppComponent0(viewUtils,parentInjector,declarationEl) { var self = this; jit_DebugAppView1.call(this, _View_PROJECTNAMEAppComponent0,renderType_PROJECTNAMEAppComponent,jit_ViewType_COMPONENT2,viewUtils,parentInjec rationEl,jit_ChangeDetectionStrategy_CheckAlways3,nodeDebugInfos_PROJECTNAMEAppComponent0); } _View_PROJECTNAMEAppComponent0.prototype = Object.create(jit_DebugAppView1.prototype); _View_PROJECTNAMEAppComponent0.prototype.createInternal = function(rootSelector) { var self = this; var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement); self._el_0 = self.renderer.createElement(parentRenderNode,'h1',self.debug(0,0,0)); self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,4)); self._text_2 = self.renderer.createText(parentRenderNode,'n',self.debug(2,2,5)); self._expr_0 = jit_uninitialized4; self.init([],[ self._el_0, self._text_1, self._text_2 ] ,[],[]); return null; }; _View_PROJECTNAMEAppComponent0.prototype.detectChangesInternal = function(throwOnChange) { var self = this; self.detectContentChildrenChanges(throwOnChange); self.debug(1,0,4);
  • 58.
  • 59.
  • 60.
    Speed immutability 60 import { Component,Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
  • 61.
    Speed immutability 61 import { Component,Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
  • 62.
    Speed immutability 62 import { Component,Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
  • 63.
    Speed immutability 63 import { Component,Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: ‘isOdd’, is_pure: false }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list.push({ name: ‘test', isOdd: !!(this.list.length % 2) }) } }
  • 64.
    Speed immutability 64 import { Component,Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'isOdd' }) export class IsOddPipe implements PipeTransform { transform(array:any[]) { return array.filter(item => item.isOdd); } } @Component({ moduleId: module.id, selector: 'project-name-app', pipes: [IsOddPipe], template: ` <button (click)="add()">add</button> <div> <div *ngFor="let item of list | isOdd"> {{ item.name }} </div> </div> `, styleUrls: ['project-name.component.css'] }) export class PROJECTNAMEAppComponent { list = [] add() { this.list = this.list.splice().filter((i) => i % 2) }) } }
  • 65.
    Speed - zone.js 65 Zone.fork().run(function() { zone.inTheZone = true; setTimeout(function () { console.log('in the zone: ' + !!zone.inTheZone); }, 0); }); console.log('in the zone: ' + !!zone.inTheZone); ————————————————————————————— 'in the zone: false' 'in the zone: true'
  • 66.
    Speed - zone.js 66 Zone.fork().run(function() { zone.inTheZone = true; setTimeout(function () { console.log('in the zone: ' + !!zone.inTheZone); }, 0); }); console.log('in the zone: ' + !!zone.inTheZone); ————————————————————————————— 'in the zone: false' 'in the zone: true'
  • 67.
    TypeScript OOP 67 class GenericService<T>{ items: Array<T> = [] addItem(item: T) { this.items.push(item) } } interface User { id: number, name: string } interface Creatives { type: string, value: string }
  • 68.
    TypeScript OOP 68 var s= new GenericService<User>(); s.addItem({ id: 1, name: 'asda' }); s.addItem({ type: ‘asda' // will fail })
  • 69.
    Глава №4 -Первые потери 69 … а вот этого я не ожидал
  • 70.
    Потеря почти всейкодовой базы 70
  • 71.
  • 72.
  • 73.
    Promise -> RXJS 73 import{Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
  • 74.
    Promise -> RXJS 74 import{Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
  • 75.
    Promise -> RXJS 75 import{Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: 'http-app', viewProviders: [HTTP_PROVIDERS], templateUrl: 'people.html' }) class PeopleComponent { constructor(http: Http) { http.get('people.json') .map(res => res.json()) .subscribe(people => this.people = people); } }
  • 76.
  • 77.
    RXJS 77 interface IObservable<T> { IDisposable Subscribe(IObserverobserver); } interface IObserver<T> { void OnCompleted(); void OnNext(T value); void OnError(Exception e); }
  • 78.
    ngResources 78 var User =$resource('/user/:userId', {userId:'@id'}); User.get({userId:123}, function(user) { user.abc = true; user.$save(); });
  • 79.
    ngResources 79 var User =$resource('/user/:userId', {userId:'@id'}); User.get({userId:123}, function(user) { user.abc = true; user.$save(); });
  • 80.
    Встроенные паттерны канулив небытие! 80 1) component 2) directive 3) filter 4) service 5) provider 6) constant 7) config 8) run 9) module
  • 81.
    Встроенные паттерны канулив небытие! 81 1) component 2) template 3) directive 4) route 5) pipe 6) service *
  • 82.
  • 83.
    Формы 83 1) [(ngModel)] 2) ng-valid| ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched
  • 84.
    Формы 84 1) [(ngModel)] 2) ng-valid| ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched 3) FormModel + FormBuilder
  • 85.
    Формы 85 1) [(ngModel)] 2) ng-valid| ng-invalid | ng-dirty | ng-pristine | ng-touched | ng-untouched 3) FormModel + FormBuilder 4) Валидация не стала легче
  • 86.
    Глава №5 -Happy End 86
  • 87.
  • 88.
  • 89.
    Почему? 89 1) Потому чтоAngular 1 не так уж и плох, а если задуматься …
  • 90.
    Почему? 90 1) Потому чтоAngular 1 не так уж и плох, а если задуматься … 2) Потому что React 15 не так уж и плох, а если задуматься …
  • 91.
    Почему? 91 1) Потому чтоAngular 1 не так уж и плох, а если задуматься … 2) Потому что React 15 не так уж и плох, а если задуматься … 3) Потому что Ember не так уж и плох, а если задуматься …
  • 92.
    Почему? 92 1) Кодовая база 2)Уровень вхождения 3) Незаконченность*
  • 93.
    Почему? 93 1) Кодовая база 2)Уровень вхождения 3) Незаконченность*
  • 94.
  • 95.
    Наши шаги 95 1) TypeScriptOOP - e2e tests 2) Angular 1.4.x -> 1.5.x 3) AutoNgConverter
  • 96.
    О чем мыне поговорили? 96
  • 97.
    97 Progressive Web Apps Native -Ionic Framework, - NativeScript - React Native. Desktop - Electron Universal - node.js, - .NET, - PHP Dependency Injection Angular CLI IDEs Testing - patched Karma, Protractor Animation Accessibility Developer Tools
  • 98.
    98 -Redux ( ngrx/ ng2-redux ) -FLUX -MV* ( MVC, MVP, MVVM ) -MALEVICH ( COD.js )
  • 99.