AngularJS: AngularJS bir şablon oluşturduktan sonra ek kod nasıl çalıştırılır?


182

DOM'de bir Açısal şablonum var. Denetleyicim bir hizmetten yeni veriler aldığında, $ kapsamındaki modeli günceller ve şablonu yeniden oluşturur. Şimdiye kadar iyi.

Sorun, şablon yeniden işlendikten ve DOM (bu durumda bir jQuery eklentisi) sonra da bazı ekstra iş yapmak gerekiyor olmasıdır.

Dinlemek için AfterRender gibi bir olay olması gerektiği anlaşılıyor, ancak böyle bir şey bulamıyorum. Belki bir direktif gitmek için bir yol olurdu, ama çok erken ateş gibi görünüyordu.

İşte benim sorunum özetleyen bir jsFiddle: Fiddle-AngularIssue

== GÜNCELLEME ==

Yararlı yorumlara dayanarak, DOM manipülasyonunu ele almak için bir direktife geçtim ve direktifin içine bir model $ watch uyguladım. Ancak yine de aynı temel sorunu yaşıyorum; $ watch olayının içindeki kod, şablon derlenip DOM'a yerleştirilmeden önce tetiklenir, bu nedenle jquery eklentisi her zaman boş bir tabloyu değerlendirir.

İlginç bir şekilde, async çağrısını kaldırırsam her şey iyi çalışır, bu yüzden doğru yönde bir adımdır.

İşte bu değişiklikleri yansıtacak şekilde güncellenmiş Fiddle'ım: http://jsfiddle.net/uNREn/12/


Bu benim sahip olduğum bir soruya benziyor. stackoverflow.com/questions/11444494/… . Belki orada bir şey yardımcı olabilir.
dnc253

Yanıtlar:


39

Bu yayın eski, ancak kodunuzu şu şekilde değiştiriyorum:

scope.$watch("assignments", function (value) {//I change here
  var val = value || null;            
  if (val)
    element.dataTable({"bDestroy": true});
  });
}

bkz jsfiddle .

Umarım sana yardımcı olur


Teşekkürler, bu çok yardımcı oldu. Bu benim için en iyi çözümdür çünkü denetleyiciden dom manipülasyonu gerektirmez ve veriler gerçekten eşzamansız bir hizmet yanıtından güncellendiğinde çalışmaya devam eder (ve denetleyicimde $ evalAsync kullanmak zorunda değilim).
gorebash

9
Bu onaylanmadı mı? Bunu denediğimde DOM oluşturulmadan hemen önce çağırıyor . Bir gölge dom'a veya başka bir şeye mi bağlı?
Shayne

Angular için nispeten yeniyim ve bu cevabı özel durumumla nasıl uygulayacağımı göremiyorum. Çeşitli cevaplara bakıyorum ve tahmin ediyorum, ama bu işe yaramıyor. 'İzle' işlevinin ne izlemesi gerektiğinden emin değilim. Açısal uygulamamız, sayfadan sayfaya değişecek çeşitli farklı yönergelere sahiptir, bu yüzden 'ne izleyeceğimizi' nasıl bilebilirim? Bir sayfanın işlenmesi bittiğinde şüphesiz bazı kodları tetiklemenin basit bir yolu var mı?
moosefetcher

63

Birincisi, render ile uğraşmak için doğru yer direktiflerdir. Benim tavsiyem DOM gibi jQuery eklentilerini bu gibi yönergeler ile sarmak olacaktır.

Aynı problemi yaşadım ve bu pasajı buldum. Bu kullanır $watchve $evalAsyncdirektifler Seviyorum sonra kod yayınlanmasını sağlamanın ng-repeatçözülmesi ve benzeri şablonlar edilmiş {{ value }}hale gördü.

app.directive('name', function() {
    return {
        link: function($scope, element, attrs) {
            // Trigger when number of children changes,
            // including by directives like ng-repeat
            var watch = $scope.$watch(function() {
                return element.children().length;
            }, function() {
                // Wait for templates to render
                $scope.$evalAsync(function() {
                    // Finally, directives are evaluated
                    // and templates are renderer here
                    var children = element.children();
                    console.log(children);
                });
            });
        },
    };
});

Umarım bu biraz mücadeleyi önlemenize yardımcı olabilir.


Bu bariz olanı belirtiyor olabilir, ancak bu sizin için işe yaramazsa, bağlantı işlevlerinizde / denetleyicilerinizde eşzamansız işlemler olup olmadığını kontrol edin. $ EvalAsync'in bunları dikkate alabileceğini sanmıyorum.
LOAS

Bir linkişlevi bir ile de birleştirebileceğinizi belirtmek gerekir templateUrl.
Wtower

35

