22

I have tried all the life cycle hooks but can't get to accomplish the needed result. The result that I need is triggering a function that initialize many jquery plugins used for different elements on a single page after every single one of these elements (components) is loaded.

So lets say that you have this structure.

Home Page Slider Widgets Product rotators ..etc

Every one of these elements has it's own component and all are children of the Home page parent component.

And what I need here is to know when all the children components and the parent component is loaded so I trigger one jquery function to initialize every plugin on the page.

Joseph Girgis
  • 3,115
  • 4
  • 19
  • 20

5 Answers5

26

You will want to use the "ngAfterViewInit" lifecycle hook, through importing AfterViewInit (https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview). You can use it as shown below:

Installation:

    tsd install jquery --save

or

    typings install dt~jquery --global --save

Utilization:

    import { Component, AfterViewInit } from '@angular/core';
    import * as $ from 'jquery';

    ngAfterViewInit() {
        this.doJqueryLoad();     
        this.doClassicLoad();

        $(this.el.nativeElement)
            .chosen()
            .on('change', (e, args) => {
                this.selectedValue = args.selected;
            });
    }

    doClassicLoad() {
      // A $( document ).ready() block.
      $( document ).ready(function() {
          console.log( "Unnecessary..." );
      });      
    }

    // You don't need to use document.ready... 
    doJqueryLoad() {
        console.log("Can use jquery without doing document.ready")
        var testDivCount = $('#testDiv').length;
        console.log("TestDivCount: ", testDivCount);
    }

Here is a plunker for an example: http://plnkr.co/edit/KweZYO9hk9Dz8pqDK77F?p=info

Dan Weber
  • 1,227
  • 1
  • 11
  • 22
  • 1
    Error: `Cannot find name '$'` Angular 2 Final – KhoPhi Jan 31 '17 at 12:49
  • It looks like you now have to declare the dollar sign. – Dan Weber Jan 31 '17 at 14:18
  • I'm having a problem using ngAfterViewInit with jquery because when I try to target elements they aren't there : ngAfterViewInit() { console.log($('#startDateTimeFilter, #endDateTimeFilter).length); } Shows 0 instead of 2. – Paul Dec 14 '17 at 17:15
  • 1
    Do you have a trailing apostrophe in your code, it's missing one here. I've created a plunker that gets the length of a div and it returns 1. See here: http://plnkr.co/edit/KweZYO9hk9Dz8pqDK77F – Dan Weber Dec 14 '17 at 18:41
  • If you have dependency which depends on Jquery you should put `"scripts": [ "node_modules/jquery/dist/jquery.js",` to your `angular.json` file for angular 6+ – canbax Oct 16 '19 at 07:29
16

The most effective way that I have found is to use setTimeout with time of 0 inside of the page/component constructor. This let's the jQuery run in the next execution cycle after Angular has finished loading all the child components.

export class HomePage {
    constructor() {
        setTimeout(() => {
            // run jQuery stuff here
        }, 0);
    }
}

Putting the setTimeout inside of ngOnInit method has also worked for me.

export class HomePage {
    ngOnInit() {
        setTimeout(() => {
            // run jQuery stuff here
        }, 0);
    }
}
Urgo
  • 700
  • 5
  • 5
  • 7
    This works for me, but it feels very hacky! Is there an update on a "proper" way to do this? – Harry May 30 '16 at 20:45
  • Timeout = 0 did not work for me, but timeout = 1 did. – Bruce Patin Oct 12 '16 at 20:40
  • I'm not using JQuery but document.querySelector(). I had been spinning my wheels for hours until I came across this. Thank you! – ckapilla May 19 '17 at 04:35
  • Thanks for the tip. It worked for me... It seems we should be able to access dom in an "AfterViewInit" function without this hack, however. – birwin Dec 03 '18 at 19:20
4

what about

bootstrap(...).then(x => {
  ...
})

otherwise I would assume ngAfterViewInit() of your root component to be a lifecycle hook that matches your requirement but you stated that you tested all already ...

update

bootstrap() or ngAfterViewInit() on the root component can only be used for the initial load. For later added components the ngAfterViewInit() of the added components can be used.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I tried but these events fire before there is anything on the page for example I tried to trigger an alert on both of them and I got this result. http://postimg.org/image/8qv5tqei9/ as you can see there is nothing yet on the page. I want to initialize the jquery plugins after everything on the page is fully loaded. none of the life hooks seems to do the trick. – Joseph Girgis Mar 01 '16 at 17:22
  • I haven't checked bootstrap.then but that can't be true for ngAfterViewInit – Günter Zöchbauer Mar 01 '16 at 17:27
  • 3
    Does not work for me either. Neither bootstrap(...).then() idea, nor ngAfterViewInit() hook of root component. What worked for me was adding jquery plugin initialization in every leaf component's ngAfterViewInit of every route. – Andris Krauze Mar 09 '16 at 15:48
1

I had the same problem here. I found 2 solutions, even they are not the best:

1 - Implement afterViewChecked to check if jquery plugin is initiated and then use a variable to control its initialization. This is the hard way because afterViewChecked is called many times.

2 - I changed some elements to be hidden using hidden property instead of ngIf. With hidden I could initialize all the jquery plugins using afterViewInit and make they work even my objects are hidden.

setTimeout did not work when using ngIf. It triggers before the content is re-rendered.

1

One possible solution would be subscribing on zone.onStable or zone.onMicrotaskEmpty

ngAfterViewInit() {
  this.zone.onStable.first().subscribe(() => {
    debugger;
  });
}

or

ngOnInit() {
  this.service.getData().subscribe(() => {
    this.data = data;
    this.zone.onMicrotaskEmpty.first().subscribe((data) => {
      $(someElem).plugin();
    });
  });
}

See also

Community
  • 1
  • 1
yurzui
  • 205,937
  • 32
  • 433
  • 399