如何在Angular 2中在每个组件加载完成后运行一个jQuery函数

22
我已经尝试了所有的生命周期钩子,但是无法完成所需的结果。 我需要的结果是在每个元素(组件)被加载后触发一个函数来初始化许多用于不同元素的jquery插件。
假设你有这样的结构:
主页 幻灯片 小部件 产品旋转器 ..等等
每个元素都有自己的组件,所有组件都是主页父组件的子级。
在这里,我需要知道所有子组件和父组件何时加载完成,以便触发一个jquery函数来初始化页面上的每个插件。

你找到这个问题的解决方案了吗? - Praveen Rawat
5个回答

26
你需要使用“ngAfterViewInit”生命周期钩子,通过导入AfterViewInit (https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview)。你可以像下面这样使用它:

安装:

    tsd install jquery --save

或者

    typings install dt~jquery --global --save

使用:

    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);
    }

这里有一个示例的Plunker: http://plnkr.co/edit/KweZYO9hk9Dz8pqDK77F?p=info

1
错误:无法找到名称'$' Angular 2 Final - KhoPhi
好像您现在需要声明美元符号。 - Dan Weber
我在使用ngAfterViewInit和jquery时遇到了问题,因为当我尝试定位元素时它们不存在:ngAfterViewInit() { console.log($('#startDateTimeFilter, #endDateTimeFilter).length); } 显示0而不是2。 - Paul
1
你的代码中有一个尾随的撇号,这里缺少一个。我创建了一个 Plunker 来获取 div 的长度,它返回 1。请参见:http://plnkr.co/edit/KweZYO9hk9Dz8pqDK77F - Dan Weber
如果您的依赖项依赖于Jquery,则应将 "scripts": ["node_modules/jquery/dist/jquery.js" 添加到Angular 6+的 angular.json 文件中。 - canbax

16

我发现最有效的方法是在页面/组件构造函数内使用时间为0的setTimeout。这样可以让jQuery在Angular加载完所有子组件后,在下一个执行周期中运行。

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

将setTimeout放在ngOnInit方法中对我也有效。

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

7
这对我有用,但感觉很糟糕!有没有关于“正式”方法的更新? - Harry
超时设置为0对我没有起作用,但是将超时设置为1却有效。 - Bruce Patin
我没有使用JQuery,而是使用document.querySelector()。在我发现这个方法之前,我已经白费了好几个小时。谢谢! - ckapilla
谢谢你的提示。它对我有用... 然而,似乎我们应该能够在“AfterViewInit”函数中访问dom,而不需要这个hack。 - birwin

4

关于什么?

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

否则我会假设您的根组件的ngAfterViewInit()是符合您要求的生命周期钩子,但您表示您已经测试过了... 更新 bootstrap()或根组件上的ngAfterViewInit()仅适用于初始加载。对于后来添加的组件,可以使用添加组件的ngAfterViewInit()

我尝试过,但这些事件在页面上还没有任何内容之前就会触发,例如我尝试在它们两个上触发警报,结果如下所示。http://postimg.org/image/8qv5tqei9/ 你可以看到页面上还没有任何东西。我想在页面上的所有内容完全加载后再初始化jquery插件。似乎没有任何生命周期钩子能起作用。 - Joseph Girgis
我还没有检查bootstrap.then,但对于ngAfterViewInit来说这不可能是真的。 - Günter Zöchbauer
3
我也无法工作。bootstrap(...).then() 方法以及根组件的 ngAfterViewInit() 钩子都不起作用。对我有用的是在每个路由的每个叶子组件的 ngAfterViewInit() 中添加 jQuery 插件初始化。 - Andris Krauze

1
我在这里遇到了同样的问题。我找到了两种解决方案,虽然它们不是最好的:
1- 实现afterViewChecked来检查jquery插件是否已经初始化,然后使用一个变量来控制其初始化。这是一种较困难的方法,因为afterViewChecked被调用了很多次。
2- 我使用hidden属性将一些元素隐藏,而不是使用ngIf。使用hidden,我可以使用afterViewInit初始化所有jquery插件,并使它们在我的对象隐藏时仍然正常工作。
当使用ngIf时,setTimeout无法工作。它会在内容重新渲染之前触发。

1

一个可能的解决方案是订阅 zone.onStablezone.onMicrotaskEmpty

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

或者

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

另请参阅


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接