$(document).ready AngularJS 的替代方案

$(document).ready alternative for AngularJS

我正在使用一个名为 Gentelella 的模板,我正在尝试在其中实施 AngularJS。但是,我遇到了某个 Javascript 文件的问题。在此文件的末尾,调用了一个 $(document).ready 函数,该函数初始化 Javascript 代码,对 HTML 代码进行了一些更改。问题是 $(document).ready 函数被调用得太早,在 HTML 完全加载之前。

出现此问题可能是因为我正在使用 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(), ...
});

main.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';

})

提前致谢!

当您使用 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>
$document.ready(function() {
  $scope.$on('$viewContentLoaded', function() {
    $timeout(function() {
      init_sparklines(), init_flot_chart(), init_sidebar(), init_wysiwyg(), init_InputMask(), ...
    })
  })
})

更新注意:兄弟们,这不是一个严格的解决方案,请看我写

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

许多 jQuery 插件依赖于 1. 绘制 DOM 的工作流程。 2. 运行 一个 init() 函数来针对这些 DOM 元素设置代码。

该工作流在 Angular 中表现不佳,因为 DOM 不是静态的:Angular 在其自己的生命周期中设置和销毁 DOM 节点,这可以覆盖事件绑定或 DOM 在 Angular 之外所做的更改。当您使用 Angular 时,文档就绪不是特别有用,因为它只表明 Angular 本身已准备好开始 运行ning。

要有效地使用 Angular,您必须养成只在实际需要时才启动代码的习惯。因此,不是 document.ready 上的一大桶 init_foo(); init_bar();,你应该有一个带有自己的初始化代码的 Foo 指令,以及一个带有自己特定初始化代码的 Bar 指令,等等。这些指令中的每一个都应该只修改由该特定指令创建的 DOM 。这是确保您需要修改的 DOM 元素实际存在并且您不会在指令之间造成冲突或意外相互依赖的唯一安全方法。

举一个例子:我猜你的 init_flot_chart() 会向下爬过 DOM 寻找一个特定的元素,它将在其中绘制一个浮动图表。而不是那种 top-down 方法,创建一个指令:

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 是一个 object 包含要在图表中绘制的任何数据。 (您可以添加其他属性以传入您喜欢的任何其他参数,例如 flot 选项、标题等)

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

(请注意,如果 theChartData 的内容发生变化,这不会自动更新;可以看到一个更详细的示例,它也监视变化并做出适当的响应 here。)

最近版本的 angular.js 中有一个名为 $postLink() 的生命周期挂钩,在该控制器的元素及其子元素被 link 编辑后调用。类似于 post-link 函数,此挂钩可用于设置 DOM 事件处理程序并直接执行 DOM manipulation.Check guide