web-dev-qa-db-fra.com

AngularJS: $ viewContentLoaded déclenché avant l'affichage partiel

Pour une vue partielle, je souhaite effectuer des opérations JavaScript que je ferais habituellement avec $(document).ready(function() {...}), par exemple. lie les auditeurs de venet aux éléments. Je sais que cela ne fonctionne pas pour AngularJS et les vues partielles chargées dans la vue "racine".

J'ai donc ajouté un contrôleur au contrôleur qui écoute l'événement $viewContentLoaded. La fonction de l'auditeur étant invoquée, l'événement est déclenché, mais il me semble que cela se produit avant le rendu de la vue partielle. Je ne vois pas non plus les éléments lorsque je définis un point d'arrêt dans la fonction de l'écouteur et que je le débogue avec firebug, et la sélection jQuery dans la fonction ne recherche pas les éléments de la vue partielle.

Voici à quoi ressemble le contrôleur:

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

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

[...]

Je suppose que je fais quelque chose de mal, car il devait y avoir plus de publications sur stackoverflow s’il s’agissait d’un bogue courant.

Comme j'utilise ui-router et ui-view, je vais vous donner un extrait du fichier de routage:

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

 [...]

Toute aide est appréciée. Merci et salutations

UPDATE 1: J'ai réduit l'erreur au cas d'utilisation suivant: Le fichier loginView.html ressemble à ceci: 

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

[...]

Dès que je supprime le ng-if de la balise div, cela fonctionne comme prévu. L'événement est déclenché après le rendu du DOM. JQuery trouve donc l'élément. Si le ng-if est associé à la balise div, le comportement est celui décrit en premier.

UPDATE 2: Comme promis, j'ai ajouté une démonstration fonctionnant différemment lors de l'ajout d'une directive ng-if. Quelqu'un peut-il m'indiquer la bonne direction? Ne vous en tenez pas au formulaire de connexion en tant que tel, car il existe de nombreux autres cas dans lesquels je souhaite supprimer certaines parties d'une vue en fonction de certaines expressions et effectuer des opérations JavaScript une fois que la vue partielle est prête.

Vous pouvez trouver la démo de travail ici: Demo

24
Florian

J'ai résolu ce problème à l'aide de directives. Ajouter un direcitve à votre élément (comme <div my-dir></div>) et faire des manipulations à l'élément dans la directive respective comme suit,

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

J'ai également essayé des événements de fournisseur d'état tels que $stateChangeSuccess, $viewContentLoaded mais je n'ai pas pu résoudre le problème. Parce qu'après le déclenchement de ces événements, le rendu sur le DOM prend du temps.

Donc, nous pouvons suivre cette approche qui donne des résultats parfaits et une manière appropriée de mettre en œuvre dans Angular JS :)

1

Ceci est une réponse à Hadès un peu tard, mais pourrait aider quelqu'un d'autre. Je configure un service que je pourrai appeler plus tard d’un contrôleur ou d’une directive.

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

    };
});

Dans le contrôleur ou la directive, incluez viewContentLoaded et appelez la fonction get avec la promesse. N'oubliez pas de modifier l'application en votre nom d'application et d'inclure le fichier de service à charger.

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

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

0
Johan Wergelius