AngularJS:$ viewContentLoaded在部分视图出现之前触发

时间:2014-11-25 14:52:39

标签: jquery angularjs dom angular-ui-router ready

对于部分视图,我想做一些我通常会对$(document).ready(function() {...})执行的JavaScript操作,例如将venet侦听器绑定到元素。我知道这对AngularJS不起作用,部分视图加载到" root"图。

因此,我向侦听器添加了一个侦听器,用于侦听$viewContentLoaded事件。调用了侦听器的函数,因此事件被触发但在我看来好像是在呈现局部视图之前。当我在侦听器函数中设置断点并使用firebug调试它时,我也没有看到元素,函数中的jquery选择也找不到局部视图的元素。

这就是控制器的样子:

angular.module('docinvoiceClientAngularjsApp')
  .controller('LoginController', function ($scope, $rootScope) {

$scope.$on('$viewContentLoaded', function(event) {
  console.log("content loaded");
  console.log($("#loginForm"));   // breakpoint here 
});

[...]

我想我做错了,因为如果这是一个常见的错误,必须在stackoverflow上发布更多帖子。

当我使用 ui-router ui-view 时,我会给你一个路由文件的摘录:

angular
  .module('docinvoiceClientAngularjsApp', [
    'ui.router',
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngMessages',
    'ngRoute',
    'ngSanitize',
    'ngTouch'
  ])
 .config(function ($routeProvider, $stateProvider) {
    $stateProvider
    .state('login', {
        url: '/',
        templateUrl: 'components/login/loginView.html',
        controller: 'LoginController'
    })
    .run(['$state', function ($state) {
        $state.transitionTo('login');
    }])

 [...]

感谢任何帮助。谢谢和亲切的问候

更新1:我将错误删除到以下用例:loginView.html如下所示:

<div id="loginContainer" style="width: 300px">
  <form id="loginForm" ng-submit="login(credentials)" ng-if="session.token == undefined">

[...]

一旦我从div标签中删除ng-if,它就会按预期工作。在呈现DOM之后触发事件,因此jQuery找到该元素。如果ng-if附加到div标签,则行为与第一次描述的相同。

更新2:正如所承诺的,我添加了一个工作演示,显示添加ng-if指令时的不同行为。谁能指出我正确的方向?不要坚持使用登录表单,因为有更多的用例,我想根据某些表达式删除视图的某些部分,并在部分视图准备好后执行一些JavaScript操作。

您可以在此处找到工作演示:Demo

3 个答案:

答案 0 :(得分:44)

这与角度消化周期有关,它是关于角度如何在引擎盖下工作,数据绑定等。有很好的教程解释这个。

要解决您的问题,请使用$ timeout,它将使代码在下一个周期执行,因为ng-if已经被解析:

app.controller('LoginController', function ($scope, $timeout) {
    $scope.$on('$viewContentLoaded', function(event) {
      $timeout(function() {
        $scope.formData.value = document.getElementById("loginForm").id;
      },0);
    });
});

此处修正了演示:http://codepen.io/anon/pen/JoYPdv

但我强烈建议你使用指令做任何DOM操作,控制器不是为了那个。 这是一个如何做到这一点的例子: Easy dom manipulation in AngularJS - click a button, then set focus to an input element

答案 1 :(得分:1)

我在指令的帮助下解决了这个问题。将一个direcitve添加到您的元素(如<div my-dir></div>)并对相应指令中的元素进行操作,如下所示,

app.directive('myDir', function () {
    return {
        restrict: 'A',
        link: function (scope, element) {
           // Do manipulations here with the help of element parameter
        }
    };
});

我还尝试了状态提供程序事件,例如$stateChangeSuccess$viewContentLoaded,但无法解决问题。因为在这些事件被解雇之后,它需要花时间在DOM上进行渲染。

因此,我们可以遵循这种方法,在Angular JS中实现完美的结果和正确的实现方式:)

答案 2 :(得分:0)

这是对哈迪斯的回答有点晚,但可能会帮助其他人。我设置了一个服务,我稍后可以从控制器或指令中调用它。

'use strict';

app.factory('viewContentLoaded', function($q, $rootScope, $timeout) {
    var viewContentLoaded = $q.defer(),
        
        foo = function() {
            $timeout(function() {
                viewContentLoaded.resolve();
                // Get all entries
            }, 100);//Timeout
        },

        checkViewContenLoadedListner = $rootScope.$on('$viewContentLoaded', foo);//Rootscope

    return {
        getLoaded: function() {      
            return viewContentLoaded.promise;
        },
        removeViewContenListner: function() {
            //Remove event listern on $viewContentLoaded no $off so call it will unsubscribe it
            //$rootScope.$off('$viewContentLoaded', foo);//Rootscope
            //Don't forget to unsubscribe later
            checkViewContenLoadedListner();
        }

    };
});

在控制器或指令中包含viewContentLoaded并使用promise调用get函数。不要忘记将应用程序更改为您的应用程序名称,并包含要加载的服务文件。

viewContentLoaded.getLoaded().then(function(){
    //Remove Listner when done
    viewContentLoaded.removeViewContenListner();
    //Your code to run       

}, function(reason) {
    //$log.error(reason);
});