2013-06-05 15 views
67

W kontrolerze otrzymuję dane JSON za pomocą usług $ http lub $ resource. Następnie zapisuję te dane w rozszerzeniu $ scope i AngularJS w strukturze HTML strony. Mój problem polega na tym, że muszę wiedzieć, jaki jest nowy rozmiar (szerokość i wysokość) listy (tj. Element HTML DOM) wypełniony dyrektywą Angular ng-repeat. W związku z tym muszę uruchomić kod javascript zaraz po ukończeniu aktualizacji modelu Angular. Jaki jest właściwy sposób na zrobienie tego? Szukałem internetu przez ostatnie cztery godziny, ale nie mogłem znaleźć żadnego rozwiązania mojego problemu.Uruchom kod jQuery po zakończeniu renderowania kodu HTML przez AngularJS

ten sposób otrzymuję JSON dane:

var tradesInfo = TradesInfo.get({}, function(data){ 
    console.log(data); 
    $scope.source.profile = data.profile; 
      $scope.trades = $scope.source.profile.trades; 
     $scope.activetrade = $scope.trades[0]; 
     $scope.ready = true; 


    init(); //I need to call this function after update is complete 

}); 

I to jest to, co dzieje się w init() funkcję:

function init(){ 
    alert($('#wrapper').width()); 
    alert($('#wrapper').height()); 
} 

Wiem, że musi być coś łatwo rozwiązać ten problem, ale mogę Po prostu znajdź to teraz. Z góry dzięki.

+0

Nie możesz. Tak samo prosto. Może wystąpić nieoczekiwane wywołanie funkcji $ compile, dowolna liczba dyrektyw, które same manipulują modelem DOM i ustawiają rzeczy do wykonania po upływie limitu czasu. System AngularJS jest pięknie zaprojektowany, więc elementy zachowują się niezależnie, a to jest niewielka cena zapłacić. Po prostu znajdź inny sposób. – rewritten

+1

Myślę, że i tak trzeba to zrobić. Mam na myśli to, że fakt, że dom z jego dyrektywami może wywoływać funkcję kompilacji lub robić coś innego, czego nie można przewidzieć, nie może temu zapobiec, imho. – Dobby007

+0

Przepraszam, że ożywam stare pytanie, ale czy możesz udostępnić zawartość #wrapper? Prawdopodobnie po prostu przeszukujesz transakcje? – FrEaKmAn

Odpowiedz

62

Właściwie w tym przypadku kątowa sposób nie jest łatwa droga, ale jedyna słuszna droga :)

Trzeba napisać dyrektywę i dołączyć do elementu, który chcesz poznać wysokość. A od kontrolera $ emitujesz wydarzenie, dyrektywa złapie zdarzenie i tam możesz zrobić manipulację DOM. NIGDY w kontrolerze.

var tradesInfo = TradesInfo.get({}, function(data){ 
    console.log(data); 
    $scope.source.profile = data.profile; 
    ... 

    $scope.$broadcast('dataloaded'); 
}); 


directive('heightStuff', ['$timeout', function ($timeout) { 
    return { 
     link: function ($scope, element, attrs) { 
      $scope.$on('dataloaded', function() { 
       $timeout(function() { // You might need this timeout to be sure its run after DOM render. 
        element.width() 
        element.height() 
       }, 0, false); 
      }) 
     } 
    }; 
}]); 
+0

Dziękuję bardzo. To działa. Czy możesz mi wyjaśnić, dlaczego $ timeout z 0ms jest potrzebny? Pytam, ponieważ próbowałem to pominąć, w tym przypadku szerokość i wysokość były nieprawidłowe. Co to jest zachowanie JavaScript z funkcją timeout? Czy uruchamia kod w innym wątku? – Dobby007

+0

Nie, właściwie nie ma innego wątku, dlatego przeglądarki robią sztuczkę. Prowadzą dom reflow tylko na i zamknięcia, dlatego jeśli potrzebujesz wymiarów elementu, nie możesz ich mieć, ponieważ nie są jeszcze renderowane. Musisz odroczyć wykonanie procedury pomiarowej i tam, gdzie pojawia się setTimeout z 0. Powiadomi przeglądarkę: uruchom mnie po wykonaniu bieżącego zamknięcia i już zarejestrowanych limitów czasu. Bardzo prosty przykład: http://jsfiddle.net/Zmetser/VfTar/ – Oliver

+8

To nie jest poprawne. Przeglądarka ponownie wyrenderuje blok natychmiast, gdy spróbujesz uzyskać dostęp do odpowiednich właściwości stylu - więc w ogólnym przypadku nie musisz wywoływać metody setTimeout w tym scenariuszu - zobacz wersję demonstracyjną http://jsfiddle.net/SLTpE/. Ale AngularJS działa w inny sposób - jest to Angular, który nie aktualizuje DOM natychmiast po zmianie modelu, a nie w przeglądarce – amakhrov

5

Kolejna propozycja pracy z JQuery. Musiałem to przetworzyć dla siatki, która została wygenerowana w dyrektywie. Chciałem przewinąć do określonego wiersza w siatce. Użyj $ emitują do transmisji z dyrektywy do kontrolera nadrzędnego:

W Kontroler:

