จะเรียกใช้ฟังก์ชันในคอนโทรลเลอร์ AngularJS บนเอกสารได้อย่างไร


254

ฉันมีฟังก์ชั่นภายในตัวควบคุมเชิงมุมของฉันฉันต้องการให้ฟังก์ชันนี้ทำงานบนเอกสารพร้อม แต่ฉันสังเกตเห็นว่าแองกูลาร์นั้นทำงานตามที่สร้างขึ้น

 function myController($scope)
 {
     $scope.init = function()
     {
        // I'd like to run this on document ready
     }

     $scope.init(); // doesn't work, loads my init before the page has completely loaded
 }

ใครรู้ว่าฉันจะไปเกี่ยวกับเรื่องนี้?

คำตอบ:


449

เราสามารถใช้angular.element(document).ready()วิธีการแนบการเรียกกลับเมื่อเอกสารพร้อม เราสามารถแนบการติดต่อกลับในตัวควบคุมดังนี้:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    angular.element(document).ready(function () {
        document.getElementById('msg').innerHTML = 'Hello';
    });
}]);

http://jsfiddle.net/jgentes/stwyvq38/1/


64
หรือคุณสามารถใช้ $ document.ready (function () {... }), เอกสารเชิงมุม: docs.angularjs.org/api/ng/service/$document
StuR

29
คุณจะต้องฉีด$documentเพื่อใช้ angular.elementทำงานนอกกรอบ
nshew

17
คุณจะต้องฉีดเอกสาร $ เพื่อใช้ แต่เป็นวิธีที่แนะนำในการอ้างอิงองค์ประกอบเอกสาร คุณไม่จำเป็นต้องทำ แต่มันทำให้การทดสอบง่ายขึ้น
Jason Cox

10
documentเป็นตัวแปรทั่วโลกที่$documentสามารถฉีดได้ด้วยมุมและทำให้การทดสอบง่ายขึ้น โดยทั่วไปเป็นการยากที่จะทดสอบแอปพลิเคชันหน่วยที่เข้าถึงตัวแปร JS ทั่วโลกเช่นเอกสารหรือหน้าต่าง ฉันยังคิดว่าmodule.runเป็นที่ที่ดีในการวางรหัสนี้แทนคอนโทรลเลอร์
lanoxx

7
วิธีนี้ใช้ไม่ได้สำหรับฉันการเรียกใช้ getElementById ส่งคืนค่า null
ThePartyTurtle

29

ดูโพสต์นี้วิธีการใช้งานฟังก์ชั่นควบคุมเชิงมุมในการโหลดหน้า?
สำหรับการค้นหาอย่างรวดเร็ว:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

วิธีนี้คุณไม่ต้องรอจนกว่าเอกสารจะพร้อม


1
จะทำอย่างไรถ้าฉันต้องการโทรหลังจากโหลดหน้า?
ฤishiษี

22

เริ่มต้นเชิงมุมอัตโนมัติเมื่อมีDOMContentLoadedเหตุการณ์หรือเมื่อสคริปต์ angular.js ถูกประเมินว่าในขณะนั้น document.readyState ถูกตั้งค่าเป็น 'สมบูรณ์' ณ จุดนี้ Angular ค้นหาคำสั่ง ng-app ซึ่งกำหนดรูทแอปพลิเคชันของคุณ

https://docs.angularjs.org/guide/bootstrap

ซึ่งหมายความว่ารหัสคอนโทรลเลอร์จะทำงานหลังจากที่ DOM พร้อมใช้งาน

$scope.init()ดังนั้นมันจึงเป็นเพียง


9
ฉันพยายามที่จะทำอย่างนั้น แต่แปลกที่มันไม่ได้ทำงานบางอย่างในหน้าของฉันไม่ได้โหลด
foreyez

และวิธีการทราบเมื่อรหัสแองกูลาร์พร้อมก่อนดำเนินการเช่นการคลิกปุ่ม ฯลฯ เมื่อเขียนการทดสอบเว็บ aotomated?
akostadinov

