การรันโค้ดเริ่มต้น AngularJS เมื่อโหลดมุมมอง


93

เมื่อฉันโหลดมุมมองฉันต้องการเรียกใช้รหัสเริ่มต้นในตัวควบคุมที่เกี่ยวข้อง

ในการทำเช่นนั้นฉันได้ใช้คำสั่ง ng-init กับองค์ประกอบหลักของมุมมองของฉัน:

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

และในตัวควบคุม:

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

    $scope.isSaving = false;
}

คำถามแรก: นี่เป็นวิธีที่ถูกต้องหรือไม่?

สิ่งต่อไปฉันมีปัญหากับลำดับเหตุการณ์ที่เกิดขึ้น ในมุมมองฉันมีปุ่ม 'บันทึก' ซึ่งใช้ng-disabledคำสั่งเช่นนี้:

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

isClean()ฟังก์ชั่นที่กำหนดไว้ในการควบคุม:

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

อย่างที่คุณเห็นมันใช้$scope.isSavingแฟล็กซึ่งเริ่มต้นในinit()ฟังก์ชัน

ปัญหา: เมื่อมุมมองที่มีการโหลดฟังก์ชั่น isClean เรียกว่าก่อนที่จะinit()ฟังก์ชั่นจึงธงคือisSaving undefinedฉันจะทำอย่างไรเพื่อป้องกันสิ่งนั้น

คำตอบ:


137

เมื่อมุมมองของคุณโหลดขึ้นตัวควบคุมที่เกี่ยวข้องก็เช่นกัน แทนที่จะใช้ng-initเพียงแค่เรียกinit()วิธีการของคุณในคอนโทรลเลอร์ของคุณ:

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

เนื่องจากคอนโทรลเลอร์ของคุณทำงานมาก่อนng-initสิ่งนี้จะช่วยแก้ปัญหาที่สองของคุณด้วย

ซอ


ในฐานะที่เป็นJohn David Fiveที่กล่าวถึงคุณอาจไม่ต้องการที่จะแนบนี้$scopeเพื่อที่จะทำให้วิธีการส่วนตัว

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

ดู jsFiddle


หากคุณต้องการรอให้ข้อมูลบางอย่างถูกตั้งค่าไว้ล่วงหน้าให้ย้ายคำขอข้อมูลนั้นไปยังการแก้ไขหรือเพิ่มผู้เฝ้าดูไปยังคอลเล็กชันหรืออ็อบเจ็กต์นั้นและเรียกใช้วิธีการเริ่มต้นเมื่อข้อมูลของคุณตรงตามเกณฑ์การเริ่มต้นของคุณ ฉันมักจะลบผู้เฝ้าดูเมื่อมีคุณสมบัติตรงตามข้อกำหนดของข้อมูลดังนั้นฟังก์ชัน init จึงไม่ทำงานแบบสุ่มอีกครั้งหากข้อมูลการรับชมของคุณเปลี่ยนแปลงและตรงตามเกณฑ์ของคุณเพื่อเรียกใช้วิธีการเริ่มต้นของคุณ

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

8
จะเกิดอะไรขึ้นหากคุณต้องการข้อมูลจากบางรุ่นเพื่อเรียกใช้การเริ่มต้น หรือมีเพียงข้อมูลบางส่วนที่มีอยู่ในเพจเมื่อทำการเรนเดอร์เพื่อให้การเริ่มต้นทำงานได้?
Eugene

38
ฟังก์ชัน init ไม่จำเป็นต้องเชื่อมต่อกับ $ scope ทำให้ฟังก์ชัน init ของคุณเป็นส่วนตัว คุณไม่ต้องการให้ฟังก์ชัน init ทำงานมากกว่าหนึ่งครั้งดังนั้นอย่าเปิดเผยใน $ scope
John David Five

2
ฉันต้องการเรียกใช้ฟังก์ชัน init ทุกครั้งที่มีการแสดงมุมมองของฉัน แต่ฉันไม่รู้ว่าจะทำอย่างไรฟังก์ชันนี้จะทำงานเพียงครั้งเดียว มีแนวคิดอย่างไรในการเรียกใช้งานในทุก ๆ หน้า / การโหลดเทมเพลต

9
ฉันไม่ใช่ผู้เชี่ยวชาญเชิงมุม แต่วิธีนี้ใช้สำหรับการทดสอบเนื่องจาก init () ถูกเรียกที่เพียงแค่อินสแตนซ์ของคอนโทรลเลอร์ ... กล่าวอีกนัยหนึ่งคือเมื่อคุณต้องการทดสอบวิธีการควบคุมวิธีเดียวจะเรียก init () เช่นกัน . ทำลายการทดสอบ!
Wagner Leonardi

