my application structure looks like this:

ts:
...
export class TodoListComponent {
get sortedTodos():ITodo[] {
console.log(this.counter++);
...
}
....
html:
<div class="todo-item" *ngFor="let todo of sortedTodos" [class.completed]="todo.completed">
<todo-list-item [todo]="todo" class="todo-item" (deleted)="onTodoDeleted(todo)"
(toggled)="onTodoUpdated($event)"></todo-list-item>
</div>
If I start application I see in console:
1
2
3
4
5
6
I really confusing about this behaviour. for me it looks very strange and I think it can lead to bugs and performance issues. Please explain why it executes 6! times when I load page at once.
I am not sure that I provided all needed information in topic. Feel free to request womething else. Also all code base can be found bitbucket repo link
P.S.
full ts file content:
import {Component, Input, Output, EventEmitter} from "@angular/core"
import {ITodo} from "../../shared/todo.model";
import {TodoService} from "../../shared/todoService";
@Component({
moduleId: module.id,
selector: "todo-list",
templateUrl: "todo-list.component.html",
styleUrls: ["todo-list.component.css"],
})
export class TodoListComponent {
@Input() todos:ITodo[];
@Output() updated:EventEmitter<ITodo> = new EventEmitter<ITodo>();
@Output() deleted:EventEmitter<ITodo> = new EventEmitter<ITodo>();
get sortedTodos():ITodo[] {
return !this.todos ? [] :
this.todos.map((todo:ITodo)=>todo)
.sort((a:ITodo, b:ITodo)=> {
if (a.title > b.title) {
return 1;
} else if (a.title < b.title) {
return -1;
}
return 0;
})
.sort((a:ITodo, b:ITodo)=> (+a.completed - (+b.completed)));
}
onTodoDeleted(todo:ITodo):void {
this.deleted.emit(todo);
}
onTodoUpdated(todo:ITodo):void {
this.updated.emit(todo);
}
constructor(private todoService:TodoService) {
}
}
It executes 6 times because:
There is a tick method in ApplicationRef class and it is executed 3 times by 2 change detection cycles. If you will enable production mode by calling enableProdMode() it will be executed 3 times
ApplicationRef is an reference to an Angular application running on a page. The tick method is running change detection from the root to leaves.
Here is how tick method looks (https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L493-L509):
tick(): void {
if (this._runningTick) {
throw new Error('ApplicationRef.tick is called recursively');
}
const scope = ApplicationRef_._tickScope();
try {
this._runningTick = true;
this._views.forEach((view) => view.ref.detectChanges()); // check
if (this._enforceNoNewChanges) {
this._views.forEach((view) => view.ref.checkNoChanges()); // check only for debug mode
}
} finally {
this._runningTick = false;
wtfLeave(scope);
}
}
For debug mode tick starts two change detection cycles. So detectChangesInternal within compiled view will be called twice.

And as your sortedTodos property is a getter so it will be executed everytime as a function.
Read more about it here (Change Detection in Angular 2)
So then we know that our sortedTodos getter is called twice for one tick
Why is the tick method executed 3 times?
1) First tick is running manually by bootstrapping application.
private _loadComponent(componentRef: ComponentRef<any>): void {
this.attachView(componentRef.hostView);
this.tick();
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L479
2) Angular2 is running within zonejs so it's the main thing which manages change detection. Mentioned above ApplicationRef is subscribed to zone.onMicrotaskEmpty.
this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }});
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L433
onMicrotaskEmpty event is an indicator when zone gets stable
private checkStable() {
if (this._nesting == 0 && !this._hasPendingMicrotasks && !this._isStable) {
try {
this._nesting++;
this._onMicrotaskEmpty.emit(null); // notice this
} finally {
this._nesting--;
if (!this._hasPendingMicrotasks) {
try {
this.runOutsideAngular(() => this._onStable.emit(null));
} finally {
this._isStable = true;
}
}
}
}
}
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/zone/ng_zone.ts#L195-L211
So after some zonejs task this event is emitted
3) You're using angular2-in-memory-web-api package and when you're trying to get mock data it does following:
createConnection(req: Request): Connection {
let res = this.handleRequest(req);
let response = new Observable<Response>((responseObserver: Observer<Response>) => {
if (isSuccess(res.status)) {
responseObserver.next(res);
responseObserver.complete();
} else {
responseObserver.error(res);
}
return () => { }; // unsubscribe function
});
response = response.delay(this.config.delay || 500); // notice this
return {
readyState: ReadyState.Done,
request: req,
response
};
}
https://github.com/angular/in-memory-web-api/blob/0.0.20/src/in-memory-backend.service.ts#L136-L155
It start regular zonejs task cycle which makes zone unStable and finally after task execution is emitted described above onMicrotaskEmpty event again
You can find more details about zonejs here
Recap:
So as you can see your solution a bit wrong. You shouldn't use getter or function as binding within your template. Possible solution you can find here
This is how Template expressions works in Angular 2, Angular executes template expressions more often than we think.They can be called after every keypress or mouse move.
There are Expression guidelines so that there is no bugs or performance issues.
Template expressions can make or break an application. Please follow these guidelines:
The only exceptions to these guidelines should be in specific circumstances that you thoroughly understand.
NO VISIBLE SIDE EFFECTS
A template expression should not change any application state other than the value of the target property.
This rule is essential to Angular's "unidirectional data flow" policy. We should never worry that reading a component value might change some other displayed value. The view should be stable throughout a single rendering pass.
QUICK EXECUTION
Angular executes template expressions more often than we think. They can be called after every keypress or mouse move. Expressions should finish quickly or the user experience may drag, especially on slower devices. Consider caching values computed from other values when the computation is expensive.
SIMPLICITY
Although it's possible to write quite complex template expressions, we really shouldn't.
A property name or method call should be the norm. An occasional Boolean negation (!) is OK. Otherwise, confine application and business logic to the component itself, where it will be easier to develop and test.
IDEMPOTENCE
An idempotent expression is ideal because it is free of side effects and improves Angular's change detection performance.
In Angular terms, an idempotent expression always returns exactly the same thing until one of its dependent values changes.
Dependent values should not change during a single turn of the event loop. If an idempotent expression returns a string or a number, it returns the same string or number when called twice in a row. If the expression returns an object (including an Array), it returns the same object reference when called twice in a row.
Number 6 in your case depends upon how many expression you have in your HTML template.
Hope this helps!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With