['$timeout',function($timeout){ 
... 
$scope.$on('dataloaded', function() { 
      $timeout(function() { // You might need this timeout to be sure its run after DOM render. 
       $scope.scrollToPosition(); 
      }, 0, false); 
     }); 
     $scope.scrollToPosition = function() { 
      var rowpos = $('#row_' + $scope.selectedActionID, "#runGrid").position(); 
      var tablepost = $('table', "#runGrid").position(); 
      $('#runGrid').scrollTop(rowpos.top - tablepost.top); 
     } 

w dyrektywie

.directive('runGrid',['$timeout', function ($timeout) { 
     // This directive generates the grip of data 
     return { 
      restrict: 'E', //DOM Element 
      scope: { //define isolated scope 
       list: '=', //use the parent object 
       selected: "=" 
      }, 

      templateUrl: '/CampaignFlow/StaticContent/Runs/run.grid.0.0.0.0.htm', //HTML template URL 

      controller: ['$scope', function ($scope) { //the directive private controller, whith its private scope 
       //$scope.statusList = [{ data_1: 11, data_2: 12 }, { data_1: 21, data_2: 22 }, { data_1: 31, data_2: 32 }]; 
       //Controller contains sort functionallity 

       $scope.sort = { column: null, direction: 1 } 
       $scope.column = null; 
       $scope.direction = "asc"; 
       $scope.sortColumn = function (id) { 
        if(id!=$scope.column) { 
         $scope.column = id; 
         $scope.direction = "asc"; 
        } else { 
         $scope.column = null; 
        } 
       } 
       $scope.toggleDir = function() { 
        $scope.direction = ($scope.direction == "asc") ? "desc" : "asc"; 
       } 
       $scope.$emit('dataloaded'); 
      }] 


     }; 
    }]) 

I to jest fragment szablonu siatka dyrektywa html:

<div style="overflow-y:auto;height: 200px;" id="runGrid"> 
      <table class="table table-striped" style="table-layout:fixed"> 
      <tbody> 
       <tr ng-repeat="status in list" id="row_{{status.action_id}}" ng-class="(status.action_id==selected)?'selected':''"> 
        <td> 

lista i wybrane parametry są wstrzykiwane z html, który używa dire ctive

<run-grid list="list" selected="selectedActionID"></run-grid> 
6

odpowiedź Oliver jest dobry, ale ma problem: jeśli zapomnisz nadawane zdarzenie, JavaScript nie będzie działał natomiast dane mogły ulec zmianie. Innym rozwiązaniem byłoby, aby obserwować zmiany w zakresie, na przykład:

var tradesInfo = TradesInfo.get({}, function(data) { 
    console.log(data); 
    $scope.profile = data.profile; 
    // ... 
}); 


directive('heightStuff', ['$timeout', 
    function($timeout) { 
    return { 
     scope: { 
     myData: '=' 
     }, 
     link: function($scope, element, attrs) { 
     $scope.$watch('myData', function() { 
      $timeout(function() { // You might need this timeout to be sure its run after DOM render. 
      element.width() 
      element.height() 
      }, 0, false); 
     }) 
     } 
    }; 
    } 
]); 
<div height-stuff my-data="profile"></div> 

ten sposób funkcje javascript nazywane są każdym razem zmiany danych bez konieczności zdarzenia niestandardowego.

3

Chciałbym dodać kolejną odpowiedź, ponieważ poprzednie odpowiedzi zakładają, że kod potrzebny do uruchomienia po wykonaniu polecenia ngRepeat to kod kątowy, który w takim przypadku wszystkie powyższe odpowiedzi dają świetne i proste rozwiązanie, kilka innych ogólny niż inne, aw przypadku, gdy jest to ważny etap życiowy, możesz zajrzeć do bloga Bena Nadela o tym, z wyjątkiem użycia $ parse zamiast $ eval.

Jednak z mojego doświadczenia, jak wynika z PO, zwykle korzysta się z niektórych wtyczek lub metod jQuery na ostatecznie skompilowanym DOM, co w tym przypadku okazało się, że najprostszym rozwiązaniem jest utworzenie dyrektywy z setTimeout, ponieważ funkcja setTimeout zostaje zepchnięte na koniec kolejki przeglądarki, jego zawsze zaraz po wszystko odbywa się w kątowe, zwykle ng-repeat który trwa po jego rodzice funkcja

angular.module('myApp', []) 
.directive('pluginNameOrWhatever', function() { 
    return function(scope, element, attrs) {   
    setTimeout(function doWork(){ 
     //jquery code and plugins 
    }, 0);   
    }; 
}); 

postLinking Bo kto zastanawia się, dlaczego nie używać $ limit czasu , że powoduje to kolejny cykl trawienny, który jest zupełnie niepotrzebny.

EDIT:

Niż do drzaus za link do sposobu korzystania $ limit czasu bez powodowania strawienia http://www.codelord.net/2015/10/14/angular-nitpicking-differences-between-timeout-and-settimeout/

+0

Powinien używać kątowego ['$ timeout'] (https://docs.angularjs.org/api/ng/service/$ timeout) – drzaus

+0

Napisałem, dlaczego uważam, że NIE używam $ timeout w ostatnim wierszu, przeczytaj ponownie. – bresleveloper

+0

Dziwne, nie mam pojęcia, dlaczego tego nie widziałem. Jeśli jednak nie chcesz cyklu trawienia, po prostu zadzwoń za pomocą 'invokeApply = false'. W przeciwnym razie [lots] (http://stackoverflow.com/questions/19609796/what-advantage-is-the-in-using-the-timeout-in-angular-js-instead-of-window-se) [ ] (https://www.reddit.com/r/angularjs/comments/3gzb0d/timeout_vs_settimeout/) [arguments] (http://www.codelord.net/2015/10/14/angular-nitpicking-differences- between-timeout-and-settimeout /) za użycie tego, co twórcy Angular napisali, aby zastąpić 'setTimeout'. – drzaus

Powiązane problemy