1
@WagnerLeonardi พูดอะไร วิธีนี้ทำให้การทดสอบวิธี init () "ส่วนตัว" ของคุณค่อนข้างยาก
Steven Rogers

36

เนื่องจาก AngularJS 1.5 เราควรใช้$onInitซึ่งมีอยู่ในส่วนประกอบ AngularJS ใด ๆ นำมาจากเอกสารประกอบวงจรชีวิตตั้งแต่ v1.5 เป็นวิธีที่เตรียมไว้ล่วงหน้า:

$ onInit () - เรียกบนตัวควบคุมแต่ละตัวหลังจากที่ตัวควบคุมทั้งหมดในองค์ประกอบได้รับการสร้างและเริ่มต้นการเชื่อมโยง (และก่อนฟังก์ชันการเชื่อมโยงก่อนและหลังสำหรับคำสั่งในองค์ประกอบนี้) นี่เป็นจุดที่ดีในการใส่รหัสเริ่มต้นสำหรับคอนโทรลเลอร์ของคุณ

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

>> การสาธิตซอ


ตัวอย่างขั้นสูงของการใช้วงจรชีวิตของส่วนประกอบ:

วงจรชีวิตของส่วนประกอบช่วยให้เราสามารถจัดการกับส่วนประกอบต่างๆได้อย่างดี ช่วยให้เราสามารถสร้างเหตุการณ์เช่น "init" "เปลี่ยนแปลง" หรือ "ทำลาย" ของส่วนประกอบ ด้วยวิธีนี้เราสามารถจัดการสิ่งต่างๆได้ซึ่งขึ้นอยู่กับวงจรชีวิตของส่วนประกอบ เล็ก ๆ น้อย ๆ นี้แสดงให้เห็นตัวอย่างในการลงทะเบียนและการถอนการลงทะเบียนฟังเหตุการณ์$rootScope $onเมื่อทราบว่าเหตุการณ์ที่$onผูกไว้$rootScopeจะไม่ถูกยกเลิกเมื่อคอนโทรลเลอร์สูญเสียการอ้างอิงในมุมมองหรือถูกทำลายเราจำเป็นต้องทำลาย$rootScope.$onผู้ฟังด้วยตนเอง สถานที่ที่ดีในการใส่สิ่งนั้นคือ$onDestroyฟังก์ชันวงจรชีวิตของส่วนประกอบ:

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

>> การสาธิตซอ


17

หรือคุณสามารถเริ่มต้นอินไลน์ในตัวควบคุม หากคุณใช้ฟังก์ชัน init ภายในตัวควบคุมไม่จำเป็นต้องกำหนดไว้ในขอบเขต ในความเป็นจริงมันสามารถดำเนินการได้ด้วยตนเอง:

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
มีสาเหตุที่ทำให้รหัส init อยู่ในการปิดโดยไม่ระบุชื่อ
Adam Tolley

@AdamTolley ไม่มีเหตุผลใดเป็นพิเศษ เขาเพียงกำหนดฟังก์ชันและเรียกใช้ทันทีโดยไม่ผูกมัดกับตัวแปร
Tair

7
คุณจะทดสอบการทำงานของ private init () อย่างถูกต้องด้วยวิธีนี้ได้อย่างไร
Steven Rogers

เฉพาะสมาชิกสาธารณะเท่านั้นที่ได้รับการทดสอบหน่วย การทดสอบหน่วยไม่ควรขึ้นอยู่กับสิ่งที่ชั้นเรียนทำแบบส่วนตัวเพื่อให้ได้ผลลัพธ์ที่คาดหวัง
ฟิลิป

14

ฉันใช้เทมเพลตต่อไปนี้ในโครงการของฉัน:

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

    });

สิ่งนี้ดูสะอาด แต่ iffe ขัดขวางคุณจากการเรียกใช้วิธีการที่คุณกำหนดขอบเขตซึ่งมักเป็นสิ่งที่เราต้องทำเรียกใช้ครั้งเดียวเมื่อเริ่มต้นจากนั้นให้พวกเขาอยู่ในขอบเขตเพื่อให้สามารถรันได้ อีกครั้งตามที่ผู้ใช้ต้องการ
chrismarx

นั่นคือถ้ามันทำงานที่ด้านบนของคอนโทรลเลอร์
chrismarx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.