Esecuzione del codice di inizializzazione AngularJS quando viene caricata la vista


92

Quando carico una vista, vorrei eseguire del codice di inizializzazione nel controller associato.

Per fare ciò, ho usato la direttiva ng-init sull'elemento principale della mia vista:

<div ng-init="init()">
  blah
</div>

e nel controller:

$scope.init = function () {
    if ($routeParams.Id) {
        //get an existing object
        });
    } else {
       //create a new object
    }

    $scope.isSaving = false;
}

Prima domanda: è questo il modo giusto per farlo?

La prossima cosa, ho un problema con la sequenza degli eventi che si verificano. Nella vista ho un pulsante 'salva', che utilizza la ng-disableddirettiva come tale:

<button ng-click="save()" ng-disabled="isClean()">Save</button>

la isClean()funzione è definita nel controller:

$scope.isClean = function () {
    return $scope.hasChanges() && !$scope.isSaving;
}

Come puoi vedere, utilizza il $scope.isSavingflag, che è stato inizializzato nella init()funzione.

PROBLEMA: quando la vista viene caricata, la funzione isClean viene chiamata prima della init()funzione, quindi il flag isSavingè undefined. Cosa posso fare per impedirlo?

Risposte:


136

Quando la tua vista viene caricata, anche il controller associato. Invece di usare ng-init, chiama semplicemente il tuo init()metodo nel controller:

$scope.init = function () {
    if ($routeParams.Id) {
        //get an existing object
    } else {
        //create a new object
    }
    $scope.isSaving = false;
}
...
$scope.init();

Poiché il tuo controller funziona prima ng-init , questo risolve anche il secondo problema.

Violino


Come John David Fiveaccennato, potresti non voler allegare questo $scopeper rendere privato questo metodo.

var init = function () {
    // do something
}
...
init();

Vedi jsFiddle


Se vuoi aspettare che determinati dati siano preimpostati, sposta quella richiesta di dati in una risoluzione o aggiungi un watcher a quella raccolta o oggetto e chiama il tuo metodo init quando i tuoi dati soddisfano i tuoi criteri di init. Di solito rimuovo il watcher una volta che i miei requisiti di dati sono soddisfatti, quindi la funzione init non viene rieseguita in modo casuale se i dati che stai guardando cambiano e soddisfano i tuoi criteri per eseguire il tuo metodo init.

var init = function () {
    // do something
}
...
var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
                    if( newVal && newVal.length > 0) {
                        unwatch();
                        init();
                    }
                });

8
Cosa succede se hai bisogno di dati da alcuni modelli per eseguire l'inizializzazione? O solo alcuni dati disponibili sulla pagina durante il rendering, in modo che l'inizializzazione possa funzionare?
Eugene

38
Non è necessario allegare una funzione init a $ scope. Rendi privata la tua funzione init. Non vuoi mai che una funzione init venga eseguita più di una volta, quindi non esporla su $ scope.
John David Five

2
Vorrei eseguire la funzione init ogni volta che viene mostrata la mia vista ma non ho idea di come, la funzione viene eseguita solo una volta. Qualche idea su come eseguirlo a ogni caricamento di pagina / modello?
Jorre

9
Non sono un esperto angolare, ma questo approccio fa schifo per i test, poiché init () viene chiamato semplicemente all'istanziazione del controller ... in altre parole, quando è necessario testare un singolo metodo di controller, viene chiamato anche init (). .rompendo le prove!
Wagner Leonardi

1
Cosa ha detto @WagnerLeonardi. Questo approccio rende il test del tuo metodo init () "privato" piuttosto difficile.
Steven Rogers

36

A partire da AngularJS 1.5 dovremmo usare$onInit che è disponibile su qualsiasi componente AngularJS. Tratto dalla documentazione del ciclo di vita dei componenti a partire dalla v1.5 è il modo preferito:

