Bagaimana menjalankan fungsi di pengontrol AngularJS pada dokumen siap?

254

Saya memiliki fungsi dalam pengontrol sudut saya, saya ingin fungsi ini dijalankan pada dokumen siap tetapi saya perhatikan bahwa sudut menjalankannya saat dom dibuat.

 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
 }

Adakah yang tahu bagaimana saya bisa melakukan ini?

foreyez
sumber

Jawaban:

449

Kita dapat menggunakan angular.element(document).ready()metode untuk melampirkan panggilan balik ketika dokumen sudah siap. Kami cukup melampirkan callback di controller seperti:

angular.module('MyApp', [])

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

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

Akan
sumber
64
atau Anda dapat menggunakan $ document.ready (function () {...}), Angular Docs: docs.angularjs.org/api/ng/service/$document
StuR
29
Anda harus menyuntikkan $documentuntuk menggunakannya. angular.elementbekerja di luar kotak.
nshew
17
Anda perlu menyuntikkan $ dokumen untuk menggunakannya, tetapi ini adalah cara yang disarankan untuk mereferensikan elemen dokumen. Anda tidak harus melakukannya, tetapi itu membuat pengujian menjadi lebih mudah.
Jason Cox
10
documentadalah variabel global di mana seperti $documentyang disuntikkan oleh sudut dan dengan demikian membuat pengujian lebih mudah. Secara umum sulit untuk menguji unit aplikasi yang mengakses variabel JS global seperti dokumen atau jendela. Saya juga berpikir itu module.runadalah tempat yang baik untuk meletakkan kode ini daripada pengontrol.
lanoxx
7
Ini tidak berfungsi untuk saya, panggilan getElementById mengembalikan nol.
ThePartyTurtle
29

Lihat posting ini. Bagaimana menjalankan fungsi pengontrol sudut pada pemuatan halaman?
Untuk pencarian cepat:

// 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
};

Dengan cara ini, Anda tidak perlu menunggu hingga dokumen siap.

vinesh
sumber
1
bagaimana jika saya perlu menelepon after-page-load?
Rishi
22

Angular menginisialisasi secara otomatis pada DOMContentLoadedacara atau ketika skrip angular.js dievaluasi jika pada saat itu document.readyState diatur ke 'selesai'. Pada titik ini Angular mencari arahan aplikasi-ng yang menunjuk akar aplikasi Anda.

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

Ini berarti bahwa kode pengontrol akan berjalan setelah DOM siap.

Jadi itu adil $scope.init().

Will M
sumber
9
Saya mencoba melakukan itu tetapi anehnya tidak berhasil beberapa hal di halaman saya tidak dimuat
foreyez
dan bagaimana cara mengetahui kapan kode sudut siap sebelum melakukan tindakan seperti mengklik tombol dll. ketika menulis tes web aotomated?
akostadinov
Juga DOMContentLoadeddiaktifkan ketika dokumen telah dimuat dan diuraikan sepenuhnya, tanpa menunggu stylesheet, gambar, dan subframe selesai dimuat. Untuk informasi lebih lanjut, lihat Apa perbedaan antara DOMContentLoaded dan memuat acara? .
georgeawg
22

Angular memiliki beberapa titik waktu untuk mulai menjalankan fungsi. Jika Anda mencari sesuatu seperti jQuery

$(document).ready();

Anda mungkin menemukan analog ini dalam sudut sangat berguna:

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

Yang ini sangat membantu ketika Anda ingin memanipulasi elemen DOM. Ini akan mulai dijalankan hanya setelah semua elemen dimuat.

UPD: Apa yang dikatakan di atas berfungsi ketika Anda ingin mengubah properti css. Namun, terkadang tidak berfungsi saat Anda ingin mengukur properti elemen, seperti lebar, tinggi, dll. Dalam hal ini Anda mungkin ingin mencoba ini:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});
Alex Link
sumber
Ini layak lebih dari jawaban yang disarankan. Saya mendapatkan pekerjaan ini tanpa menggunakan $ timeout.
Richie86
ini hanya berfungsi untuk saya dengan setTimeout (0) - tanpa itu isinya di bawah ng-repeat misalnya belum dimuat pada saat ini
Ignacio Vazquez
2

