2014-11-25 9 views
22

Per una vista parziale, desidero eseguire alcune operazioni JavaScript che di solito farei con $(document).ready(function() {...}), ad es. legare gli ascoltatori al veneto agli elementi. So che questo non funziona per AngularJS e le viste parziali caricate nella vista "root".

Così ho aggiunto un ascoltatore al controller che ascolta l'evento $viewContentLoaded. La funzione del listener viene invocata, quindi l'evento viene generato ma mi sembra che sia prima che venga visualizzata la vista parziale. Né vedo gli elementi quando imposto un breakpoint nella funzione dell'ascoltatore e lo faccio il debug con firebug, né la selezione jquery all'interno della funzione trova gli elementi della vista parziale.

Questo è ciò che il controller appare come:

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

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

[...] 

Credo che sto facendo qualcosa di sbagliato come ci doveva essere più messaggi su StackOverflow se questo è un bug comune.

Come sto utilizzando ui-router e ui-view, io vi darà un estratto del file di routing:

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

[...] 

Ogni aiuto è apprezzato. Grazie e cordiali saluti

UPDATE 1: ho messo a nudo l'errore verso il seguente caso d'uso: Il loginView.html è simile al seguente:

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

[...] 

Appena posso rimuovere la ng-if dal tag div , funziona come previsto. L'evento viene attivato dopo il rendering del DOM, quindi jQuery trova l'elemento. Se lo ng-if è collegato al tag div, il comportamento è come descritto in precedenza.

UPDATE 2: Come promesso, ho aggiunto una demo funzionante che mostra il diverso comportamento quando si aggiunge una direttiva ng-if. Qualcuno può indicarmi la giusta direzione? Non attenersi al modulo di accesso in quanto tale, poiché ci sono molti altri casi d'uso in cui voglio rimuovere alcune parti di una vista basate su alcune espressioni e fare alcuni elementi JavaScript dopo che la vista parziale è pronta.

È possibile trovare la demo funzionante qui: Demo

+0

È possibile creare una direttiva per voi dom roba. – dfsq

+1

Ho creato una [demo di lavoro] (http://codepen.io/anon/pen/gbpmQy) in base al codice e funziona correttamente. La prossima volta, prova a fornire la demo da solo. – hon2a

+0

Mille grazie per la demo di lavoro. Ho aggiornato la descrizione come ho scoperto che il ng-if causa il diverso comportamento. Hai un'idea del perché si comporta diversamente? L'espressione viene valutata su "true" come posso vedere il form di login, solo un momento troppo tardi;) – Florian

risposta

39

Questo è legato al ciclo di digerire angolare, si tratta di come funziona angolare sotto il cofano, l'associazione dati, ecc Ci sono grandi tutorial che spiegano questo.

Per risolvere il problema, l'uso $ timeout, renderà il codice eseguire nel ciclo successivo, whem il ng, se è stato già analizzato:

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

fisso demo qui: http://codepen.io/anon/pen/JoYPdv

Ma Vi consiglio caldamente di usare le direttive per manipolare DOM, il controller non è per questo. Ecco un esempio di come eseguire questa operazione: Easy dom manipulation in AngularJS - click a button, then set focus to an input element

+0

Considererò questa la risposta più preziosa, in quanto fornisce una possibile soluzione e un suggerimento per trovare ulteriori informazioni sul ciclo di digest, anche se ora sto usando la direttiva. – Florian

+0

Molto ben spiegato! Sono stato in grado di integrare App.js e AngularJS usando il chunk di codice di cui sopra e ho capito anche i dettagli complicati del timeout. – Nirus

+6

Il problema con questo approccio è che in ui-router $ viewContentLoaded si attiva per ogni vista. Se hai un sacco di punti di vista in una singola pagina il codice sarà eseguito più volte. C'è un modo per eseguirlo una sola volta quando TUTTE le visualizzazioni sono state caricate? – Hades

0

Questa è una risposta ad Ade un po 'in ritardo ma potrebbe aiutare qualcun altro. Ho configurato un servizio che in seguito potrò chiamare da un controller o da una direttiva.

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

 
    }; 
 
});

Nel controllore o direttiva comprendono viewContentLoaded e chiamare la funzione get con la promessa. Non dimenticare di cambiare l'app con qualsiasi nome dell'app e includere il file di servizio da caricare.

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

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

1

ho risolto questo problema con l'aiuto di direttive. Aggiungere una direcitve al vostro elemento (come <div my-dir></div>) e fare manipolazioni all'elemento nel rispettivo direttiva come segue,

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

Ho anche provato Avvenimenti statali come $stateChangeSuccess, $viewContentLoaded ma non sono riuscito a risolvere il problema. Perché dopo che questi eventi sono stati licenziati, ci vuole del tempo per eseguire il rendering sul DOM.

Quindi, siamo in grado di seguire questo approccio, che dà risultati perfetti e modo corretto di attuare nella angolare JS :)