Skip to content

Commit 22e2b11

Browse files
committed
update
1 parent 277530e commit 22e2b11

File tree

483 files changed

+90082
-22466
lines changed

Some content is hidden

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

483 files changed

+90082
-22466
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# ChangeDetection
2+
3+
## Immutable Objects
4+
5+
In this example we have 2 components:
6+
7+
1. parent
8+
9+
```
10+
export class ParentComponent implements OnInit {
11+
12+
data = { counter: 0 };
13+
14+
constructor() { }
15+
16+
increment() {
17+
this.data.counter++;
18+
}
19+
20+
ngOnInit(): void {
21+
}
22+
23+
}
24+
25+
<!--html-->
26+
27+
<button (click)="increment()">increment</button>
28+
<hr>
29+
<app-counter [data]='data'></app-counter>
30+
31+
```
32+
33+
2. child
34+
35+
```
36+
export class CounterComponent implements OnChanges {
37+
38+
@Input() data;
39+
40+
constructor() { }
41+
42+
ngOnChanges(change: SimpleChanges): void {
43+
console.log('counter', change);
44+
// at the moment ngOnchanges is not triggered!
45+
}
46+
47+
<!--html-->
48+
49+
{{ data.counter }}
50+
```
51+
52+
What wanto to use now *immutable objects* to speed up the change detection process that angular runs behind the curtains in order to keep the DOM in sync.
53+
54+
More specific we want to tell angular to update the `counter` component only when the **obj.reference** changes and not when a property within that obj.changes. Obviously this will increase the performance of our app.
55+
56+
> Keep in mind that so far `ngOnChanges` is not triggered. To make it work we need to change the obj reference!
57+
58+
So first in our ChildComponent:
59+
60+
```
61+
@Component({
62+
selector: 'app-counter',
63+
templateUrl: './counter.component.html',
64+
styleUrls: ['./counter.component.scss'],
65+
66+
changeDetection: ChangeDetectionStrategy.OnPush
67+
})
68+
69+
```
70+
71+
> **OnPush strategy**:
72+
>
73+
> When a component is marked with `changeDetection: ChangeDetectionStrategy.OnPush`, angular will assume that the input object did not change if the object reference did not change. Meaning that changing a property won't trigger change detection. Thus the view will be out of sync with the model.
74+
75+
In order to update the view we need to use **immutable objs.** in our parent component:
76+
77+
```
78+
increment() {
79+
const clone = { ...this.data };
80+
clone.counter++;
81+
this.data = clone;
82+
}
83+
```
84+
85+
Now it works and we can log the `change` inside `ngOnChanges`.
86+
87+
88+
## Observables and ChangeDetectorRef
89+
90+
Now rather than pass the data directly we wanna pass it into an `Observable` this time.
91+
92+
1. ParentComponent
93+
94+
```
95+
export class ParentComponentCdr implements OnInit {
96+
97+
_counter = 0;
98+
data$ = new BehaviorSubject({ counter: 0 });
99+
100+
constructor() { }
101+
102+
increment() {
103+
this._counter++
104+
this.data$.next({ counter: this._counter });
105+
}
106+
```
107+
108+
2. ChildComponent
109+
110+
```
111+
@Input() data: Observable<any>;
112+
_data;
113+
114+
ngOnInit() {
115+
this.data.subscribe((value) => {
116+
this._data = value;
117+
});
118+
}
119+
```
120+
121+
> The view is still not updated. How come?
122+
123+
Observable never change their reference.
124+
125+
> How can we fix it?
126+
127+
```
128+
constructor(private cdr: ChangeDetectorRef) { }
129+
130+
ngOnInit() {
131+
this.data.subscribe((value) => {
132+
this._data = value;
133+
this.cdr.markForCheck();
134+
});
135+
}
136+
```
137+
138+
### What's the difference between `markForCheck()` and `detectChanges()` ?
139+
140+
- `cdr.detectChanges()` will run change detection immediately from the current component down through its descendants.
141+
142+
- `cdr.markForCheck()` will not run change detection, but mark its ancestors as needing to run change detection. Next time change detection runs anywhere, it will run also for those components which were marked.
143+
144+
#### When should we use it?
145+
146+
147+
- Use `detectChanges()` when you've updated the model after angular has run it's change detection, or if the update hasn't been in angular world at all.
148+
149+
- Use `markForCheck()` if you're using `OnPush` and you're bypassing the ChangeDetectionStrategy by mutating some data or you've updated the model inside a setTimeout;
150+
151+
152+
153+
154+
155+
156+
157+
158+
159+
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
{
2+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3+
"version": 1,
4+
"newProjectRoot": "projects",
5+
"projects": {
6+
"changeDetection": {
7+
"projectType": "application",
8+
"schematics": {
9+
"@schematics/angular:component": {
10+
"style": "scss"
11+
}
12+
},
13+
"root": "",
14+
"sourceRoot": "src",
15+
"prefix": "app",
16+
"architect": {
17+
"build": {
18+
"builder": "@angular-devkit/build-angular:browser",
19+
"options": {
20+
"outputPath": "dist/changeDetection",
21+
"index": "src/index.html",
22+
"main": "src/main.ts",
23+
"polyfills": "src/polyfills.ts",
24+
"tsConfig": "tsconfig.app.json",
25+
"aot": true,
26+
"assets": [
27+
"src/favicon.ico",
28+
"src/assets"
29+
],
30+
"styles": [
31+
"src/styles.scss"
32+
],
33+
"scripts": []
34+
},
35+
"configurations": {
36+
"production": {
37+
"fileReplacements": [
38+
{
39+
"replace": "src/environments/environment.ts",
40+
"with": "src/environments/environment.prod.ts"
41+
}
42+
],
43+
"optimization": true,
44+
"outputHashing": "all",
45+
"sourceMap": false,
46+
"extractCss": true,
47+
"namedChunks": false,
48+
"extractLicenses": true,
49+
"vendorChunk": false,
50+
"buildOptimizer": true,
51+
"budgets": [
52+
{
53+
"type": "initial",
54+
"maximumWarning": "2mb",
55+
"maximumError": "5mb"
56+
},
57+
{
58+
"type": "anyComponentStyle",
59+
"maximumWarning": "6kb",
60+
"maximumError": "10kb"
61+
}
62+
]
63+
}
64+
}
65+
},
66+
"serve": {
67+
"builder": "@angular-devkit/build-angular:dev-server",
68+
"options": {
69+
"browserTarget": "changeDetection:build"
70+
},
71+
"configurations": {
72+
"production": {
73+
"browserTarget": "changeDetection:build:production"
74+
}
75+
}
76+
},
77+
"extract-i18n": {
78+
"builder": "@angular-devkit/build-angular:extract-i18n",
79+
"options": {
80+
"browserTarget": "changeDetection:build"
81+
}
82+
},
83+
"test": {
84+
"builder": "@angular-devkit/build-angular:karma",
85+
"options": {
86+
"main": "src/test.ts",
87+
"polyfills": "src/polyfills.ts",
88+
"tsConfig": "tsconfig.spec.json",
89+
"karmaConfig": "karma.conf.js",
90+
"assets": [
91+
"src/favicon.ico",
92+
"src/assets"
93+
],
94+
"styles": [
95+
"src/styles.scss"
96+
],
97+
"scripts": []
98+
}
99+
},
100+
"lint": {
101+
"builder": "@angular-devkit/build-angular:tslint",
102+
"options": {
103+
"tsConfig": [
104+
"tsconfig.app.json",
105+
"tsconfig.spec.json",
106+
"e2e/tsconfig.json"
107+
],
108+
"exclude": [
109+
"**/node_modules/**"
110+
]
111+
}
112+
},
113+
"e2e": {
114+
"builder": "@angular-devkit/build-angular:protractor",
115+
"options": {
116+
"protractorConfig": "e2e/protractor.conf.js",
117+
"devServerTarget": "changeDetection:serve"
118+
},
119+
"configurations": {
120+
"production": {
121+
"devServerTarget": "changeDetection:serve:production"
122+
}
123+
}
124+
}
125+
}
126+
}},
127+
"defaultProject": "changeDetection"
128+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { AppPage } from './app.po';
2+
import { browser, logging } from 'protractor';
3+
4+
describe('workspace-project App', () => {
5+
let page: AppPage;
6+
7+
beforeEach(() => {
8+
page = new AppPage();
9+
});
10+
11+
it('should display welcome message', () => {
12+
page.navigateTo();
13+
expect(page.getTitleText()).toEqual('changeDetection app is running!');
14+
});
15+
16+
afterEach(async () => {
17+
// Assert that there are no errors emitted from the browser
18+
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19+
expect(logs).not.toContain(jasmine.objectContaining({
20+
level: logging.Level.SEVERE,
21+
} as logging.Entry));
22+
});
23+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Karma configuration file, see link for more information
2+
// https://karma-runner.github.io/1.0/config/configuration-file.html
3+
4+
module.exports = function (config) {
5+
config.set({
6+
basePath: '',
7+
frameworks: ['jasmine', '@angular-devkit/build-angular'],
8+
plugins: [
9+
require('karma-jasmine'),
10+
require('karma-chrome-launcher'),
11+
require('karma-jasmine-html-reporter'),
12+
require('karma-coverage-istanbul-reporter'),
13+
require('@angular-devkit/build-angular/plugins/karma')
14+
],
15+
client: {
16+
clearContext: false // leave Jasmine Spec Runner output visible in browser
17+
},
18+
coverageIstanbulReporter: {
19+
dir: require('path').join(__dirname, './coverage/changeDetection'),
20+
reports: ['html', 'lcovonly', 'text-summary'],
21+
fixWebpackSourcePaths: true
22+
},
23+
reporters: ['progress', 'kjhtml'],
24+
port: 9876,
25+
colors: true,
26+
logLevel: config.LOG_INFO,
27+
autoWatch: true,
28+
browsers: ['Chrome'],
29+
singleRun: false,
30+
restartOnFileChange: true
31+
});
32+
};

0 commit comments

Comments
 (0)