Menjalankan kode inisialisasi AngularJS saat view dimuat

93

Saat saya memuat tampilan, saya ingin menjalankan beberapa kode inisialisasi di pengontrol terkait.

Untuk melakukannya, saya telah menggunakan direktif ng-init pada elemen utama tampilan saya:

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

dan di pengontrol:

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

    $scope.isSaving = false;
}

Pertanyaan pertama: apakah ini cara yang benar untuk melakukannya?

Hal berikutnya, saya punya masalah dengan urutan kejadian yang terjadi. Dalam tampilan saya memiliki tombol 'simpan', yang menggunakan ng-disabledarahan seperti:

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

yang isClean()fungsi didefinisikan di controller:

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

Seperti yang Anda lihat, ini menggunakan $scope.isSavingbendera, yang diinisialisasi dalam init()fungsi.

MASALAH: ketika melihat dimuat, fungsi isClean disebut sebelum ini init()fungsi, maka bendera isSavingadalah undefined. Apa yang dapat saya lakukan untuk mencegahnya?

Sam
sumber

Jawaban:

137

Saat tampilan Anda dimuat, begitu pula pengontrol yang terkait. Daripada menggunakan ng-init, cukup panggil init()metode Anda di pengontrol:

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

Karena pengontrol Anda berjalan sebelumnya ng-init, ini juga menyelesaikan masalah kedua Anda.

Biola


Seperti yang John David Fivedisebutkan, Anda mungkin tidak ingin melampirkan ini ke $scopeuntuk menjadikan metode ini pribadi.

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

Lihat jsFiddle


Jika Anda ingin menunggu data tertentu menjadi preset, pindahkan permintaan data tersebut ke penyelesaian atau tambahkan pengamat ke koleksi atau objek itu dan panggil metode init Anda ketika data Anda memenuhi kriteria init Anda. Saya biasanya menghapus pengamat setelah persyaratan data saya terpenuhi sehingga fungsi init tidak dijalankan kembali secara acak jika data yang Anda tonton berubah dan memenuhi kriteria Anda untuk menjalankan metode init.

var init = function () {
    // do something
}
...
var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
                    if( newVal && newVal.length > 0) {
                        unwatch();
                        init();
                    }
                });
Mark Rajcok
sumber
8
Bagaimana jika Anda membutuhkan data dari beberapa model untuk menjalankan inisialisasi? Atau hanya beberapa data yang tersedia di halaman saat rendering, sehingga inisialisasi dapat berfungsi?
Eugene
38
Fungsi init tidak perlu dilampirkan ke $ scope. Jadikan fungsi init Anda pribadi. Anda tidak ingin fungsi init berjalan lebih dari sekali jadi jangan mengeksposnya di $ scope.
John David Five
2
Saya ingin menjalankan fungsi init setiap kali tampilan saya ditampilkan, tetapi saya tidak tahu caranya, fungsi ini hanya dijalankan sekali. Adakah ide bagaimana saya dapat menjalankannya di setiap pemuatan halaman / template?
Jorre
9
Saya bukan ahli sudut, tetapi pendekatan ini menyebalkan untuk pengujian, karena init () dipanggil hanya dengan instantiasi pengontrol ... dengan kata lain, ketika Anda perlu menguji satu metode pengontrol, init () dipanggil juga. . melanggar tes!
Wagner Leonardi
1
Apa yang dikatakan @WagnerLeonardi. Pendekatan ini membuat pengujian metode init () "pribadi" Anda cukup sulit.
Steven Rogers
36

Sejak AngularJS 1.5 kita harus menggunakan$onInit yang tersedia di semua komponen AngularJS. Diambil dari dokumentasi siklus hidup komponen sejak v1.5 dengan cara yang dipilih:

$ onInit () - Dipanggil pada setiap pengontrol setelah semua pengontrol pada elemen telah dibangun dan bindingnya diinisialisasi (dan sebelum fungsi penautan pra & pasca untuk arahan pada elemen ini). Ini adalah tempat yang baik untuk meletakkan kode inisialisasi untuk pengontrol Anda.

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

>> Demo Fiddle


Contoh lanjutan penggunaan siklus hidup komponen:

Siklus hidup komponen memberi kita kemampuan untuk menangani barang komponen dengan cara yang baik. Ini memungkinkan kita untuk membuat acara untuk misalnya "init", "mengubah" atau "menghancurkan" sebuah komponen. Dengan cara itu kami dapat mengelola hal-hal yang bergantung pada siklus hidup suatu komponen. Contoh kecil ini menunjukkan untuk mendaftarkan & membatalkan pendaftaran $rootScopependengar acara $on. Dengan mengetahui, bahwa peristiwa yang $ondiikat $rootScopetidak akan dibatalkan saat pengontrol kehilangan referensinya dalam tampilan atau dimusnahkan, kita perlu menghancurkan $rootScope.$onlistener secara manual. Tempat yang baik untuk meletakkan barang itu adalah $onDestroyfungsi siklus hidup suatu komponen:

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 biola

lin
sumber
17

Atau Anda bisa menginisialisasi inline di controller. Jika Anda menggunakan fungsi init internal ke pengontrol, fungsi ini tidak perlu didefinisikan dalam cakupan. Faktanya, ini bisa dijalankan sendiri:

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
sumber
1
adakah alasan mengapa kode init berada dalam penutupan anonim?
Adam Tolley
@AdamTolley tidak ada alasan khusus. Dia hanya mendefinisikan suatu fungsi dan segera memanggilnya, tanpa mengikatnya ke var.
Tair
7
Bagaimana Anda bisa menguji unit private init () dengan benar dengan cara ini?
Steven Rogers
Hanya anggota publik yang diuji unitnya. Tes unit tidak boleh bergantung pada apa yang dilakukan kelas secara pribadi untuk mendapatkan hasil yang diharapkan.
Phil
14

Saya menggunakan template berikut dalam proyek saya:

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
sumber
ini terlihat bersih, tetapi iffe menghalangi Anda untuk menjalankan metode yang Anda tentukan pada ruang lingkup, yang sering kali perlu kita lakukan, menjalankannya sekali saat permulaan, dan kemudian memilikinya juga pada ruang lingkup untuk dapat menjalankannya lagi seperti yang diperlukan oleh pengguna
chrismarx
yaitu, jika dijalankan di bagian atas pengontrol-
chrismarx