Showing posts with label angular. Show all posts
Showing posts with label angular. Show all posts

Oct 4, 2020

When the change detector of OnPush component of Angular runs?

There are lots posts about how change detector works in Angualr. But I can't find a simple answer about when the change detector runs in OnPush components. I assume you understand how change detection works. If not , here is what you need to know. Angular use both zone.js and change detection. Zone.js modify DOM api(such as event, setTimeout etc), if an api trigger an exection of angular function, angular change detection cycle kick off. However, change detection cycle and change detector are two different concept. Change detector detects change. Change detection cycle does not necessary make change detector detect change. An angular application is a component tree. Each component will has its own change detector. The change detection start from the root components' change detector, all the way to the bottom component's change detector, asking them to detect change inside their components. By default, all change detectors will detect accordingly, unless the a change detector is manually detached by developer or it belongs to an OnPush component.

Here is a diagram shows when the change detector of OnPush component of Angular runs.

Change Detection Cycle start
Change Detection...
Is the trigger of Change Detection above (not inside/below) of the component?
Is the trigger of Change Det...
No (Inside / below)
No (Inside / below)
Yes (Above)
Yes (Above)
Is the @input reference changed
Is the @input refere...
The detector runs
The detector runs
Do nothing
Do nothing
Is it triggered by setTimeout
setInterval
promise.resolve
http
Is it triggered by se...
No
No
Yes
Yes
No
No
Yes
Yes
When Does the change detector of OnPush component run?
When Does the change detector of OnPush component run?
Viewer does not support full SVG 1.1

OnPush component does not change what can trigger change detection cycle. It does not when change detection cycle kicks off. It does not change how its parent's change detector runs, and it does not change how its children detector runs. It changes when the its and only its change detector runs. If you are an author of a component, and the component use @Input properties, and you will see there will be lots instance of this component in your application, and also there are lots binding in your component, it will make the performance better to mark the change detection strategy "OnPush" . Otherwise, the default change detection stragegy works just fine.

Sep 30, 2020

Create an Angular demo project

When learning Angular or any other framework or we have an idea, we want to quickly create a proof of concept. One way is quickly to quickly go to a online editor, such stackblitz, codepen, and start coding a demo project, then show case to other people of your code. But the problem is that, we quickly forget about the url of the projects, the more we create and the more we forget. So I built a project to serve as container of all demo projects. Here is what you need to do if you want to create new demo.

  1. Create a component, name it as demo-[name], such as demo-hero. Put your demo code in the component.
  2. Register the component in the demo-list.ts, like below. That is it.
//demo-list.ts
import { DemoHelloComponent } from "./demo/demo-hello/demo-hello.component";
import { DemoByeComponent } from "./demo/demo-bye/demo-bye.component";
import { DemoHeroComponent } from "./demo/demo-hero/demo-hero.component";

export const demoList = {
  DemoHelloComponent,
  DemoByeComponent,
  DemoHeroComponent
};

Here it is preview of the project

Pretty simple, right? Here is how I do that.

  • Step 1 Create the DemoService(demo.service.ts) which list all the component you want to demo.
  • Step 2. Create a component(demo.component.ts) menu link to all the component in list
  • Step 3. Create route (app.module.ts) like demo/:id and link to the each component

If you interested in the code, you can navigate in the project

Angular component field initialization

In my previous post, I talked about Class field initialization in typescript. How about field initialization of angular component, is it different? Angular component is defined an typescript class. It should follow same logics. However, Angular component has life cycle. The ngOnInit is special method that angular will run angular first displays the data-bound properties and sets the directive or component's input properties. So if your initialization code depends on these data-bound proprties and input properties, you can not run them in constructor. So the following code doesn't work

@Component({
  //..
})
class MyComponent {

  //angular specific
  @Input() firstName: string;
  @Input() lastName: string;

  fullName: string;

  constructor() {
    //this does not work
    this.fullName = this.firstName + ',' + this.lastName;
  }
}

You have to run this code in the ngOnInit method like below.

@Component({
  //..
})
class MyComponent {

  //angular specific
  @Input() firstName: string;
  @Input() lastName: string;

  fullName: string;

  ngOnInit() {
    this.fullName = this.firstName + ',' + this.lastName;
  }

}

But some people take it too far. Since it is safter to run in ngOnInit method, why not always put all initialize code in this method. And So the following initialization code become very popular, even the initialization code has no dependency of angular data-bound proprties and input properties.

@Component({
  //..
})
class ProductComponent {

  displayCode$: Observable<boolean>;
  errorMessage$: Observable<string>;
  products$: Observable<Product[]>;
  selectedProduct$: Observable<Product>;

  constructor(private store: Store) { }

  ngOnInit(): void {
    this.store.dispatch(ProductPageActions.loadProducts());
    this.products$ = this.store.select(getProducts);
    this.errorMessage$ = this.store.select(getError);
    this.selectedProduct$ = this.store.select(getCurrentProduct);
    this.displayCode$ = this.store.select(getShowProductCode);
  }
}

So If we understand this, can initialize the code like the following. It saves your effor of explicit typing by using typescript type inference. and it also improves code readability.

@Component({
  //..
})
class ProductComponent {

  products$ = this.store.select(getProducts);
  errorMessage$ = this.store.select(getError);
  selectedProduct$ = this.store.select(getCurrentProduct);
  displayCode$ = this.store.select(getShowProductCode);

  constructor(private store: Store) { }
}