$ onInit () - Chiamato su ogni controller dopo che tutti i controller su un elemento sono stati costruiti e le loro associazioni sono state inizializzate (e prima delle funzioni di pre e post collegamento per le direttive su questo elemento). Questo è un buon posto per inserire il codice di inizializzazione per il controller.

var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {

    //default state
    $scope.name = '';

    //all your init controller goodness in here
    this.$onInit = function () {
      $scope.name = 'Superhero';
    }
});

>> Fiddle Demo


Un esempio avanzato di utilizzo del ciclo di vita dei componenti:

Il ciclo di vita dei componenti ci dà la capacità di gestire le cose dei componenti in un buon modo. Ci permette di creare eventi per es. "Init", "change" o "distruggi" di un componente. In questo modo siamo in grado di gestire cose che dipendono dal ciclo di vita di un componente. Questo piccolo esempio mostra come registrare e annullare la registrazione di un $rootScopelistener di eventi $on. Conoscendo, che un evento $onbinded sulla $rootScopenon sarà undinded quando il controllore perde il suo riferimento nella vista o di ottenere distrutto abbiamo bisogno di distruggere un $rootScope.$onascoltatore manualmente. Un buon posto dove mettere queste cose è la $onDestroyfunzione del ciclo di vita di un componente:

var myApp = angular.module('myApp',[]);

myApp.controller('MyCtrl', function ($scope, $rootScope) {

  var registerScope = null;

  this.$onInit = function () {
    //register rootScope event
    registerScope = $rootScope.$on('someEvent', function(event) {
        console.log("fired");
    });
  }

  this.$onDestroy = function () {
    //unregister rootScope event by calling the return function
    registerScope();
  }
});

>> Demo di violino


17

Oppure puoi semplicemente inizializzare inline nel controller. Se si utilizza una funzione di inizializzazione interna al controller, non è necessario definirla nell'ambito. In effetti, può essere autoeseguito:

function MyCtrl($scope) {
    $scope.isSaving = false;

    (function() {  // init
        if (true) { // $routeParams.Id) {
            //get an existing object
        } else {
            //create a new object
        }
    })()

    $scope.isClean = function () {
       return $scope.hasChanges() && !$scope.isSaving;
    }

    $scope.hasChanges = function() { return false }
}

1
c'è una ragione per cui il codice init è in una chiusura anonima?
Adam Tolley,

@AdamTolley non c'è motivo particolare. Definisce semplicemente una funzione e la chiama immediatamente, senza associarla a una variabile.
Tair

7
Come puoi testare correttamente la funzione private init () in questo modo?
Steven Rogers

Solo i membri pubblici vengono sottoposti a test di unità. I test unitari non dovrebbero dipendere da ciò che le classi fanno privatamente per ottenere i risultati attesi.
Phil

14

Uso il seguente modello nei miei progetti:

angular.module("AppName.moduleName", [])

/**
 * @ngdoc controller
 * @name  AppName.moduleName:ControllerNameController
 * @description Describe what the controller is responsible for.
 **/
    .controller("ControllerNameController", function (dependencies) {

        /* type */ $scope.modelName = null;
        /* type */ $scope.modelName.modelProperty1 = null;
        /* type */ $scope.modelName.modelPropertyX = null;

        /* type */ var privateVariable1 = null;
        /* type */ var privateVariableX = null;

        (function init() {
            // load data, init scope, etc.
        })();

        $scope.modelName.publicFunction1 = function () /* -> type  */ {
            // ...
        };

        $scope.modelName.publicFunctionX = function () /* -> type  */ {
            // ...
        };

        function privateFunction1() /* -> type  */ {
            // ...
        }

        function privateFunctionX() /* -> type  */ {
            // ...
        }

    });

questo sembra pulito, ma l'iffe ti impedisce di eseguire metodi che stai definendo sull'ambito, che è spesso ciò che dobbiamo fare, eseguirli una volta all'avvio e quindi averli anche sull'ambito per poterli eseguire sempre secondo le necessità dell'utente
chrismarx

cioè, se viene eseguito nella parte superiore del controller-
chrismarx
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.