109

I can not understand what the difference between ngOnInit and ngAfterViewInit.

I found the only difference between them is @ViewChild. According to the following code, the elementRef.nativeElement in them are the same.

What scene should we use ngAfterViewInit?

@Component({
  selector: 'my-child-view',
  template: `
  <div id="my-child-view-id">{{hero}}</div>
  `
})
export class ChildViewComponent {
  @Input() hero: string = 'Jack';
}

//////////////////////
@Component({
  selector: 'after-view',
  template: `
    <div id="after-view-id">-- child view begins --</div>
      <my-child-view [hero]="heroName"></my-child-view>
    <div>-- child view ends --</div>`
    + `
    <p *ngIf="comment" class="comment">
      {{comment}}
    </p>
  `
})
export class AfterViewComponent implements AfterViewInit, OnInit {
  private prevHero = '';
  public heroName = 'Tom';
  public comment = '';

  // Query for a VIEW child of type `ChildViewComponent`
  @ViewChild(ChildViewComponent) viewChild: ChildViewComponent;

  constructor(private logger: LoggerService, private elementRef: ElementRef) {
  }

  ngOnInit() {
    console.log('OnInit');
    console.log(this.elementRef.nativeElement.querySelector('#my-child-view-id'));
    console.log(this.elementRef.nativeElement.querySelector('#after-view-id'));
    console.log(this.viewChild);
    console.log(this.elementRef.nativeElement.querySelector('p'));
  }

  ngAfterViewInit() {
    console.log('AfterViewInit');
    console.log(this.elementRef.nativeElement.querySelector('#my-child-view-id'));
    console.log(this.elementRef.nativeElement.querySelector('#after-view-id'));
    console.log(this.viewChild);
    console.log(this.elementRef.nativeElement.querySelector('p'));
  }
}
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
Zhiyuan Sun
  • 1,171
  • 2
  • 7
  • 5

3 Answers3

144

ngOnInit() is called after ngOnChanges() was called the first time. ngOnChanges() is called every time inputs are updated by change detection.

ngAfterViewInit() is called after the view is initially rendered. This is why @ViewChild() depends on it. You can't access view members before they are rendered.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 2
    When you say _rendered_ , do you mean it appears on the screen ? ( or sent to be rendered to appear on the screen) – Royi Namir Jun 22 '17 at 19:11
  • 3
    When it's added to the DOM. If you set `display: hidden` it's till rendered, but not visible on the screen. But if you investigate the DOM using the browsers devtools, you'll be able to see the markup. – Günter Zöchbauer Jun 22 '17 at 19:12
  • 4
    "_you can't access view members before they are rendered_" - So how do you explain that the `ViewChild` (vc) is available on `onNgInit` ? https://plnkr.co/edit/AzhRe6bjnuPLKJWEJGwp?p=preview , Can you please explain ? – Royi Namir Jun 27 '17 at 06:45
  • 12
    @Royi I can't open your Plunker on my phone and it will tke a few days until I'm back to my computer. Statically added elements are already available in `ngOnInit`. If you have content that is rendered for examply by `*ngFor` from data passes to an `@Input`, this content won't yet be available in `ngOnInit` – Günter Zöchbauer Jun 27 '17 at 18:14
  • 2
    Thank you very much for the response. That's exactly the scenario. So I guess this is it. https://i.imgur.com/Vbajl4F.jpg . Enjoy your vacation. – Royi Namir Jun 27 '17 at 18:29
  • I would still always use `ngAfterViewInit`, because it works for all cases (except content generated from async fetched data bacause this can't be covered by component callbacks) – Günter Zöchbauer Jun 29 '17 at 08:32
  • In general aren't you better off using ngOnInit to avoid these issues? https://stackoverflow.com/questions/45012141/updating-boolean-in-afterviewinit-causes-expression-has-changed-after-it-was-ch – tony Jul 19 '21 at 07:18
46

ngOnInit() is called right after the directive's data-bound properties have been checked for the first time, and before any of its children have been checked. It is invoked only once when the directive is instantiated.

ngAfterViewInit() is called after a component's view, and its children's views, are created. Its a lifecycle hook that is called after a component's view has been fully initialized.

Peter
  • 10,492
  • 21
  • 82
  • 132
  • 1
    "after a component's view has been fully initialized" would suggest that is after ngOnInit which is not true (ngAfterViewInit could be called before/during ngOnInit) – Andrej K Feb 07 '22 at 20:57
3

Content is what is passed as children. View is the template of the current component.

The view is initialized before the content and ngAfterViewInit() is therefore called before ngAfterContentInit().

** ngAfterViewInit() is called when the bindings of the children directives (or components) have been checked for the first time. Hence its perfect for accessing and manipulating DOM with Angular 2 components. As @Günter Zöchbauer mentioned before is correct @ViewChild() hence runs fine inside it.

Example:

@Component({
    selector: 'widget-three',
    template: `<input #input1 type="text">`
})
export class WidgetThree{
    @ViewChild('input1') input1;

    constructor(private renderer:Renderer){}

    ngAfterViewInit(){
        this.renderer.invokeElementMethod(
            this.input1.nativeElement,
            'focus',
            []
        )
    }
}
STEEL
  • 8,955
  • 9
  • 67
  • 89
  • I think you are wrong here. ngAfterViewInit() executes only after ngAfterContentChecked() and ngAfterContentChecked() executes only after the ngAfterContentInit() and every subsequent ngDoCheck(). Please refer angular lifecycle hooks details for more details https://angular.io/guide/lifecycle-hooks – Suneet Bansal Feb 16 '19 at 08:49