Angular конфигурируем до неузнаваемости 1
АЛЕКСЕЙ ОХРИМЕНКО TWITTER: AI_BOY setinterval(learnjavascript) 2
?
IPONWEB 9
10
Awesome Telegram RU https://github.com/aiboy/awesome-telegram-ru
Какое важное событие было на прошлой неделе?
5 лет
Angular >= 2.x AngularJs < 2.x
4.2.3
Angular, Angular нас и [НАШ_FRAMEWORK] неплохо кормит 17
18
Angular
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!'; 20
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!'; 21
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!'; 22
Синтаксис Шаблонов 25
/** * 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')) { 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')) { 26
/** * 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')) { 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')) { 27
/** * 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')) { 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')) { 28
// 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')) { var isvisible = _getvalue(node.ctx, node.getattribute('ng-show')); if (!isvisible) { node.style.display = 'none'; // 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'; 29
// 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')) { 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 30 property if (node.getattribute('ng-class')) {
// 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); return container; function _getvalue(ctx, attrval) { if (attrval === 'self') { return ctx; return ctx[attrval]; 31
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; return ctx[attrval]; 32
[] () [()] 33
[property]= value -> property= value () [()] 34
[property]= value -> property= value (event)= handler() -> on-event= handler() [()] 35
[property]= value -> property= value (event)= handler() -> on-event= handler() [(target)]= value -> on-change= update() -> target= value 36
bind-property= value -> property= value (event)= handler() -> on-event= handler() [(target)]= value -> on-change= update() -> target= value 37
bind-property= value -> property= value on-event= handler() -> on-event= handler() [(target)]= value -> on-change= update() -> target= value 38
bind-property= value -> property= value on-event= handler() -> on-event= handler() bindon-prop= value -> on-change= update() -> target= value 39
class MovieItem extends React.Component { componentdidmount() { this.nv.addeventlistener("nv-enter", this.handlenventer); componentwillunmount() { this.nv.removeeventlistener("nv-enter", this.handlenventer); handlenventer = (event) => { console.log("nv Enter:", event);
<div (nv-enter)="handleevent()"></div>
Как конфигурировать? 43
Как конфигурировать? «Встроенные» настройки 44
Как конфигурировать? «Встроенные» настройки Dependency Injection (Providers) 45
Как конфигурировать? «Встроенные» настройки Dependency Injection (Providers) Platforms 46
Что такое DI? S.O.L.I.D-ый JavaScript https://www.youtube.com/watch?v=wi3wpzrekzq
class Car { constructor() { this.engine = new Engine(); this.tires = Tires.getInstance(); this.doors = app.get('doors');
class Car { constructor(engine, tires, doors) { this.engine = engine; this.tires = tires; this.doors = doors;
var car = new Car( new Engine(), new Tires(), new Doors() ); var car = new Car( new MockEngine(), new MockTires(), new MockDoors() );
Angular Dependency Injection
var injector = Injector.resolveAndCreate([ Car, Engine, Tires, Doors ]);
var injector = Injector.resolveAndCreate([ { provide: Car, useclass: Car, { provide: Engine, useclass: Engine, { provide: Tires, useclass: Tires, { provide: Doors, useclass: Doors ]);
import { Inject from 'angular2/core'; class Car { constructor( @Inject(Engine) engine, @Inject(Tires) tires, @Inject(Doors) doors ) {...
Можно ли заменить DI или вообще им не пользоваться? 55
НЕТ @angular/core 56
Angular Componen
Angular Componen Module
Angular Componen Module Platform
Angular Componen Module Module Com Platform
WebWorkers 61
import {bootstrapworkerui from '@angular/platform-webworker'; import {enableprodmode from '@angular/core'; export function main() { enableprodmode(); bootstrapworkerui('loader.js'); 62
import {bootstrapworkerui from '@angular/platform-webworker'; import {enableprodmode from '@angular/core'; export function main() { enableprodmode(); bootstrapworkerui('loader.js'); 63
@NgModule({ imports: [WorkerAppModule], bootstrap: [AppComponent], declarations: [AppComponent] ) class WebWorkerModule { export function main() { enableprodmode(); platformworkerappdynamic().bootstrapmodule(webworkermodule); 64
@NgModule({ imports: [WorkerAppModule], bootstrap: [AppComponent], declarations: [AppComponent] ) class WebWorkerModule { export function main() { enableprodmode(); platformworkerappdynamic().bootstrapmodule(webworkermodule); 65
https://docs.nativescript.org/tutorial/ng-chapter-0 import { Component from "@angular/core"; @Component({ selector: "my-app", template: ` <ActionBar title="my Apple" class="action-bar"></actionbar> <Image src="~/images/apple.jpg"></image> `, styles: [` @keyframes spin { from { transform: rotate(0); to { transform: rotate(360); Image { animation-name: spin; animation-duration: 3s; animation-iteration-count: infinite; animation-timing-function: linear; `] ) export class AppComponent {
https://github.com/vakrilov/native-scriptaccelerometer/blob/master/index.ios.ts var queue = NSOperationQueue.alloc().init(); accmanager.startaccelerometerupdatestoqueuewithhandler(queue, (data, error) => { dispatch_async(main_queue, () => { wrappedcallback({ x: data.acceleration.x, y: data.acceleration.y, z: data.acceleration.z ) ) );
https://github.com/vakrilov/native-scriptaccelerometer/blob/master/index.ios.ts var queue = NSOperationQueue.alloc().init(); accmanager.startaccelerometerupdatestoqueuewithhandler(queue, (data, error) => { dispatch_async(main_queue, () => { wrappedcallback({ x: data.acceleration.x, y: data.acceleration.y, z: data.acceleration.z ) ) );
HTML 70
PUG/Jade Mustache STAN etc 71
module.exports = { // your config settings... module: [ //your modules... loaders: [ { test: /\.pug/, loader: 'pug-html', query: { doctype: 'html', plugins: [require('pug-plugin-ng')],, ] ] ; @Component({ selector: 'app', templateurl: './app.template.pug' ) https://github.com/tycho01/pug-plugin-ng 72
module.exports = { // your config settings... module: [ //your modules... loaders: [ { test: /\.pug/, loader: 'pug-html', query: { doctype: 'html', plugins: [require('pug-plugin-ng')],, ] ] ; @Component({ selector: 'app', templateurl: './app.template.pug' ) https://github.com/tycho01/pug-plugin-ng 73
ng-container([ngswitch]='type') template([ngswitchcase]=`'object'`) object([val]=`val` [schema]=`new_schema` [named]=`false`) template([ngswitchcase]=`'array'`) array([val]=`val` [schema]=`new_schema` [named]=`false`) template([ngswitchcase]=`'scalar'`) ng-container([ngswitch]=`ishtml(val)`) template([ngswitchcase]=`true`) myiframe([val]=`val`) template(ngswitchdefault).scalar([innerhtml]=`val scalar:new_schema`) template(ngswitchdefault) p UNIMPLEMENTED OUTPUT-VALUE TYPE ({{ type json )! 74
<div class="blue-box"> <div class="blue-box-title">{{boxtitle</div> {{define "bob"this is the template bob{{end <span> <ng-content></ng-content> </span> </div> 75
<div class="blue-box"> <div class="blue-box-title">{{boxtitle</div> {{define "bob"this is the template bob{{end <span> <ng-content></ng-content> </span> </div> 76
<div class="blue-box"> <div class="blue-box-title">{{boxtitle</div> {{define "bob"this is the template bob{{end <span> <ng-content></ng-content> </span> </div> ERROR 77
<div class="blue-box"> <div class="blue-box-title">{{boxtitle</div> <div ngnonbindable> {{define "bob"this is the template bob{{end </div> <span> <ng-content></ng-content> </span> </div> 78
class Parser2 extends Parser { myinterpolationregexp = /\[\[([\s\s]*?)\]\]/g; // <- CUSTOMIZATION constructor(public _lexer: Lexer) { super(_lexer) splitinterpolation(input, location):splitinterpolation { var parts = StringWrapper.split(input, this.myinterpolationregexp); // code here return new SplitInterpolation(strings, expressions); private _findinterpolationerrorcolumn2(parts: string[], partinerridx: number): number { // code here return errlocation.length; 79
class Parser2 extends Parser { myinterpolationregexp = /\[\[([\s\s]*?)\]\]/g; // <- CUSTOMIZATION constructor(public _lexer: Lexer) { super(_lexer) splitinterpolation(input, location):splitinterpolation { var parts = StringWrapper.split(input, this.myinterpolationregexp); // code here return new SplitInterpolation(strings, expressions); private _findinterpolationerrorcolumn2(parts: string[], partinerridx: number): number { // code here return errlocation.length; 80
class Parser2 extends Parser { myinterpolationregexp = /\[\[([\s\s]*?)\]\]/g; // <- CUSTOMIZATION constructor(public _lexer: Lexer) { super(_lexer) splitinterpolation(input, location):splitinterpolation { var parts = StringWrapper.split(input, this.myinterpolationregexp); // code here return new SplitInterpolation(strings, expressions); private _findinterpolationerrorcolumn2(parts: string[], partinerridx: number): number { // code here return errlocation.length; 81
{ provide:parser, useclass: Parser2 82
<div>[[ title ]]</div> {{begin asdas {{#end <div> <div *ngfor="let item of list isodd"> [[ item.name ]] </div> </div> 83
<div>[[ title ]]</div> {{begin asdas {{#end <div> <div *ngfor="let item of list isodd"> [[ item.name ]] </div> </div> 84
CSS 85
import {Component from '@angular/core' @Component({ selector: 'my-app', providers: [], styles: [`.test { color: red; `], template: ` <div> <h2 class='test'>hello {{name</h2> </div> `, directives: [] ) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' 86
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!)' 87
88
ViewEncapsulation Emulated 89
ViewEncapsulation Emulated Native 90
ViewEncapsulation Emulated Native None 91
import {Component from '@angular/core' @Component({ selector: 'my-app', providers: [], encapsulation: ViewEncapsulation.None styles: [` body { color: red; `], template: ` <div> <h2>hello {{name</h2> </div> `, directives: [] ) export class App { constructor() { this.name = 'Angular2 (Release Candidate!)' 92
https://github.com/typestyle/typestyle 93
Language 94
TypeScript import { Component from '@angular/core'; @Component({ selector: 'my-app', template: `<h1>hello {{name</h1>` ) export class AppComponent { name = 'Angular';
ES2015 / ES6 import { Component from '@angular/core'; @Component({ selector: 'my-app', template: `<h1>hello {{name</h1>` ) export class AppComponent { name = 'Angular';
ES5 (function(app) { app.appcomponent = ng.core.component({ selector: 'my-app', template: '<h1>hello {{name</h1>' ).Class({ constructor: function() { this.name = 'Angular' ); )(window.app (window.app = {));
Dart
Speed 99
Zone.js 100
const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createserver((req, res) => { res.statuscode = 200; res.setheader('content-type', 'text/plain'); res.end('hello World'); ); server.listen(port, hostname, () => { console.log(`server running at http://${hostname:${port/`) ); 101
102
process.on('uncaughtexception', (err) => { console.log(`caught exception: ${err`); ); 103
104
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); settimeout(somecallback, 0); 105
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); settimeout(somecallback, 0); 106
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); settimeout(somecallback, 0); 107
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); settimeout(somecallback, 0); 108
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); // TRUE settimeout(somecallback, 0); 109
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); settimeout(somecallback, 0); 110
Zone.current.fork({).run(function () { Zone.current.inTheZone = true; settimeout(somecallback, 0); ); function somecallback() { console.log(zone.current.inthezone); // FALSE settimeout(somecallback, 0); 111
Change Detection 112
113
114
115
116
117
// very simplified version of actual source class ApplicationRef { changedetectorrefs:changedetectorref[] = []; constructor(private zone: NgZone) { this.zone.onturndone.subscribe(() => { this.zone.run(() => this.tick()) ); tick() { this.changedetectorrefs.foreach((ref) => ref.detectchanges()); 118
// very simplified version of actual source class ApplicationRef { changedetectorrefs:changedetectorref[] = []; constructor(private zone: NgZone) { this.zone.onturndone.subscribe(() => { this.zone.run(() => this.tick() ); tick() { this.changedetectorrefs.foreach((ref) => ref.detectchanges()); 119
// very simplified version of actual source class ApplicationRef { changedetectorrefs:changedetectorref[] = []; constructor(private zone: NgZone) { this.zone.onturndone.subscribe(() => { this.zone.run(() => this.tick() ); tick() { this.changedetectorrefs.foreach((ref) => ref.detectchanges()); 120
// very simplified version of actual source class ApplicationRef { changedetectorrefs:changedetectorref[] = []; constructor(private zone: NgZone) { this.zone.onturndone.subscribe(() => { this.zone.run(() => this.tick() ); tick() { this.changedetectorrefs.foreach((ref) => ref.detectchanges()); 121
122
@Component({ template: '<v-card [vdata]="vdata"></v-card>' ) class VCardApp { constructor() { this.vdata = { name: 'Christoph Burgdorf', email: 'christoph@thoughtram.io' changedata() { this.vdata.name = 'Pascal Precht'; 123
@Component({ template: ` <h2>{{vdata.name</h2> <span>{{vdata.email</span> ` ) class VCardCmp { @Input() vdata; 124
@Component({ template: '<v-card [vdata]="vdata"></v-card>' ) class VCardApp { constructor() { this.vdata = { name: 'Christoph Burgdorf', email: 'christoph@thoughtram.io' changedata() { this.vdata = { name: 'Pascal Precht' ; 125
@Component({ template: ` <h2>{{vdata.name</h2> <span>{{vdata.email</span> `, changedetection: ChangeDetectionStrategy.OnPush ) class VCardCmp { @Input() vdata; 126
127
Управляем Zone и CD 128
constructor(private zone: NgZone) { 129
processoutsideangularzone() { this.progress = 0; this.zone.runoutsideangular(() => { this.increaseprogress(() => { this.zone.run(() => { console.log('outside Done!'); ); ); ); 130
processoutsideangularzone() { this.progress = 0; this.zone.runoutsideangular(() => { this.increaseprogress(() => { this.zone.run(() => { console.log('outside Done!'); ); ); ); 131
constructor(private cd: ChangeDetectorRef) { 132
ngoninit() { this.additemstream.subscribe(() => { this.counter++; // application state changed this.cd.markforcheck(); // marks path ) 133
https://hackernoon.com/everything-you- need-to-know-about-change-detection- in-angular-8006c51d206f 134
ROUTER
export class CustomUrlSerializer implements UrlSerializer { /** Parses a url into a {@link UrlTree */ parse(url: string): UrlTree { const p = new CustomUrlParser(url); const UrlTree2 = UrlTree; return eval('new UrlTree2(p.parseRootSegment(), p.parsequeryparams(), p.parsefragme /** Converts a {@link UrlTree into a url */ serialize(tree: UrlTree): string { const segment = `/${serializesegment(tree.root, true)`; const query = serializequeryparams(tree.queryparams); const fragment = tree.fragment!== null && tree.fragment!== undefined? `#${encodeuri(tree.frag return `${segment${query${fragment`;
providers: [ {provide: UrlSerializer, useclass: CustomUrlSerializer, ],
THE END 138
АЛЕКСЕЙ http://bit.ly/2fuszfj ОХРИМЕНКО TWITTER: AI_BOY 139