Angularконфигурируем до неузнаваемости
1
АЛЕКСЕЙ ОХРИМЕНКО
TWITTER: AI_BOY
setInterval(learnJavaScript)2
?
IPONWEB
9
10
Awesome Telegram RUhttps://github.com/aiboy/awesome-telegram-ru
Какое важное событие было на прошлой
неделе?
5 лет
Angular >= 2.xAngularJs < 2.x
4.2.3
Angular, Angular… нас и [НАШ_FRAMEWORK]
неплохо кормит
17
18
Angular
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
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!'; }
Синтаксис Шаблонов
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')) {
node.value = _getValue(node.ctx, 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')) {
node.value = _getValue(node.ctx, 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')) {
node.value = _getValue(node.ctx, 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
30
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')) {
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')) {
31
// 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]; }
32
// 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]; }
33
[]
()
[()]
34
[property]=‘value’ -> property=‘value’
()
[()]
35
[property]=‘value’ -> property=‘value’
(event)=‘handler()’ -> on-event=‘handler()’
[()]
36
[property]=‘value’ -> property=‘value’
(event)=‘handler()’ -> on-event=‘handler()’
[(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
37
bind-property=‘value’ -> property=‘value’
(event)=‘handler()’ -> on-event=‘handler()’
[(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
38
bind-property=‘value’ -> property=‘value’
on-event=‘handler()’ -> on-event=‘handler()’
[(target)]=‘value’ -> on-change=‘update()’ -> target=‘value’
39
bind-property=‘value’ -> property=‘value’
on-event=‘handler()’ -> on-event=‘handler()’
bindon-prop=‘value’ -> on-change=‘update()’ -> target=‘value’
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?
https://www.youtube.com/watch?v=wi3wPzReKZQ
S.O.L.I.D-ый JavaScript
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
ModuleComponen
Angular
ModuleComponen
Platform
Angular
ModuleComponen
Platform
ModuleCom
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-script-accelerometer/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-script-accelerometer/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
https://github.com/tycho01/pug-plugin-ng
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' })
72
https://github.com/tycho01/pug-plugin-ng
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' })
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
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
100
Zone.js
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: '[email protected]' } }
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: '[email protected]' } }
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.parseFragment())'); }
/** 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.fragment)}` : ''; return `${segment}${query}${fragment}`; } }
providers: [ {provide: UrlSerializer, useClass: CustomUrlSerializer}, ],
THE END
138