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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27129829/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 11:21:38  来源:igfitidea点击:

AngularJS: $viewContentLoaded fired before partial view appears

jqueryangularjsdomangular-ui-routerready

提问by Florian

For a partial view I want to do some JavaScript stuff that I usually would do with $(document).ready(function() {...}), e.g. bind venet listeners to elements. I know that this doesn't work for AngularJS and partial views loaded into the "root" view.

对于局部视图,我想做一些我通常会使用的 JavaScript 内容$(document).ready(function() {...}),例如将 venet 侦听器绑定到元素。我知道这不适用于 AngularJS 和加载到“根”视图中的部分视图。

Thus I added a listener to the controller that listens to the $viewContentLoadedevent. The listener's function is invoked, so the event is fired but it seems to me as if it is before the partial view is rendered. Neither do I see the elements when I set a breakpoint in the listener's function and debug it with firebug, nor does the jquery selection within the function find the partial view's elements.

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

This is what the controller looks like:

这是控制器的样子:

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

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

[...]

I guess that I am doing something wrong as there had to be more posts on stackoverflow if this is a common bug.

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

As I am using ui-routerand ui-view, I will give you an excerpt of the routing file:

当我使用ui-routerui-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');
    }])

 [...]

Any help is appreciated. Thanks and kind regards

任何帮助表示赞赏。谢谢和亲切的问候

UPDATE 1:I stripped the error down to the following usecase: The loginView.html looks like the following:

更新 1:我将错误归结为以下用例: loginView.html 如下所示:

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

[...]

As soon as I remove the ng-iffrom the div tag, it works as expected. The event is triggered after the DOM is rendered, thus jQuery finds the element. If the ng-ifis attached to the div tag, the behaviour is as first described.

一旦我ng-if从 div 标签中删除,它就会按预期工作。该事件在 DOM 渲染后触发,从而 jQuery 找到该元素。如果ng-if附加到 div 标签,则行为如最初所述。

UPDATE 2:As promised I added a working demo that shows the different behaviour when adding a ng-ifdirective. Can anyone point me the right direction? Don't stick to the login form as such, as there are many more use cases where I want to remove certain parts of a view based on some expression and do some JavaScript stuff after the partial view is ready.

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

You can find the working demo here: Demo

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

回答by T4deu

This is related to angular digest cycle, it's about how angular works underneath the hood, data binding etc. There are great tutorials explaining this.

这与 angular 摘要循环有关,它是关于 angular 在幕后如何工作、数据绑定等。有很多教程解释了这一点。

To solve your problem, use $timeout, it will make the code execute on the next cycle, whem the ng-if was already parsed:

要解决您的问题,请使用 $timeout,它将使代码在下一个循环中执行,而 ng-if 已被解析:

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

Fixed demo here: http://codepen.io/anon/pen/JoYPdv

固定演示在这里:http: //codepen.io/anon/pen/JoYPdv

But I strongly advise you to use directives do any DOM manipulation, the controller isn't for that. Here is a example of how do this: Easy dom manipulation in AngularJS - click a button, then set focus to an input element

但是我强烈建议你使用指令来做任何 DOM 操作,控制器不适合这个。以下是如何执行此操作的示例: 在 AngularJS 中轻松操作 dom - 单击按钮,然后将焦点设置到输入元素

回答by Sampath Kumar Uppala

I have solved this issue with the help of Directives. Add one direcitve to your element (like <div my-dir></div>) and do manipulations to the element in respective directive as follows,

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

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

I have also tried state provider events like $stateChangeSuccess, $viewContentLoadedbut couldn't solve the issue. Because after those events got fired, it's taking time to render on the DOM.

我也尝试过像 那样的状态提供者事件$stateChangeSuccess$viewContentLoaded但无法解决问题。因为在这些事件被触发后,在 DOM 上渲染需要时间。

So, we can follow this approach which gives perfect results and proper way to implement in Angular JS :)

所以,我们可以遵循这种方法,它给出了完美的结果和在 Angular JS 中实现的正确方法:)

回答by Johan Wergelius

This is an answer to Hades a bit late but might help someone else. I setup a service which I will later be able to call from a controller or directive.

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

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

    };
});

In the controller or directive include viewContentLoaded and call the get function with the promise. Don't forget to change the app to whatever you app name is and include the service file to be loaded.

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

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

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