AngularJS中的$(document).ready替代方法

3
我正在使用一个名为Gentelella的模板,并尝试将AngularJS集成到其中。然而,我在某个Javascript文件中遇到了问题。在该文件末端调用了一个$(document).ready函数,该函数初始化一些JavaScript代码,对HTML代码进行了一些更改。问题在于,在HTML完全加载之前就调用了$(document).ready函数。
这个问题可能是因为我使用了ngRoute,它将模板HTML文件注入到index.html的ng-view中。当这发生时,DOM可能已经在AngularJS注入模板(即HTML)之前宣布文档已准备好。
所以基本上,我需要找到一种方法,在AngularJS注入模板后调用JavaScript文件中的某些代码。
我附加了一些代码以了解该问题:
custom.min.js的片段
$(document).ready(function () {
  init_sparklines(), init_flot_chart(), init_sidebar(), init_wysiwyg(), init_InputMask(), ...
});

主要JS代码片段:

.config(function($routeProvider, $httpProvider) {

  $routeProvider.when('/', {
    templateUrl : 'dash.html',
    controller : 'dash',
    controllerAs: 'controller'
  }).when('/login', {
    templateUrl : 'login.html',
    controller : 'navigation',
    controllerAs: 'controller'
  }).when('/plain_page', {
    templateUrl : 'plain_page.html',
    controller : 'dash',
    controllerAs: 'controller'
  }).otherwise('/');

  $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';

})

感谢您的提前帮助!

你能检查一下这个链接吗:https://dev59.com/EWMl5IYBdhLWcg3wJUIC - Agam Banga
1
不要尝试在document.ready中进行钩子操作;从需要使用该代码的指令的link函数内调用它。 (通常,在使用Angular时直接修改DOM会导致问题;最好在Angular自己的生命周期内工作。) - Daniel Beck
@DanielBeck 谢谢您的回答。我知道在AngularJS项目中更改DOM并使用jQuery函数不是最好的方法,但现在情况就是这样。您能否给出链接函数的具体示例?我只需将init代码插入链接中吗?谢谢! - Jérémy
1
这个链接可以更好地解释它:https://docs.angularjs.org/guide/directive(或者正如@mistalis在下面展示的,您可以将它包含在控制器或ng-init中。)(我应该指出我仍在使用v1.x术语,当他们重写一切时我就离开了Angular,所以对当前版本不太熟悉,但我认为v4的等效指令是`ngAfterViewInit`) - Daniel Beck
@DanielBeck 对不起,但我还没有想出如何使用 link 解决这个问题。我应该创建一个新的指令并在其中放置代码吗? - Jérémy
我忘了提到一个小细节(不确定它是否重要),就是我正在一个Spring Boot项目中运行整个项目。但我认为这并不会影响任何事情。 - Jérémy
5个回答

5
许多jQuery插件的工作流程是1.绘制DOM 2.运行init()函数来对这些DOM元素进行设置。然而,在Angular中,这个工作流程表现不佳,因为DOM不是静态的:Angular会在其自身的生命周期内设置和销毁DOM节点,这可能会覆盖事件绑定或在Angular外部进行的DOM更改。当使用Angular时,文档准备就绪并没有特别有用,因为它只表示Angular本身已准备好开始运行。
要有效地使用Angular,您必须养成仅在实际需要时初始化代码的习惯。因此,与其在document.ready上有一个大桶init_foo(); init_bar();,您应该拥有一个具有其自己的初始化代码的Foo指令,以及一个具有其自己特定初始化代码的Bar指令等等。每个指令都应该仅修改由该特定指令创建的DOM。这是确保您需要修改的DOM元素实际存在且不会在指令之间创建冲突或意外相互依赖的唯一安全方式。
举个例子:我猜测您的init_flot_chart()会沿着DOM向下爬,寻找其中的一个元素,然后在其中绘制flot图表。相比这种自上而下的方法,创建一个指令会更好。
angular.module('yourApp')
  .directive('flotWrapper', function () {
    return {
      template: "<div></div>",
      scope: {
        data: '@'
      },
      link: function(scope, elem, attrs) {
        var options = {}; // or could pass this in as an attribute if desired
        $.plot(elem, scope.data, options); // <-- this calls flot on the directive's element; no DOM crawling necessary
      }
    };
});

您可以像这样使用:

<flot-wrapper data="{{theChartData}}"></flot-wrapper>

...其中theChartData是一个包含图表绘制所需数据的对象。(您可以添加其他属性以传递其他参数,例如flot选项、标题等。)

当Angular绘制flotWrapper指令时,它首先在指令模板中创建DOM元素,然后运行其link函数来处理模板的根元素。(flot库本身可以通过普通的<script>标签包含,因此在指令需要时可以使用其plot函数。)

(请注意,如果theChartData的内容更改,这不会自动更新;还可以看到一个更为复杂的示例,该示例还监视更改并做出相应响应,请参见此处。)


1

由于您正在使用ngRoute,因此当页面加载时,您的控制器将运行。 您可以在控制器启动时调用init()方法来执行任何您想要的操作。

function MyCtrl($scope) {
    function init() {
        console.log('controller started!');
    }
    init();
}

作为一个附注,这是John Papa Angular's guide中推荐的最佳实践。
另一种可能性是使用ng-init指令,例如:
<div ng-controller="MyCtrl" ng-init="myFunction()">...</div>

谢谢你的回答。这个解决方案似乎不起作用。我在init()方法中调用了一个警报,当警报被调用时,HTML还没有完全加载。 - Jérémy

0
在最近的angular.js版本中有一个名为$postLink()的生命周期钩子函数,它会在该控制器的元素及其子元素已链接之后调用。类似于post-link函数,这个钩子函数可以用来设置DOM事件处理程序和直接的DOM操作。请参阅guide

-1
$document.ready(function() {
  $scope.$on('$viewContentLoaded', function() {
    $timeout(function() {
      init_sparklines(), init_flot_chart(), init_sidebar(), init_wysiwyg(), init_InputMask(), ...
    })
  })
})

更新通知:抱歉各位,这不是一个严谨的解决方案,请查看我写的另一个答案


1
"$timeout" 不是一个完美的解决方案,因为我们无法决定在什么时间后需要调用该函数。 - Harshit Jain
这个完全不起作用。我需要在某个地方触发viewContentLoaded事件吗? - Jérémy
最好能够解释一下你的代码是如何解决问题的,以及为什么这样做。 - brasofilo

-1

这与Angular的脏检查循环有关,涉及到Angular在底层的工作原理、数据绑定等。有很多优秀的教程可以解释这些内容。

要解决您的问题,请使用$timeout,它将使代码在下一个循环中执行:

app.controller('Controller', function ($scope, $timeout) {
    $scope.$on('$viewContentLoaded', function(event) {
      $timeout(function() {
        init_sparklines(), init_flot_chart(), init_sidebar(), init_wysiwyg(), init_InputMask(), ...
      },0);
    });
});

谢谢您的回答。这个方案不起作用。函数在实际注入HTML之前已经被调用了。 - Jérémy

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