นอกจากนี้ยังDOMContentLoadedถูกไล่ออกเมื่อเอกสารได้รับการโหลดและแยกวิเคราะห์อย่างสมบูรณ์โดยไม่ต้องรอสไตล์ชีตรูปภาพและเฟรมย่อยเพื่อให้การโหลดเสร็จสิ้น สำหรับข้อมูลเพิ่มเติมให้ดูที่ DOMContentLoaded และโหลดเหตุการณ์แตกต่างกันอย่างไร .
georgeawg

22

Angular มีหลาย timepoints เพื่อเริ่มการใช้งานฟังก์ชั่น หากคุณมองหาบางอย่างเช่น jQuery

$(document).ready();

คุณอาจพบว่าอะนาล็อกในเชิงมุมมีประโยชน์มาก:

$scope.$watch('$viewContentLoaded', function(){
    //do something
});

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

UPD: สิ่งที่กล่าวข้างต้นทำงานเมื่อคุณต้องการเปลี่ยนคุณสมบัติ css อย่างไรก็ตามบางครั้งมันไม่ทำงานเมื่อคุณต้องการวัดคุณสมบัติขององค์ประกอบเช่นความกว้างความสูง ฯลฯ ในกรณีนี้คุณอาจต้องการลอง:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

สิ่งนี้สมควรได้รับมากกว่าคำตอบที่แนะนำ ฉันได้งานนี้โดยไม่ใช้ $ timeout
Richie86

มันใช้งานได้สำหรับฉันเท่านั้นกับ setTimeout (0) - ไม่มีเนื้อหาภายใต้ ng-repeat ตัวอย่างเช่นยังไม่โหลดตอนนี้
Ignacio Vazquez

2

ฉันมีสถานการณ์ที่คล้ายกันซึ่งฉันต้องใช้งานฟังก์ชั่นคอนโทรลเลอร์หลังจากโหลดมุมมองและหลังจากองค์ประกอบบุคคลที่สามภายในมุมมองถูกโหลดเริ่มต้นและวางการอ้างอิงกับตัวเองในขอบเขต $ สิ่งที่ได้ผลสำหรับฉันคือการตั้งค่านาฬิกาบนคุณสมบัติขอบเขตนี้และใช้ฟังก์ชันของฉันหลังจากที่เริ่มต้นแล้วเท่านั้น

// $scope.myGrid property will be created by the grid itself
// The grid will have a loadedRows property once initialized

$scope.$watch('myGrid', function(newValue, oldValue) {
    if (newValue && newValue.loadedRows && !oldValue) {
        initializeAllTheGridThings();
    }
});

Watcher เรียกว่าสองสามครั้งด้วยค่าที่ไม่ได้กำหนด จากนั้นเมื่อสร้างกริดและมีคุณสมบัติที่คาดไว้ฟังก์ชันการกำหนดค่าเริ่มต้นอาจถูกเรียกอย่างปลอดภัย ครั้งแรกที่ตัวเรียกใช้งานด้วยค่าใหม่ที่ไม่ได้กำหนดค่าเดิมค่าเก่าจะยังไม่ถูกกำหนด


1

นี่คือความพยายามของฉันภายในตัวควบคุมภายนอกโดยใช้ coffeescript มันใช้งานได้ค่อนข้างดี โปรดทราบว่า settings.screen.xs | sm | md | lg เป็นค่าคงที่ที่กำหนดในไฟล์ที่ไม่ใช่ uglified ฉันรวมกับแอพ ค่าดังกล่าวเป็นจุดพักอย่างเป็นทางการของ Bootstrap 3 สำหรับขนาดคิวรีสื่อบันทึกบาร์นี้:

xs = settings.screen.xs // 480
sm = settings.screen.sm // 768
md = settings.screen.md // 992
lg = settings.screen.lg // 1200

doMediaQuery = () ->

    w = angular.element($window).width()

    $scope.xs = w < sm
    $scope.sm = w >= sm and w < md
    $scope.md = w >= md and w < lg
    $scope.lg = w >= lg
    $scope.media =  if $scope.xs 
                        "xs" 
                    else if $scope.sm
                        "sm"
                    else if $scope.md 
                        "md"
                    else 
                        "lg"

$document.ready () -> doMediaQuery()
angular.element($window).bind 'resize', () -> doMediaQuery()

1

คำตอบ

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

