Skip to content

Commit 760723c

Browse files
author
mivanov
committed
feat(change-detection-strategy-default-async): update readme.md
1 parent 629ddcf commit 760723c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1069
-1
lines changed

.angular-cli.json

+24
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,30 @@
7575
"dev": "environments/environment.ts",
7676
"prod": "environments/environment.prod.ts"
7777
}
78+
},
79+
{
80+
"root": "change-detection-strategy-default-async-pipe",
81+
"outDir": "dist",
82+
"assets": [
83+
"assets",
84+
"favicon.ico"
85+
],
86+
"index": "index.html",
87+
"main": "main.ts",
88+
"polyfills": "polyfills.ts",
89+
"test": "test.ts",
90+
"tsconfig": "tsconfig.app.json",
91+
"testTsconfig": "tsconfig.spec.json",
92+
"prefix": "",
93+
"styles": [
94+
"styles.css"
95+
],
96+
"scripts": [],
97+
"environmentSource": "environments/environment.ts",
98+
"environments": {
99+
"dev": "environments/environment.ts",
100+
"prod": "environments/environment.prod.ts"
101+
}
78102
}
79103
],
80104
"e2e": {

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ $ ng serve --app 2 --port 4202
5454
<details>
5555
<summary>Learn more</summary>
5656
<div><br>
57-
<img src="">
57+
<img src="https://habrastorage.org/webt/p5/ac/hg/p5achg7jybtcquownvjxohcs5ck.gif" />
5858
<pre>
5959
$ ng serve --app 3 --port 4203
6060
</pre>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.tick {
2+
text-align: center;
3+
}
4+
5+
6+
.tick:after {
7+
content: "Application.tick() count: " attr(data-tick-count);
8+
display: block;
9+
font-size: 14px;
10+
text-align: center;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div #tick class="tick">
2+
NgZone + ChangeDetection.Default + Async pipe (random generate tree) <br>
3+
Open the console, if you want to see the lifecycle
4+
</div>
5+
6+
<tree>
7+
<div>
8+
<div class="center">
9+
count$ = {{ countTree$|async|json }}
10+
</div>
11+
</div>
12+
13+
<child
14+
[limit]="{ deep: 5, limitComponents: { input: 1, button: 1} }"
15+
[count$]="countTree$">
16+
</child>
17+
</tree>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, ViewChild} from '@angular/core';
2+
import {Lifecycle} from '../common/utils/Lifecycle';
3+
import {TreeNode} from '../common/tree-node.class';
4+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
5+
import {Subscription} from 'rxjs/Subscription';
6+
7+
@Component({
8+
selector: 'app-root',
9+
templateUrl: './app.component.html',
10+
styleUrls: ['./app.component.css'],
11+
changeDetection: ChangeDetectionStrategy.Default
12+
})
13+
@Lifecycle({defaultName: true})
14+
export class AppComponent extends TreeNode {
15+
16+
@ViewChild('tick', {read: ElementRef}) private tick: ElementRef;
17+
private countTick: number = 0;
18+
19+
public countTree$ = new BehaviorSubject({ value: 0 });
20+
public cd: ChangeDetectorRef;
21+
private sub$: Subscription;
22+
private timer;
23+
24+
constructor(context: Injector) {
25+
super(context);
26+
}
27+
28+
public ngOnInit() {
29+
super.ngOnInit();
30+
this.sub$ = this.countTree$.subscribe(() => {
31+
clearInterval(this.timer);
32+
this.timer = setTimeout(() => this.cd.detectChanges(), 150);
33+
});
34+
}
35+
36+
37+
public ngAfterViewChecked() {
38+
this.countTick++;
39+
super.ngAfterViewChecked();
40+
if (this.tick) {
41+
this.tick.nativeElement.setAttribute('data-tick-count', this.countTick);
42+
}
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {BrowserModule} from '@angular/platform-browser';
2+
import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
3+
4+
import {AppComponent} from './app.component';
5+
import {TreeComponent} from './tree/tree/tree.component';
6+
import {ChildComponent} from './tree/child/child.component';
7+
import {ButtonClickComponent} from './component/button-click/button-click.component';
8+
import {FormsModule} from '@angular/forms';
9+
import {InputChangeComponent} from './component/input-change/input-change.component';
10+
import { ShowInputsComponent } from './component/show-inputs/show-inputs.component';
11+
import { IntervalComponent } from './component/interval/interval.component';
12+
import { FactorialComponent } from './component/factorial/factorial.component';
13+
14+
@NgModule({
15+
declarations: [
16+
AppComponent,
17+
TreeComponent,
18+
ChildComponent,
19+
ButtonClickComponent,
20+
InputChangeComponent,
21+
ShowInputsComponent,
22+
IntervalComponent,
23+
FactorialComponent,
24+
],
25+
imports: [
26+
BrowserModule,
27+
FormsModule
28+
],
29+
providers: [],
30+
bootstrap: [AppComponent],
31+
schemas: [NO_ERRORS_SCHEMA]
32+
})
33+
export class AppModule {
34+
}

change-detection-strategy-default-async-pipe/app/component/button-click/button-click.component.css

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<p>ButtonClickComponent: </p>
2+
<show-inputs [changes]="changes"></show-inputs>
3+
<button (click)="addCount()">click!</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {ChangeDetectionStrategy, Component, Injector, Input} from '@angular/core';
2+
import {Lifecycle} from "../../../common/utils/Lifecycle";
3+
import {TreeNode} from "../../../common/tree-node.class";
4+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
5+
6+
@Component({
7+
selector: 'button-click',
8+
templateUrl: './button-click.component.html',
9+
styleUrls: ['./button-click.component.css'],
10+
changeDetection: ChangeDetectionStrategy.Default
11+
})
12+
@Lifecycle({defaultName: true})
13+
export class ButtonClickComponent extends TreeNode {
14+
15+
@Input() count$: BehaviorSubject<{ value: number }>;
16+
17+
constructor(context: Injector) {
18+
super(context);
19+
}
20+
21+
public addCount() {
22+
let countValue: number = (this.count$.getValue()).value;
23+
this.count$.next({ value: ++countValue });
24+
}
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.factorial {
2+
max-width: 190px;
3+
padding: 5px;
4+
overflow: auto;
5+
white-space: nowrap;
6+
}
7+
8+
.computed:after {
9+
content: "Computed in template: " attr(data-total) "ms";
10+
display: block;
11+
font-size: 10px;
12+
text-align: center;
13+
}
14+
15+
.execute:after {
16+
content: "Execute count in template: " attr(data-execute);
17+
display: block;
18+
font-size: 10px;
19+
text-align: center;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="factorial">
2+
<p>FactorialComponent: </p>
3+
<input type="number" [(ngModel)]="factorialValue" min="0"><br>
4+
{{factorialValue}}! = {{factorial(factorialValue)}} <br><br>
5+
<div #total class="computed"></div>
6+
<div #execute class="execute"></div>
7+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, NgZone, ViewChild} from '@angular/core';
2+
import * as math from 'mathjs';
3+
4+
math.config({number: 'BigNumber', precision: 100});
5+
6+
@Component({
7+
selector: 'factorial',
8+
templateUrl: './factorial.component.html',
9+
styleUrls: ['./factorial.component.css'],
10+
changeDetection: ChangeDetectionStrategy.Default
11+
})
12+
export class FactorialComponent {
13+
public factorialValue = 10;
14+
private executeCount: number = 0;
15+
@ViewChild('total', {read: ElementRef}) private total: ElementRef;
16+
@ViewChild('execute', {read: ElementRef}) private execute: ElementRef;
17+
18+
constructor() {}
19+
20+
public factorial(n) {
21+
this.executeCount++;
22+
const start = performance.now();
23+
const result = math.eval(`${n}!`).toString();
24+
const total = Math.ceil(performance.now() - start);
25+
26+
if (this.total) {
27+
this.total.nativeElement.setAttribute('data-total', total);
28+
this.execute.nativeElement.setAttribute('data-execute', this.executeCount);
29+
}
30+
31+
return result;
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
input {
2+
max-width: 130px;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<p>InputChangeComponent: </p>
2+
<show-inputs [changes]="changes"></show-inputs>
3+
<input type="number" [ngModel]="(count$ | async).value" (ngModelChange)="addCount($event)">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {ChangeDetectionStrategy, Component, Injector, Input} from '@angular/core';
2+
import {TreeNode} from '../../../common/tree-node.class';
3+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
4+
5+
@Component({
6+
selector: 'input-change',
7+
templateUrl: './input-change.component.html',
8+
styleUrls: ['./input-change.component.css'],
9+
changeDetection: ChangeDetectionStrategy.Default
10+
})
11+
export class InputChangeComponent extends TreeNode {
12+
13+
@Input() count$: BehaviorSubject<{ value: number }>;
14+
15+
constructor(context: Injector) {
16+
super(context);
17+
}
18+
19+
public addCount($event) {
20+
this.count$.next({ value: parseInt($event) || 0 });
21+
}
22+
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<label>
2+
{{ runOutside || setInterval || asyncPipe }} interval
3+
<input type="checkbox" [ngModel]="enabled" (ngModelChange)="updateInterval($event)">
4+
</label>
5+
6+
<div *ngIf="asyncPipe">{{ (timerObservable | async) || formatDate }}</div>
7+
<div *ngIf="!asyncPipe">{{ formatDate }}</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {ChangeDetectionStrategy, Component, Injector, Input} from '@angular/core';
2+
import {interval} from 'rxjs/observable/interval';
3+
import {map} from 'rxjs/operators';
4+
import {Subscription} from 'rxjs/Subscription';
5+
import {Observable} from 'rxjs/Observable';
6+
import {TreeNode} from '../../../common/tree-node.class';
7+
import {Lifecycle} from '../../../common/utils/Lifecycle';
8+
9+
@Component({
10+
selector: 'interval',
11+
templateUrl: './interval.component.html',
12+
styleUrls: ['./interval.component.css'],
13+
changeDetection: ChangeDetectionStrategy.Default
14+
})
15+
@Lifecycle({defaultName: true})
16+
export class IntervalComponent extends TreeNode {
17+
@Input() public runOutside: string;
18+
@Input() public setInterval: string;
19+
@Input() public asyncPipe: string;
20+
public enabled: boolean;
21+
public formatDate: string;
22+
public timerSub: Subscription;
23+
public timerObservable: Observable<string>;
24+
public timer: number;
25+
26+
constructor(context: Injector) {
27+
super(context);
28+
this.formatDate = this.getFormattedDate();
29+
}
30+
31+
public enableInterval() {
32+
if (this.setInterval) {
33+
this.timerInterval();
34+
} else if (this.runOutside) {
35+
this.zone.runOutsideAngular(() => {
36+
const timer = interval(1000).pipe(map(this.getFormattedDate));
37+
this.timerSub = timer.subscribe((time) => {
38+
this.formatDate = time;
39+
this.cd.detectChanges();
40+
});
41+
});
42+
} else {
43+
this.zone.runOutsideAngular(() => { // but async pipe execute main zone
44+
this.timerObservable = interval(1000).pipe(map(this.getFormattedDate));
45+
});
46+
}
47+
}
48+
49+
public updateInterval(enable: boolean) {
50+
if (enable) {
51+
this.enableInterval();
52+
} else {
53+
if (this.timerSub) {
54+
this.timerSub.unsubscribe();
55+
}
56+
this.timerObservable = null;
57+
clearInterval(this.timer);
58+
}
59+
}
60+
61+
private timerInterval() {
62+
this.timer = window.setInterval(() => {
63+
this.formatDate = this.getFormattedDate();
64+
this.cd.detectChanges();
65+
}, 1000);
66+
}
67+
68+
private getFormattedDate() {
69+
return new Date().toJSON().substring(10, 19).replace('T', '');
70+
}
71+
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.inputs {
2+
background: rgba(256, 256, 256, 0.8);
3+
padding: 5px 2px;
4+
margin: 5px 2px;
5+
border: 1px dotted #000;
6+
color: #000;
7+
font-weight: normal;
8+
text-align: left;
9+
font-family: monospace;
10+
max-width: 150px;
11+
overflow: auto;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="inputs">
2+
<b>@Inputs: </b>
3+
<div *ngFor="let key of keys(changes)">
4+
{{ key }}: {{ changes[key]['currentValue']|async|json }}
5+
</div>
6+
</div>
7+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {ChangeDetectionStrategy, Component, Input, SimpleChanges} from '@angular/core';
2+
3+
@Component({
4+
selector: 'show-inputs',
5+
templateUrl: './show-inputs.component.html',
6+
styleUrls: ['./show-inputs.component.css'],
7+
changeDetection: ChangeDetectionStrategy.Default
8+
})
9+
export class ShowInputsComponent {
10+
@Input() public changes: SimpleChanges;
11+
12+
public keys(val) {
13+
return Object.keys(val);
14+
}
15+
}

change-detection-strategy-default-async-pipe/app/tree/child/child.component.css

Whitespace-only changes.

0 commit comments

Comments
 (0)