Yanıtlar:
Sadece bir önsezi: neden ngCloak yönergesinin bunu nasıl yaptığına bakmayalım? Açıkça ngCloak yönergesi, içerik yüklendikten sonra içeriği göstermeyi başarır. Bahse girerim ngCloak'a bakmak kesin cevaba götürecek ...
1 saat sonra DÜZENLE: Tamam, ngCloak'a baktım ve gerçekten kısa. Bunun açıkça ima ettiği şey, derleme işlevinin {{şablon}} ifadeleri (yani yüklediği şablon) değerlendirilene kadar çalıştırılmayacağı ve dolayısıyla ngCloak yönergesinin güzel işlevselliğidir.
Benim eğitimli tahminim, sadece ngCloak ile aynı sadeliğe sahip bir yönerge yapmak, sonra derleme işlevinizde yapmak istediğinizi yapmak olacaktır. :) Yönergeyi uygulamanızın kök öğesine yerleştirin. Direktifi myOnload gibi bir şey çağırabilir ve onu my-onload niteliği olarak kullanabilirsiniz. Derleme işlevi, şablon derlendikten sonra (ifadeler değerlendirilir ve alt şablonlar yüklenir) çalıştırılır.
DÜZENLE, 23 saat sonra: Tamam, bu yüzden biraz araştırma yaptım ve kendi sorumu da sordum . Sorduğum soru dolaylı olarak bu soruyla ilgiliydi ama tesadüfen beni bu soruyu çözen cevaba götürdü.
Cevap, basit bir yönerge oluşturup kodunuzu yönergenin link işlevine koyabilmenizdir; bu işlev (çoğu kullanım durumu için aşağıda açıklanmıştır) öğeniz hazır / yüklendiğinde çalışacaktır. Dayanarak derleme ve bağlantı fonksiyonları infaz edildiği düzenin Josh'un açıklaması ,
bu işarete sahipseniz:
<div directive1> <div directive2> <!-- ... --> </div> </div>
Ardından AngularJS, yönerge işlevlerini belirli bir sırayla çalıştırarak yönergeleri oluşturur:
directive1: compile directive2: compile directive1: controller directive1: pre-link directive2: controller directive2: pre-link directive2: post-link directive1: post-link
Varsayılan olarak düz bir "bağlantı" işlevi bir bağlantı sonrası işlevidir, bu nedenle dış yönergenizin1 bağlantı işlevi, iç yönergenin2 bağlantı işlevi çalışana kadar çalışmayacaktır. Bu nedenle bağlantı sonrası yalnızca DOM manipülasyonu yapmanın güvenli olduğunu söylüyoruz. Bu nedenle, orijinal soruya doğru, dış yönergenin link işlevinden child yönergesinin iç html'sine erişmede herhangi bir sorun olmamalıdır, ancak yukarıda belirtildiği gibi dinamik olarak eklenen içerikler derlenmelidir.
Bundan, her şey hazır / derlendiğinde / bağlandığında / yüklendiğinde kodumuzu çalıştırmak için basitçe bir direktif oluşturabileceğimiz sonucuna varabiliriz:
app.directive('ngElementReady', [function() {
return {
priority: -1000, // a low number so this directive loads after all other directives have loaded.
restrict: "A", // attribute only
link: function($scope, $element, $attributes) {
console.log(" -- Element ready!");
// do what you want here.
}
};
}]);
Şimdi yapabileceğiniz şey, ngElementReady direktifini uygulamanın kök öğesine koymaktır ve yüklendiğinde ateşlenecektir console.log
:
<body data-ng-app="MyApp" data-ng-element-ready="">
...
...
</body>
Bu kadar basit! Sadece basit bir yönerge hazırlayın ve kullanın. ;)
Ekleyerek bir ifadeyi (yani bir işlevi) çalıştırabilmesi için daha da özelleştirebilirsiniz $scope.$eval($attributes.ngElementReady);
:
app.directive('ngElementReady', [function() {
return {
priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
restrict: "A",
link: function($scope, $element, $attributes) {
$scope.$eval($attributes.ngElementReady); // execute the expression in the attribute.
}
};
}]);
O zaman herhangi bir öğe üzerinde kullanabilirsiniz:
<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
...
<div data-ng-element-ready="divIsReady()">...<div>
</body>
Öğenizin altında bulunduğu kapsamda (denetleyicide) tanımlanmış işlevlerinizin (örn. BodyIsReady ve divIsReady) olduğundan emin olun.
Uyarılar: Bunun çoğu durumda işe yarayacağını söyledim . NgRepeat ve ngIf gibi belirli yönergeleri kullanırken dikkatli olun. Kendi kapsamlarını yaratırlar ve direktifiniz ateşlenmeyebilir. Örneğin, yeni ngElementReady yönergemizi ngIf'e sahip olan bir öğeye koyarsanız ve ngIf'in durumu false olarak değerlendirilirse, ngElementReady yönergemiz yüklenmez. Veya, örneğin, yeni ngElementReady yönergemizi bir ngInclude yönergesi olan bir öğeye koyarsanız, yönergemiz ngInclude için şablon yoksa yüklenmeyecektir. Direktifleri hepsini aynı öğeye koymak yerine iç içe geçirdiğinizden emin olarak bu sorunların bazılarının üstesinden gelebilirsiniz. Örneğin, bunu yaparak:
<div data-ng-element-ready="divIsReady()">
<div data-ng-include="non-existent-template.html"></div>
<div>
bunun yerine:
<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>
NgElementReady yönergesi ikinci örnekte derlenecek, ancak bağlantı işlevi çalıştırılmayacaktır. Not: Yönergeler her zaman derlenir, ancak yukarıdaki gibi belirli senaryolara bağlı olarak bağlantı işlevleri her zaman çalıştırılmaz.
DÜZENLE, birkaç dakika sonra:
Oh, ve soruyu tam olarak cevaplamak için, şimdi $emit
veya $broadcast
olayınızı ng-element-ready
öznitelikte yürütülen ifade veya işlevden yapabilirsiniz . :) Örneğin:
<div data-ng-element-ready="$emit('someEvent')">
...
<div>
DÜZENLE, hatta birkaç dakika sonra:
@ satchmorun'un cevabı da işe yarıyor, ancak yalnızca ilk yükleme için. İşte bağlantı işlevleri ve diğerleri dahil olmak üzere şeylerin yürütülme sırasını açıklayan çok kullanışlı bir SO sorusuapp.run
. Bu nedenle, kullanım durumunuza bağlı app.run
olarak iyi olabilir, ancak belirli öğeler için geçerli olmayabilir, bu durumda bağlantı işlevleri daha iyidir.
EDIT, beş ay sonra, 17 Ekim 08:11 PST:
Bu, eşzamansız olarak yüklenen bölümlerle çalışmaz. Parçalarınıza defter tutma eklemeniz gerekir (örneğin, bir yol, her bir parçanın içeriğinin ne zaman yüklendiğini takip etmesini sağlamak ve ardından bir olay yayınlamaktır, böylece üst kapsam, kaç parçanın yüklendiğini sayabilir ve sonunda ne yapması gerektiğini yapabilir. tüm parçalar yüklendikten sonra yapın).
DÜZENLE, 23 Ekim 22:52 PST:
Bir resim yüklendiğinde bazı kodların ateşlenmesi için basit bir yönerge hazırladım:
/*
* This img directive makes it so that if you put a loaded="" attribute on any
* img element in your app, the expression of that attribute will be evaluated
* after the images has finished loading. Use this to, for example, remove
* loading animations after images have finished loading.
*/
app.directive('img', function() {
return {
restrict: 'E',
link: function($scope, $element, $attributes) {
$element.bind('load', function() {
if ($attributes.loaded) {
$scope.$eval($attributes.loaded);
}
});
}
};
});
DÜZENLE, 24 Ekim 00:48 PST:
Orijinal ngElementReady
yönergemi geliştirdim ve olarak yeniden adlandırdım whenReady
.
/*
* The whenReady directive allows you to execute the content of a when-ready
* attribute after the element is ready (i.e. done loading all sub directives and DOM
* content except for things that load asynchronously like partials and images).
*
* Execute multiple expressions by delimiting them with a semi-colon. If there
* is more than one expression, and the last expression evaluates to true, then
* all expressions prior will be evaluated after all text nodes in the element
* have been interpolated (i.e. {{placeholders}} replaced with actual values).
*
* Caveats: if other directives exists on the same element as this directive
* and destroy the element thus preventing other directives from loading, using
* this directive won't work. The optimal way to use this is to put this
* directive on an outer element.
*/
app.directive('whenReady', ['$interpolate', function($interpolate) {
return {
restrict: 'A',
priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
link: function($scope, $element, $attributes) {
var expressions = $attributes.whenReady.split(';');
var waitForInterpolation = false;
function evalExpressions(expressions) {
expressions.forEach(function(expression) {
$scope.$eval(expression);
});
}
if ($attributes.whenReady.trim().length == 0) { return; }
if (expressions.length > 1) {
if ($scope.$eval(expressions.pop())) {
waitForInterpolation = true;
}
}
if (waitForInterpolation) {
requestAnimationFrame(function checkIfInterpolated() {
if ($element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
requestAnimationFrame(checkIfInterpolated);
}
else {
evalExpressions(expressions);
}
});
}
else {
evalExpressions(expressions);
}
}
}
}]);
Örneğin, someFunction
bir öğe yüklendiğinde ve {{placeholders}}
henüz değiştirilmediğinde ateşlemek için bunu şu şekilde kullanın :
<div when-ready="someFunction()">
<span ng-repeat="item in items">{{item.property}}</span>
</div>
someFunction
tüm item.property
yer tutucular değiştirilmeden önce çağrılacaktır .
İstediğiniz kadar ifadeyi değerlendirin ve son ifadeyi şu şekilde değerlendirilmesini true
bekleyin {{placeholders}}
:
<div when-ready="someFunction(); anotherFunction(); true">
<span ng-repeat="item in items">{{item.property}}</span>
</div>
someFunction
ve değiştirildikten anotherFunction
sonra kovulacak {{placeholders}}
.
Bu, gelecekteki değişikliklerde değil, yalnızca bir öğe ilk yüklendiğinde çalışır. Yer $digest
tutucular başlangıçta değiştirildikten sonra bir devam ederse , istenildiği gibi çalışmayabilir (bir $ özet, verilerin değişmesi durana kadar 10 kez gerçekleşebilir). Kullanım durumlarının büyük çoğunluğu için uygun olacaktır.
DÜZENLE, 31 Ekim 19:26 PST:
Pekala, bu muhtemelen benim son ve son güncellemem. Bu muhtemelen oradaki kullanım durumlarının 99.999'u için işe yarayacaktır:
/*
* The whenReady directive allows you to execute the content of a when-ready
* attribute after the element is ready (i.e. when it's done loading all sub directives and DOM
* content). See: /programming/14968690/sending-event-when-angular-js-finished-loading
*
* Execute multiple expressions in the when-ready attribute by delimiting them
* with a semi-colon. when-ready="doThis(); doThat()"
*
* Optional: If the value of a wait-for-interpolation attribute on the
* element evaluates to true, then the expressions in when-ready will be
* evaluated after all text nodes in the element have been interpolated (i.e.
* {{placeholders}} have been replaced with actual values).
*
* Optional: Use a ready-check attribute to write an expression that
* specifies what condition is true at any given moment in time when the
* element is ready. The expression will be evaluated repeatedly until the
* condition is finally true. The expression is executed with
* requestAnimationFrame so that it fires at a moment when it is least likely
* to block rendering of the page.
*
* If wait-for-interpolation and ready-check are both supplied, then the
* when-ready expressions will fire after interpolation is done *and* after
* the ready-check condition evaluates to true.
*
* Caveats: if other directives exists on the same element as this directive
* and destroy the element thus preventing other directives from loading, using
* this directive won't work. The optimal way to use this is to put this
* directive on an outer element.
*/
app.directive('whenReady', ['$interpolate', function($interpolate) {
return {
restrict: 'A',
priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
link: function($scope, $element, $attributes) {
var expressions = $attributes.whenReady.split(';');
var waitForInterpolation = false;
var hasReadyCheckExpression = false;
function evalExpressions(expressions) {
expressions.forEach(function(expression) {
$scope.$eval(expression);
});
}
if ($attributes.whenReady.trim().length === 0) { return; }
if ($attributes.waitForInterpolation && $scope.$eval($attributes.waitForInterpolation)) {
waitForInterpolation = true;
}
if ($attributes.readyCheck) {
hasReadyCheckExpression = true;
}
if (waitForInterpolation || hasReadyCheckExpression) {
requestAnimationFrame(function checkIfReady() {
var isInterpolated = false;
var isReadyCheckTrue = false;
if (waitForInterpolation && $element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
isInterpolated = false;
}
else {
isInterpolated = true;
}
if (hasReadyCheckExpression && !$scope.$eval($attributes.readyCheck)) { // if the ready check expression returns false
isReadyCheckTrue = false;
}
else {
isReadyCheckTrue = true;
}
if (isInterpolated && isReadyCheckTrue) { evalExpressions(expressions); }
else { requestAnimationFrame(checkIfReady); }
});
}
else {
evalExpressions(expressions);
}
}
};
}]);
Bunu böyle kullan
<div when-ready="isReady()" ready-check="checkIfReady()" wait-for-interpolation="true">
isReady will fire when this {{placeholder}} has been evaluated
and when checkIfReady finally returns true. checkIfReady might
contain code like `$('.some-element').length`.
</div>
Tabii ki, muhtemelen optimize edilebilir, ancak bunu öyle bırakacağım. requestAnimationFrame güzel.
In için docsangular.Module
, açıklayan bir giriş var run
işlevi:
Enjektörün tüm modülleri yüklemesi bittiğinde yapılması gereken işi kaydetmek için bu yöntemi kullanın.
Dolayısıyla, uygulamanız olan bir modülünüz varsa:
var app = angular.module('app', [/* module dependencies */]);
Modüller yüklendikten sonra şunları çalıştırabilirsiniz:
app.run(function() {
// Do post-load initialization stuff here
});
Dolayısıyla run
, DOM hazır olduğunda ve bağlandığında çağrılmayacağına dikkat çekildi. $injector
Tarafından başvurulan modül ng-app
, DOM derleme adımından ayrı olan tüm bağımlılıklarını yüklediğinde çağrılır .
El ile başlatmaya bir kez daha baktım ve bu işin püf noktası olması gerekiyor.
Göstermek için bir keman yaptım .
HTML basittir:
<html>
<body>
<test-directive>This is a test</test-directive>
</body>
</html>
Bir ng-app
. Ve bazı DOM manipülasyonu yapacak bir direktifim var, böylece işlerin sırasından ve zamanlamasından emin olabiliriz.
Her zamanki gibi bir modül oluşturulur:
var app = angular.module('app', []);
Ve işte direktif:
app.directive('testDirective', function() {
return {
restrict: 'E',
template: '<div class="test-directive"><h1><div ng-transclude></div></h1></div>',
replace: true,
transclude: true,
compile: function() {
console.log("Compiling test-directive");
return {
pre: function() { console.log("Prelink"); },
post: function() { console.log("Postlink"); }
};
}
};
});
test-directive
Etiketi a div
sınıfıyla değiştireceğiz test-directive
ve içeriğini bir h1
.
Hem bağlantı öncesi hem de sonrası bağlantı işlevlerini döndüren bir derleme işlevi ekledim, böylece bunların ne zaman çalıştığını görebiliriz.
İşte kodun geri kalanı:
// The bootstrapping process
var body = document.getElementsByTagName('body')[0];
// Check that our directive hasn't been compiled
function howmany(classname) {
return document.getElementsByClassName(classname).length;
}
Herhangi bir şey yapmadan önce test-directive
, DOM'da sınıfına sahip hiçbir öğe olmamalı ve işimiz bittikten sonra 1 olmalıdır.
console.log('before (should be 0):', howmany('test-directive'));
angular.element(document).ready(function() {
// Bootstrap the body, which loades the specified modules
// and compiled the DOM.
angular.bootstrap(body, ['app']);
// Our app is loaded and the DOM is compiled
console.log('after (should be 1):', howmany('test-directive'));
});
Oldukça basit. Belge hazır olduğunda, angular.bootstrap
uygulamanızın kök öğesi ve bir dizi modül adı ile arayın .
Aslında, modüle bir run
işlevapp
eklerseniz, herhangi bir derleme gerçekleşmeden önce çalıştığını görürsünüz.
Keman çalar ve konsolu izlerseniz, şunları görürsünüz:
before (should be 0): 0
Compiling test-directive
Prelink
Postlink
after (should be 1): 1 <--- success!
run
önce ateşleniyor ve çalıştırıldığında html hepsi orada değil
$timeout( initMyPlugins,0)
Angular, bir sayfanın yüklenmesi bittiğinde sinyal vermenin bir yolunu sağlamamıştır, bunun nedeni "tamamlandı" nın uygulamanıza bağlı olması olabilir . Örneğin, hiyerarşik parça ağacınız varsa, biri diğerlerini yüklüyor. "Bitir", hepsinin yüklenmiş olduğu anlamına gelir. Herhangi bir çerçeve, kodunuzu analiz etmekte ve her şeyin yapıldığını veya hala beklendiğini anlamakta zorlanacaktır. Bunun için, bunu kontrol etmek ve belirlemek için uygulamaya özel mantık sağlamanız gerekir.
Açısal başlatmanın ne zaman tamamlandığını değerlendirmede nispeten doğru olan bir çözüm buldum.
Yönerge:
.directive('initialisation',['$rootScope',function($rootScope) {
return {
restrict: 'A',
link: function($scope) {
var to;
var listener = $scope.$watch(function() {
clearTimeout(to);
to = setTimeout(function () {
console.log('initialised');
listener();
$rootScope.$broadcast('initialised');
}, 50);
});
}
};
}]);
Bu daha sonra body
öğeye bir öznitelik olarak eklenebilir ve daha sonra kullanım için dinlenebilir$scope.$on('initialised', fn)
Artık $ özet döngüsü olmadığında uygulamanın başlatıldığını varsayarak çalışır. $ watch her özet döngüsü çağrılır ve böylece bir zamanlayıcı başlatılır (setTimeout $ timeout değil, böylece yeni bir özet döngüsü tetiklenmez). Zaman aşımı süresi içinde bir özet döngüsü gerçekleşmezse, uygulamanın başlatıldığı varsayılır.
Açıkçası, satchmoruns çözümü kadar doğru değil (çünkü bir özet döngüsü zaman aşımından daha uzun sürüyor olabilir) ancak benim çözümüm, yönetimi çok daha kolay hale getiren modülleri takip etmenizi gerektirmiyor (özellikle büyük projeler için) ). Her neyse, ihtiyaçlarımı karşılayacak kadar doğru görünüyor. Umarım yardımcı olur.
Angular UI Router kullanıyorsanız , $viewContentLoaded
olayı dinleyebilirsiniz .
"$ viewContentLoaded - DOM oluşturulduktan sonra görünüm yüklendikten sonra tetiklenir. Görünümün " $ kapsamı "etkinliği yayar." - Bağlantı
$scope.$on('$viewContentLoaded',
function(event){ ... });
JQuery ile açısal DOM manipülasyonunu gözlemliyorum ve uygulamam için bir son belirledim (uygulama özetim için ihtiyaç duyduğum bir tür önceden tanımlanmış ve tatmin edici durum), örneğin ng-tekrarlayıcımın 7 sonuç üretmesini bekliyorum ve orada i bu amaç için setInterval yardımıyla bir gözlem fonksiyonu kuracaktır.
$(document).ready(function(){
var interval = setInterval(function(){
if($("article").size() == 7){
myFunction();
clearInterval(interval);
}
},50);
});
NgRoute modülünü kullanmıyorsanız , yani $ viewContentLoaded olayınız yoksa .
Başka bir yönerge yöntemi kullanabilirsiniz:
angular.module('someModule')
.directive('someDirective', someDirective);
someDirective.$inject = ['$rootScope', '$timeout']; //Inject services
function someDirective($rootScope, $timeout){
return {
restrict: "A",
priority: Number.MIN_SAFE_INTEGER, //Lowest priority
link : function(scope, element, attr){
$timeout(
function(){
$rootScope.$emit("Some:event");
}
);
}
};
}
Trusktr'ın cevabına göre en düşük önceliğe sahip. Artı $ zaman aşımı , Angular'ın geri arama çalıştırılmadan önce tüm olay döngüsünden geçmesine neden olur.
$ rootScope kullanıldı, çünkü direktifin uygulamanın herhangi bir kapsamına yerleştirilmesine izin veriyor ve sadece gerekli dinleyicileri bilgilendiriyor.
$ rootScope. $ emit tüm $ rootScope için bir olayı tetikler. $ sadece dinleyicilerde. İlginç olan kısım, $ rootScope. $ Yayınının tüm $ rootScope. $ On ve $ kapsam. $ On dinleyicileri Kaynak
Angular ekibine ve bu Github sorununa göre :
şimdi sırasıyla ng-view ve ng-include'de yayınlanan $ viewContentLoaded ve $ includeContentLoaded olaylarına sahibiz. Bence bu, derlemeyi ne zaman bitirdiğimizi öğrenmeye en yakın şey.
Buna dayanarak, şu anda bunu güvenilir bir şekilde yapmak mümkün görünmüyor , aksi takdirde Angular olayı kutudan çıkarırdı.
Uygulamanın önyüklenmesi, özet döngüsünün kök kapsamda çalıştırılması anlamına gelir ve ayrıca bir özet döngüsü tamamlandı olayı yoktur.
Angular 2 tasarım belgelerine göre :
Birden çok özütleme nedeniyle, modelin kararlı olduğunu bileşene belirlemek ve bildirmek imkansızdır. Bunun nedeni, bildirimin verileri daha fazla değiştirebilmesidir, bu da bağlama işlemini yeniden başlatabilir.
Buna göre bunun mümkün olmaması, Angular 2'de yeniden yazmaya gitme kararının alınmasının nedenlerinden biridir.
Yönlendirme yoluyla gelen ana parçadan sonra / tarafından yüklenen bir parçam vardı.
Bu alt bölüm yüklendikten sonra bir işlev çalıştırmam gerekiyordu ve yeni bir yönerge yazmak istemedim ve arsızca kullanabileceğinizi anladım. ngIf
Üst kısmi denetleyici:
$scope.subIsLoaded = function() { /*do stuff*/; return true; };
Alt bölümün HTML'si
<element ng-if="subIsLoaded()"><!-- more html --></element>
Sunucu tarafı verileriyle (JSP, PHP) JS oluşturmak istiyorsanız, denetleyiciniz yüklendiğinde otomatik olarak yüklenecek olan bir hizmete mantığınızı ekleyebilirsiniz.
Ek olarak, tüm direktiflerin derlenmesi / bağlanması tamamlandığında tepki vermek istiyorsanız, yukarıdaki başlatma mantığına önerilen uygun çözümleri ekleyebilirsiniz.
module.factory('YourControllerInitService', function() {
// add your initialization logic here
// return empty service, because it will not be used
return {};
});
module.controller('YourController', function (YourControllerInitService) {
});
Bunların hepsi harika çözümler, Ancak, şu anda Yönlendirme kullanıyorsanız, o zaman bu çözümü en kolay ve gereken kod miktarı olarak buldum. Rotayı tetiklemeden önce bir sözün tamamlanmasını beklemek için 'çözme' özelliğini kullanma. Örneğin
$routeProvider
.when("/news", {
templateUrl: "newsView.html",
controller: "newsController",
resolve: {
message: function(messageService){
return messageService.getMessage();
}
}
})
Belgelerin tamamı için burayı tıklayın - K. Scott Allen'a Kredi Verin
bu örnekle size yardımcı olabilir miyim?
Özel fantezi kutusunda, enterpolasyonlu değerlere sahip içerikleri gösteriyorum.
hizmette, "açık" fantezi kutusu yönteminde
open: function(html, $compile) {
var el = angular.element(html);
var compiledEl = $compile(el);
$.fancybox.open(el);
}
$ derleme derlenmiş verileri döndürür. derlenmiş verileri kontrol edebilirsiniz