เป็นคนเดียวที่ทำงานในสถานการณ์ส่วนใหญ่ที่ฉันทดสอบ ในหน้าตัวอย่างที่มี 4 องค์ประกอบทั้งหมดซึ่งสร้าง HTML จากเทมเพลตลำดับของเหตุการณ์คือ

$document ready
$onInit
$postLink
(and these 3 were repeated 3 more times in the same order for the other 3 components)
$viewContentLoaded (repeated 3 more times)
$timeout execution (repeated 3 more times)

ดังนั้น $ document.ready () จึงไม่มีประโยชน์ในกรณีส่วนใหญ่เนื่องจาก DOM ที่สร้างในเชิงมุมอาจไม่มีที่ใกล้เคียง

แต่น่าสนใจยิ่งขึ้นแม้หลังจากที่ $ viewContentLoaded โหลดแล้วองค์ประกอบที่น่าสนใจก็ยังไม่พบ

หลังจากพบการดำเนินการ $ timeout เท่านั้น โปรดทราบว่าแม้ว่าการหมดเวลาของ $ จะมีค่าเป็น 0 แต่ผ่านไปเกือบ 200 มิลลิวินาทีก่อนที่จะถูกดำเนินการแสดงว่าเธรดนี้ถูกพักไว้ชั่วครู่หนึ่งสันนิษฐานว่าในขณะที่ DOM มีเทมเพลตเชิงมุมเพิ่มบนเธรดหลัก เวลาทั้งหมดจาก $ document.ready () ครั้งแรกถึงการดำเนินการหมดเวลา $ ครั้งสุดท้ายคือเกือบ 500 มิลลิวินาที

ในกรณีพิเศษหนึ่งกรณีที่มีการตั้งค่าองค์ประกอบและจากนั้นค่าข้อความ () จะถูกเปลี่ยนในภายหลังในการหมดเวลา $ $ ค่าการหมดเวลา $ จะต้องเพิ่มขึ้นจนกว่าจะทำงาน (แม้ว่าจะพบองค์ประกอบในระหว่างการหมดเวลา $ ) บางสิ่งบางอย่างในอุปกรณ์ของบุคคลที่สามทำให้เกิดค่า async นำหน้าข้อความจนเวลาผ่านไป ความเป็นไปได้อีกอย่างคือ $ scope $ evalAsync แต่ไม่ได้ลอง

ฉันยังคงมองหาเหตุการณ์หนึ่งที่บอกฉันว่า DOM ได้ตัดสินอย่างสมบูรณ์และสามารถจัดการเพื่อให้ทุกกรณีทำงานได้ จนถึงตอนนี้จำเป็นต้องใช้ค่าไทม์เอาต์โดยพลการซึ่งหมายความว่านี่เป็น kludge ที่อาจไม่ทำงานบนเบราว์เซอร์ที่ทำงานช้า ฉันไม่ได้ลองตัวเลือก JQuery เช่น liveQuery และเผยแพร่ / สมัครสมาชิกซึ่งอาจใช้งานได้ แต่แน่นอนว่าไม่ใช่เชิงมุมที่บริสุทธิ์


1

หากคุณได้รับสิ่งที่ต้องการgetElementById call returns nullอาจเป็นเพราะฟังก์ชั่นกำลังทำงานอยู่ แต่ ID ไม่มีเวลาโหลดใน DOM

ลองใช้คำตอบของ Will (ไปด้านบน) ด้วยความล่าช้า ตัวอย่าง:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    $scope.sleep = (time) => {
        return new Promise((resolve) => setTimeout(resolve, time));
    };
    angular.element(document).ready(function () {
        $scope.sleep(500).then(() => {        
            //code to run here after the delay
        });
    });
}]);

0

ทำไมไม่ลองกับสิ่งที่พูดถึงเอกสารเชิงมุมhttps://docs.angularjs.org/api/ng/function/angular.element

angular.element (โทรกลับ)

ฉันเคยใช้สิ่งนี้ในฟังก์ชั่น $ onInit () {... } ของฉัน

 var self = this;

 angular.element(function () {
        var target = document.getElementsByClassName('unitSortingModule');
        target[0].addEventListener("touchstart", self.touchHandler, false);
        ...
    });

สิ่งนี้ใช้ได้สำหรับฉัน


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