Misko'nun tavsiyelerine uyarak, zaman uyumsuz işlem istiyorsanız, $ timeout () yerine (işe yaramaz)

$timeout(function () { $scope.assignmentsLoaded(data); }, 1000);

$ evalAsync () kullanın (hangisi işe yarar)

$scope.$evalAsync(function() { $scope.assignmentsLoaded(data); } );

Keman . Ayrıca, veri / modelde bir değişikliği simüle ederek - veri değiştirmenin işe yaradığını göstermek için $ scope.assignments öğesini değiştirecek bir "veri satırını kaldır" bağlantısı ekledim.

Süre Kavramsal Bakış sayfanın bölüm evalAsync geçerli yığın çerçevesi dışında meydana ihtiyacı varken kullanılması gerektiğini açıklar, ancak tarayıcı görüntüleyen önce. (Burada tahmin ediliyor ... "mevcut yığın çerçevesi" muhtemelen Açısal DOM güncellemelerini içeriyor.) Tarayıcı oluşturulduktan sonra bir şeye ihtiyacınız varsa $ timeout kullanın.

Ancak, daha önce de bildiğiniz gibi, burada asenkron işlem için herhangi bir ihtiyaç olduğunu sanmıyorum.


4
$scope.$evalAsync($scope.assignmentsLoaded(data));hiçbir anlam ifade etmiyor IMO. İçin argüman $evalAsync()bir işlev olmalıdır. Örneğinizde, daha sonra yürütülmek üzere bir işlevin kaydedilmesi gereken zamanda işlevi çağırıyorsunuz.
hgoebl

@hgoebl, $ evalAsync argümanı bir işlev veya ifade olabilir. Bu durumda bir ifade kullandım. Ama haklısın, ifade hemen yürütülür. Cevabı daha sonra yürütmek için bir işlevde sarmak için değiştirdim. Yakaladığınız için teşekkürler.
Mark Rajcok

26

Ben basit (ucuz ve neşeli) çözüm sadece render son öğenin sonuna ng-show = "someFunctionThatAlwaysReturnsZeroOrNothing ()" ile boş bir span eklemek olduğunu bulduk. Bu işlev, span öğesinin görüntülenip görüntülenmeyeceğini kontrol etmek için çalıştırılır. Bu işlevde başka bir kod yürütün.

Bunun bir şey yapmanın en zarif yolu olmadığını anlıyorum, ancak benim için çalışıyor ...

