AngularJS: $ viewContentLoaded acionado antes da exibição parcial aparecer

Para uma visão parcial, eu quero fazer algumas coisas JavaScript que eu normalmente faria com $(document).ready(function() {...}) , por exemplo, ligar os listeners venet aos elementos. Eu sei que isso não funciona para AngularJS e visualizações parciais carregadas na visualização “raiz”.

Assim, adicionei um ouvinte ao controlador que ouve o evento $viewContentLoaded . A function do ouvinte é chamada, portanto, o evento é triggersdo, mas parece-me que é antes de a visualização parcial ser renderizada. Nem vejo os elementos quando defino um ponto de interrupção na function do ouvinte e depuro-o com o firebug, nem a seleção de jquery na function encontra os elementos da vista parcial.

É assim que o controlador se parece:

 angular.module('docinvoiceClientAngularjsApp') .controller('LoginController', function ($scope, $rootScope) { $scope.$on('$viewContentLoaded', function(event) { console.log("content loaded"); console.log($("#loginForm")); // breakpoint here }); [...] 

Eu acho que estou fazendo algo errado, já que tem que ter mais posts no stackoverflow se esse é um bug comum.

Como estou usando o ui-router e o ui-view , vou dar um trecho do arquivo de roteamento:

 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'); }]) [...] 

Qualquer ajuda é apreciada. Obrigado e melhores cumprimentos

UPDATE 1: Eu limpei o erro para o seguinte usecase: O loginView.html se parece com o seguinte:

 
[...]

Assim que eu remover o ng-if da tag div, ele funciona como esperado. O evento é triggersdo depois que o DOM é renderizado, portanto, o jQuery localiza o elemento. Se o ng-if estiver anexado à tag div, o comportamento será o primeiro descrito.

ATUALIZAÇÃO 2: Como prometido, adicionei uma demonstração de trabalho que mostra o comportamento diferente ao adicionar uma diretiva ng-if . Alguém pode me apontar a direção certa? Não se atenha ao formulário de login como tal, pois há muitos mais casos de uso em que desejo remover certas partes de uma exibição com base em alguma expressão e fazer algumas coisas JavaScript depois que a visualização parcial estiver pronta.

Você pode encontrar a demonstração de trabalho aqui: Demo

Isso está relacionado ao ciclo de digestão angular, é sobre como o funcionamento angular sob o capô, vinculação de dados etc. Há excelentes tutoriais explicando isso.

Para resolver seu problema, use $ timeout, ele fará o código executar no próximo ciclo, quando o ng-if já foi analisado:

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

Corrigido a demonstração aqui: http://codepen.io/anon/pen/JoYPdv

Mas eu recomendo fortemente que você use diretivas para qualquer manipulação de DOM, o controlador não é para isso. Aqui está um exemplo de como fazer isso: Fácil manipulação dom no AngularJS – clique em um botão e defina o foco para um elemento de input

Eu resolvi este problema com a ajuda de diretivas. Adicione uma direcitve ao seu elemento (como

) e faça manipulações ao elemento na respectiva diretiva da seguinte maneira,

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

Eu também tentei events de provedor de estado como $stateChangeSuccess , $viewContentLoaded mas não consegui resolver o problema. Porque depois que esses events foram triggersdos, está demorando para renderizar no DOM.

Então, podemos seguir essa abordagem que dá resultados perfeitos e maneira correta de implementar no Angular JS 🙂

Esta é uma resposta para Hades um pouco tarde, mas pode ajudar alguém. Eu configuro um serviço que posteriormente poderei chamar de um controlador ou diretiva.

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