Ausführen des AngularJS-Initialisierungscodes beim Laden der Ansicht

92

Wenn ich eine Ansicht lade, möchte ich einen Initialisierungscode in dem zugehörigen Controller ausführen.

Zu diesem Zweck habe ich die ng-init-Direktive für das Hauptelement meiner Ansicht verwendet:

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

und in der Steuerung:

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

    $scope.isSaving = false;
}

Erste Frage: Ist das der richtige Weg?

Als nächstes habe ich ein Problem mit der Abfolge der Ereignisse. In der Ansicht habe ich eine Schaltfläche "Speichern", die die ng-disabledDirektive als solche verwendet:

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

Die isClean()Funktion ist in der Steuerung definiert:

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

Wie Sie sehen können, wird das $scope.isSavingFlag verwendet, das in der init()Funktion initialisiert wurde .

Problem: Wenn die Ansicht geladen wird, wird die isClean Funktion aufgerufen , bevor die init()Funktion, damit das Flag isSavingist undefined. Was kann ich tun, um das zu verhindern?

Sam
quelle

Antworten:

136

Wenn Ihre Ansicht geladen wird, wird auch der zugehörige Controller geladen. Anstatt zu verwenden ng-init, rufen Sie einfach Ihre init()Methode in Ihrem Controller auf:

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

Da Ihr Controller zuvor ausgeführt wurde ng-init, wird auch Ihr zweites Problem gelöst.

Geige


Wie bereits John David Fiveerwähnt, möchten Sie dies möglicherweise nicht anhängen $scope, um diese Methode privat zu machen.

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

Siehe jsFiddle


Wenn Sie warten möchten, bis bestimmte Daten voreingestellt sind, verschieben Sie diese Datenanforderung entweder in eine Auflösung oder fügen Sie dieser Sammlung oder diesem Objekt einen Watcher hinzu und rufen Sie Ihre Init-Methode auf, wenn Ihre Daten Ihren Init-Kriterien entsprechen. Normalerweise entferne ich den Watcher, sobald meine Datenanforderungen erfüllt sind, damit die Init-Funktion nicht zufällig erneut ausgeführt wird, wenn sich die von Ihnen beobachteten Daten ändern und Ihre Kriterien zum Ausführen Ihrer Init-Methode erfüllen.

var init = function () {
    // do something
}
...
var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
                    if( newVal && newVal.length > 0) {
                        unwatch();
                        init();
                    }
                });
Mark Rajcok
quelle
8
Was ist, wenn Sie Daten von einigen Modellen benötigen, um die Initialisierung auszuführen? Oder nur einige Daten, die beim Rendern auf der Seite verfügbar sind, damit die Initialisierung funktioniert?
Eugene
38
Eine Init-Funktion muss nicht an $ scope angehängt werden. Machen Sie Ihre Init-Funktion privat. Sie möchten nie, dass eine Init-Funktion mehr als einmal ausgeführt wird. Stellen Sie sie also nicht für $ scope bereit.
John David Five
2
Ich möchte die Init-Funktion jedes Mal ausführen, wenn meine Ansicht angezeigt wird, aber ich habe keine Ahnung, wie, die Funktion wird nur einmal ausgeführt. Irgendwelche Ideen, wie ich es bei jedem Laden von Seiten / Vorlagen ausführen kann?
Jorre
9
Ich bin kein Winkelexperte, aber dieser Ansatz ist zum Testen schlecht, da init () einfach bei der Controller-Instanziierung aufgerufen wird. Mit anderen Worten, wenn Sie eine einzelne Controller-Methode testen müssen, wird auch init () aufgerufen. .Brechen Sie die Tests!
Wagner Leonardi
1
Was @WagnerLeonardi gesagt hat. Dieser Ansatz macht das Testen Ihrer "privaten" init () -Methode ziemlich schwierig.
Steven Rogers
36

Seit AngularJS 1.5 sollten wir verwenden,$onInit was für jede AngularJS-Komponente verfügbar ist. Entnommen aus der Dokumentation zum Komponentenlebenszyklus seit Version 1.5 ist die bevorzugte Methode:

$ onInit () - Wird auf jedem Controller aufgerufen, nachdem alle Controller eines Elements erstellt und ihre Bindungen initialisiert wurden (und vor den Pre- und Post-Linking-Funktionen für die Anweisungen für dieses Element). Dies ist ein guter Ort, um Initialisierungscode für Ihren Controller einzugeben.

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


Ein erweitertes Beispiel für die Verwendung des Komponentenlebenszyklus:

Der Komponentenlebenszyklus gibt uns die Möglichkeit, mit Komponentenmaterial gut umzugehen. Es ermöglicht uns, Ereignisse für z. B. "Init", "Ändern" oder "Zerstören" einer Komponente zu erstellen. Auf diese Weise können wir Dinge verwalten, die vom Lebenszyklus einer Komponente abhängen. Dieses kleine Beispiel zeigt, wie Sie einen $rootScopeEreignis-Listener registrieren und die Registrierung aufheben $on. Wenn wir wissen, dass ein $ongebundenes Ereignis $rootScopenicht unberührt bleibt, wenn der Controller seine Referenz in der Ansicht verliert oder zerstört wird, müssen wir einen $rootScope.$onListener manuell zerstören . Ein guter Ort, um dieses Zeug zu platzieren, ist die $onDestroyLebenszyklusfunktion einer Komponente:

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

>> Geigen-Demo

lin
quelle
17

Oder Sie können einfach Inline im Controller initialisieren. Wenn Sie eine Controller-interne Funktion verwenden, muss diese nicht im Bereich definiert werden. In der Tat kann es selbst ausgeführt werden:

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 }
}
Joseph Oster
quelle
1
Gibt es einen Grund dafür, dass der Init-Code anonym geschlossen wird?
Adam Tolley
@AdamTolley gibt es keinen besonderen Grund. Er definiert nur eine Funktion und ruft sie sofort auf, ohne sie an eine Variable zu binden.
Tair
7
Wie können Sie die private init () - Funktion auf diese Weise richtig testen?
Steven Rogers
Nur öffentliche Mitglieder sind Unit-getestet. Unit-Tests sollten nicht davon abhängen, was Klassen privat tun, um die erwarteten Ergebnisse zu erzielen.
Phil
14

Ich verwende die folgende Vorlage in meinen Projekten:

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  */ {
            // ...
        }

    });
schirrmacher
quelle
Das sieht sauber aus, aber das iffe hindert Sie daran, Methoden auszuführen, die Sie für den Bereich definieren. Dies ist häufig der Fall, müssen Sie sie beim Start einmal ausführen und sie dann auch für den Bereich ausführen, um sie ausführen zu können wieder wie vom Benutzer benötigt
chrismarx
Das heißt, wenn es oben auf dem Controller ausgeführt wird
Chrismarx