Görünüm yüklendiğinde AngularJS başlatma kodunu çalıştırma


93

Bir görünümü yüklediğimde, ilişkili denetleyicisinde bazı başlatma kodunu çalıştırmak istiyorum.

Bunu yapmak için, görüşümün ana unsuru üzerine ng-init direktifini kullandım:

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

ve kontrolörde:

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

    $scope.isSaving = false;
}

İlk soru: bunu yapmanın doğru yolu bu mu?

Sonraki şey, gerçekleşen olayların sıralamasıyla ilgili bir sorunum var. Görünümde, ng-disabledyönergeyi şu şekilde kullanan bir 'kaydet' düğmesi var :

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

isClean()fonksiyon cihaz şu şekilde tanımlanır:

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

Gördüğünüz gibi $scope.isSaving, init()işlevde başlatılan bayrağı kullanıyor .

Sorun: görünüşüdür yüklendiğinde, isClean işlev olarak adlandırılır önceinit() dolayısıyla bayrak, fonksiyon isSavingolduğu undefined. Bunu önlemek için ne yapabilirim?

Yanıtlar:


137

Görünümünüz yüklendiğinde, ilişkili denetleyicisi de yüklenir. Kullanmak yerine, yönteminizi denetleyicinizde ng-initçağırmanız yeterlidir init():

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

Denetleyiciniz daha önce çalıştığından ng-init, bu aynı zamanda ikinci sorununuzu da çözer.

Vaktini boşa harcamak


Belirtildiği gibi John David Five, $scopebu yöntemi özel kılmak için bunu eklemek istemeyebilirsiniz .

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

JsFiddle'a bakın


Belirli verilerin önceden ayarlanmasını beklemek istiyorsanız, bu veri talebini bir çözüme taşıyın veya bu koleksiyona veya nesneye bir izleyici ekleyin ve verileriniz başlatma kriterlerinizi karşıladığında başlatma yönteminizi çağırın. İzleyiciyi genellikle veri gereksinimlerim karşılandığında kaldırırım, böylece izlediğiniz veriler değişirse ve başlatma yönteminizi çalıştırmak için kriterlerinizi karşılarsa başlatma işlevi rastgele yeniden çalıştırılmaz.

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

8
Ya başlatmayı çalıştırmak için bazı modellerden verilere ihtiyacınız varsa? Ya da sadece oluşturma sırasında sayfada bulunan bazı veriler, böylece başlatma işe yarayabilir mi?
Eugene

38
$ Kapsam'a bir init işlevinin eklenmesi gerekmez. İnit işlevinizi özel yapın. Bir init işlevinin bir kereden fazla çalışmasını asla istemezsiniz, bu yüzden onu $ kapsamda göstermeyin.
John David Five

2
Görünümüm her gösterildiğinde init işlevini çalıştırmak istiyorum, ancak nasıl olduğuna dair hiçbir fikrim yok, işlev yalnızca bir kez çalıştırılıyor. Her sayfa / şablon yüklemesinde onu nasıl çalıştırabileceğime dair bir fikriniz var mı?
Jorre

9
Açısal uzman değilim, ancak bu yaklaşım test etmek için berbattır, çünkü init () basitçe denetleyici somutlaştırmasında çağrılır ... başka bir deyişle, tek bir controller yöntemini test etmeniz gerektiğinde, init () de çağrılır. .testleri bozmak!
Wagner Leonardi

1
@WagnerLeonardi ne dedi. Bu yaklaşım, "özel" init () yönteminizi test etmeyi oldukça zorlaştırır.
Steven Rogers

37

AngularJS 1.5'ten beri$onInit , herhangi bir AngularJS bileşeninde mevcut olanı kullanmalıyız . Alındığı bileşen ömrü belgelerinde v1.5 onun tercih ettiği yolu başlangıcı:

$ onInit () - Bir öğedeki tüm denetleyiciler oluşturulduktan ve bağları başlatıldıktan sonra (ve bu öğedeki yönergeler için ön ve son bağlama işlevlerinden önce) her denetleyiciye çağrılır. Bu, denetleyiciniz için başlatma kodunu koymak için iyi bir yerdir.

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

>> Keman Demosu


Bileşen yaşam döngüsünü kullanmanın gelişmiş bir örneği:

Bileşen yaşam döngüsü bize bileşen maddelerini iyi bir şekilde ele alma yeteneği verir. Örneğin, bir bileşenin "başlatılması", "değiştirilmesi" veya "yok edilmesi" için olaylar oluşturmamızı sağlar. Bu şekilde, bir bileşenin yaşam döngüsüne bağlı olan şeyleri yönetebiliriz. Bu küçük örnek, bir $rootScopeolay dinleyicisini kaydetmeyi ve kaydını silmeyi gösterir $on. Bilerek, bir olay olduğunu $onüzerinde binded $rootScopedenetleyici görünümünde kendisiyle referansını kaybettiğinde undinded olmayacak veya tahrip alma biz yok etmek gerekir $rootScope.$onelle dinleyicisi. $onDestroyBunları koymak için iyi bir yer , bir bileşenin yaşam döngüsü işlevidir:

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

>> Fiddle demosu


17

Veya denetleyicide sıralı olarak başlatabilirsiniz. Denetleyicide dahili bir init işlevi kullanırsanız, kapsamda tanımlanmasına gerek yoktur. Aslında, kendi kendine yürütülebilir:

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
Başlatma kodunun anonim olarak kapatılmasının bir nedeni var mı?
Adam Tolley

@AdamTolley belirli bir neden yok. Sadece bir fonksiyonu tanımlar ve onu bir var'a bağlamadan hemen çağırır.
Tair

7
Özel init () işlevinin bu şekilde düzgün çalıştığını nasıl birim test edebilirsiniz?
Steven Rogers

Yalnızca genel üyeler birim testine tabi tutulur. Birim testleri, beklenen sonuçları elde etmek için hangi sınıfların özel olarak yaptığına bağlı olmamalıdır.
Phil

14

Projelerimde aşağıdaki şablonu kullanıyorum:

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

    });

bu temiz görünüyor, ancak iffe, kapsamda tanımladığınız yöntemleri çalıştırmanızı engelliyor, bu genellikle yapmamız gereken şeydir, başlangıçta bir kez çalıştırın ve ardından bunları çalıştırabilmek için kapsamda da bulundurun. kullanıcı tarafından ihtiyaç duyulduğunda tekrar
chrismarx

yani, denetleyicinin tepesinde çalıştırılıyorsa
chrismarx
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.