Benzer bir durum vardı, ancak bir animasyon başladığında bir yükleme göstergesini kaldırmam gerektiğinde biraz tersine döndü, mobil cihazlarda açısal görüntülenecek animasyondan çok daha hızlı başlatıldı ve yükleme göstergesi olduğu için bir ng-pelerin kullanımı yetersizdi gerçek veriler görüntülenmeden önce iyi kaldırıldı. Bu durumda, ilk oluşturulan öğeye dönüş 0 işlevimi ekledim ve bu işlevde yükleme göstergesini gizleyen var saygısını çevirdim. (tabii ki bu fonksiyon tarafından tetiklenen yükleme göstergesine bir ng-hide ekledim.


3
Bu kesin bir kesmek ama tam olarak ne gerekli. Kodumu tam olarak işlemden sonra (veya tam olarak çok yakın) çalışmaya zorladı. İdeal bir dünyada bunu yapmazdım ama işte buradayız ...
Andrew

$anchorScrollDOM render sonra çağrılacak yerleşik hizmet gerekiyordu ve hiçbir şey hile yapmak gibiydi ama bu. Hackish ama işe yarıyor.
Pat Migliaccio

ng-init daha iyi bir alternatif
Flying Gambit

1
ng-init her zaman çalışmayabilir. Örneğin, dom için bir dizi görüntü ekleyen bir ng-tekrar var. Ng-tekrarına her resim eklendikten sonra bir fonksiyon çağırmak istersem ng-show kullanmalıyım.
Michael Bremerkamp


4

Sonunda çözümü buldum, koleksiyonumu güncellemek için bir REST servisi kullanıyordum. Datatable jquery dönüştürmek için aşağıdaki kod:

$scope.$watchCollection( 'conferences', function( old, nuew ) {
        if( old === nuew ) return;
        $( '#dataTablex' ).dataTable().fnDestroy();
        $timeout(function () {
                $( '#dataTablex' ).dataTable();
        });
    });

3

bunu oldukça sık yapmak zorunda kaldım. i bir yönerge var ve model şeyler tamamen DOM içine yüklendikten sonra bazı jquery şeyler yapmak gerekiyor. bu yüzden linke benim mantığı koymak: yönerge fonksiyonu ve bir setTimeout (function () {.....}, 1) kodu sarın; setTimout, DOM yüklendikten sonra tetiklenir ve 1 milisaniye, kod yürütülmeden önce DOM yüklendikten sonraki en kısa süredir. Bu benim için çalışıyor gibi görünüyor ama ben bir şablon yükleme tamamlandıktan sonra açısal bir olay yükseltmek isterdim böylece şablon tarafından kullanılan yönergeler jquery şeyler yapmak ve DOM öğelerine erişmek. Bu yardımcı olur umarım.



2

Ne $ scope. $ EvalAsync () ya da $ timeout (fn, 0) benim için güvenilir bir şekilde çalıştı.

İkisini birleştirmem gerekiyordu. Bir yönerge hazırladım ve ayrıca iyi önlem için varsayılan değerden daha yüksek bir öncelik koydum. İşte bunun için bir yönerge (Bağımlılıkları enjekte etmek için ngInject kullanıyorum):

app.directive('postrenderAction', postrenderAction);

/* @ngInject */
function postrenderAction($timeout) {
    // ### Directive Interface
    // Defines base properties for the directive.
    var directive = {
        restrict: 'A',
        priority: 101,
        link: link
    };
    return directive;

    // ### Link Function
    // Provides functionality for the directive during the DOM building/data binding stage.
    function link(scope, element, attrs) {
        $timeout(function() {
            scope.$evalAsync(attrs.postrenderAction);
        }, 0);
    }
}

Direktifi aramak için şunu yaparsınız:

<div postrender-action="functionToRun()"></div>

Bir ng-yineleme işlemi tamamlandıktan sonra çağırmak istiyorsanız, ng-repeat'ime boş bir span ekledim ve ng-if = "$ last":

<li ng-repeat="item in list">
    <!-- Do stuff with list -->
    ...

    <!-- Fire function after the last element is rendered -->
    <span ng-if="$last" postrender-action="$ctrl.postRender()"></span>
</li>

1

Bir hizmeti güncellediğiniz ve yeni bir görünüme (sayfa) yönlendirdiğiniz ve ardından hizmetleriniz güncellenmeden önce yönergelerinizin yüklendiği bazı senaryolarda, $ watch veya $ timeout başarısız olursa $ rootScope. $ Broadcast kullanabilirsiniz.

Görünüm

<service-history log="log" data-ng-repeat="log in requiedData"></service-history>

kontrolör

app.controller("MyController",['$scope','$rootScope', function($scope, $rootScope) {

   $scope.$on('$viewContentLoaded', function () {
       SomeSerive.getHistory().then(function(data) {
           $scope.requiedData = data;
           $rootScope.$broadcast("history-updation");
       });
  });

}]);

Direktif

app.directive("serviceHistory", function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {
           log: '='
        },
        link: function($scope, element, attrs) {
            function updateHistory() {
               if(log) {
                   //do something
               }
            }
            $rootScope.$on("history-updation", updateHistory);
        }
   };
});

1

Oldukça basit bir çözümle geldim. Bunu yapmanın doğru yolu olup olmadığından emin değilim ama pratik anlamda çalışıyor. Oluşturulmasını istediğimiz şeyi doğrudan izleyelim. Örneğin, bazı ng-repeats içeren bir yönergede , paragrafın uzunluğuna (başka şeylere sahip olabilirsiniz!) Veya tüm html'ye dikkat ederim. Yönerge şöyle olacaktır:

.directive('myDirective', [function () {
    'use strict';
    return {

        link: function (scope, element, attrs) {
            scope.$watch(function(){
               var whole_p_length = 0;
               var ps = element.find('p');
                for (var i=0;i<ps.length;i++){
                    if (ps[i].innerHTML == undefined){
                        continue
                    }
                    whole_p_length+= ps[i].innerHTML.length;
                }
                //it could be this too:  whole_p_length = element[0].innerHTML.length; but my test showed that the above method is a bit faster
                console.log(whole_p_length);
                return whole_p_length;
            }, function (value) {   
                //Code you want to be run after rendering changes
            });
        }
}]);

NOT sonra kod aslında çalışır render değiştirir ziyade komple oluşturma. Ancak çoğu durumda değişikliklerin gerçekleştiği her durumda durumlarla başa çıkabileceğinizi tahmin ediyorum. Ayrıca p, kodunuzu oluşturma işlemi tamamlandıktan sonra yalnızca bir kez çalıştırmak istiyorsanız, bu uzunluğu (veya başka bir ölçüyü) modelinizle karşılaştırmayı düşünebilirsiniz . Bu konuda herhangi bir düşünce / yorum için teşekkür ederiz.


0

Açısal UI araçlarının 'jQuery Passthrough' modülünü kullanabilirsiniz . Ben bir web hizmetinden zaman uyumsuz almak ve ng-tekrar ile render bazı görüntüler için bir jQuery dokunmatik atlıkarınca eklentisi başarıyla bağladı.

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.