Saya memiliki situasi yang sama di mana saya perlu menjalankan fungsi pengontrol setelah tampilan dimuat dan juga setelah komponen pihak ke-3 tertentu dalam tampilan dimuat, diinisialisasi, dan telah menempatkan referensi ke dirinya sendiri pada $ scope. Apa yang akhirnya berhasil bagi saya adalah mengatur jam tangan pada properti lingkup ini dan menjalankan fungsi saya hanya setelah diinisialisasi.

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

Pengamat disebut beberapa kali dengan nilai yang tidak ditentukan. Kemudian ketika grid dibuat dan memiliki properti yang diharapkan, fungsi inisialisasi dapat dengan aman dipanggil. Pertama kali pengamat dipanggil dengan newValue yang tidak ditentukan, oldValue akan tetap tidak terdefinisi.

MikeyM
sumber
1

Inilah upaya saya di dalam pengontrol luar menggunakan coffeescript. Ini bekerja dengan baik. Harap perhatikan bahwa settings.screen.xs | sm | md | lg adalah nilai statis yang ditentukan dalam file yang tidak di-uglifikasi yang saya sertakan dengan aplikasi. Nilai-nilai tersebut sesuai dengan breakpoint resmi Bootstrap 3 untuk ukuran kueri media eponymous:

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()
akutz
sumber
1

Jawabannya

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

adalah satu-satunya yang berfungsi di sebagian besar skenario yang saya uji. Di halaman contoh dengan 4 komponen yang semuanya membangun HTML dari templat, urutan acara adalah

$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)

Jadi $ document.ready () tidak berguna dalam banyak kasus karena DOM yang sedang dibangun dalam sudut mungkin belum siap.

Tetapi lebih menarik, bahkan setelah $ viewContentLoaded dipecat, elemen yang menarik masih tidak dapat ditemukan.

Hanya setelah $ timeout dieksekusi ditemukan. Perhatikan bahwa meskipun batas waktu $ adalah nilai 0, hampir 200 milidetik berlalu sebelum dieksekusi, menunjukkan bahwa utas ini tertahan cukup lama, mungkin sementara DOM memiliki templat sudut ditambahkan pada utas utama. Total waktu dari $ document.ready () yang pertama ke eksekusi $ timeout terakhir adalah hampir 500 milidetik.

Dalam satu kasus luar biasa di mana nilai komponen ditetapkan dan kemudian nilai text () diubah kemudian dalam $ timeout, nilai $ timeout harus ditingkatkan hingga berfungsi (walaupun elemen dapat ditemukan selama $ timeout) ). Sesuatu async dalam komponen pihak ke-3 menyebabkan nilai lebih diutamakan dari teks sampai waktu yang cukup berlalu. Kemungkinan lain adalah $ scope. $ EvalAsync, tetapi belum dicoba.

Saya masih mencari satu peristiwa yang memberi tahu saya bahwa DOM telah sepenuhnya tenang dan dapat dimanipulasi sehingga semua kasus bekerja. Sejauh ini nilai batas waktu sewenang-wenang diperlukan, artinya setidaknya ini adalah kludge yang mungkin tidak berfungsi pada browser yang lambat. Saya belum mencoba opsi JQuery seperti liveQuery dan menerbitkan / berlangganan yang mungkin berfungsi, tetapi tentu saja tidak murni sudut.

Wray Smallwood
sumber
1

Jika Anda mendapatkan sesuatu seperti getElementById call returns null , itu mungkin karena fungsinya berjalan, tetapi ID belum punya waktu untuk memuat di DOM.

Coba gunakan jawaban Will (ke atas) dengan penundaan. Contoh:

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
        });
    });
}]);
Tuan Green
sumber
0

Mengapa tidak mencoba dengan apa yang disebut dokumen sudut https://docs.angularjs.org/api/ng/function/angular.element .

angular.element (callback)

Saya telah menggunakan ini di dalam fungsi $ onInit () {...} saya.

 var self = this;

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

Ini berhasil untuk saya.

Mahib
sumber
0
$scope.$on('$ViewData', function(event) {
//Your code.
});
Shreyansh Kumar
sumber