Подтвердить что ты не робот

Событие завершения ng-repeat

Я хочу вызвать функцию jQuery для таргетинга div с таблицей. Эта таблица заполнена ng-repeat.

Когда я вызываю его

$(document).ready()

У меня нет результата.

Также

$scope.$on('$viewContentLoaded', myFunc);

не помогает.

Есть ли способ выполнить функцию сразу после заполнения ng-repeat? Я прочитал совет об использовании пользовательского directive, но я не знаю, как его использовать с ng-repeat и моим div...

4b9b3361

Ответ 1

В самом деле, вы должны использовать директивы, и нет никакого события, связанного с концом цикла ng-Repeat (поскольку каждый элемент создается индивидуально и имеет собственное событие). Но a) использование директив может быть всем, что вам нужно, и b) существует несколько специфических свойств ng-Repeat, которые вы можете использовать для завершения вашего события "on ngRepeat".

В частности, если все, что вы хотите, это стиль/добавление событий во всю таблицу, вы можете сделать это, используя директиву, которая охватывает все элементы ngRepeat. С другой стороны, если вы хотите конкретно адресовать каждый элемент, вы можете использовать директиву в ngRepeat, и он будет действовать на каждый элемент после его создания.

Затем существуют свойства $index, $first, $middle и $last, которые вы можете использовать для запуска событий. Итак, для этого HTML:

<div ng-controller="Ctrl" my-main-directive>
  <div ng-repeat="thing in things" my-repeat-directive>
    thing {{thing}}
  </div>
</div>

Вы можете использовать такие директивы:

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('color','blue');
    if (scope.$last){
      window.alert("im the last!");
    }
  };
})
.directive('myMainDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('border','5px solid red');
  };
});

Смотрите это в действии в этом Plunker. Надеюсь, это поможет!

Ответ 2

Если вы просто хотите выполнить некоторый код в конце цикла, здесь немного более простое изменение, которое не требует дополнительной обработки событий:

<div ng-controller="Ctrl">
  <div class="thing" ng-repeat="thing in things" my-post-repeat-directive>
    thing {{thing}}
  </div>
</div>
function Ctrl($scope) {
  $scope.things = [
    'A', 'B', 'C'  
  ];
}

angular.module('myApp', [])
.directive('myPostRepeatDirective', function() {
  return function(scope, element, attrs) {
    if (scope.$last){
      // iteration is complete, do whatever post-processing
      // is necessary
      element.parent().css('border', '1px solid black');
    }
  };
});

Смотрите живую демонстрацию.

Ответ 3

Здесь приведена повторяющаяся директива, которая вызывает указанную функцию, когда она истинна. Я обнаружил, что вызываемая функция должна использовать $timeout с интервалом = 0 перед выполнением манипуляций с DOM, например инициализация всплывающих подсказок на визуализированных элементах. jsFiddle: http://jsfiddle.net/tQw6w/

В $scope.layoutDone попробуйте прокомментировать строку $timeout и раскомментируйте "NOT CORRECT!" чтобы увидеть разницу во всплывающих подсказках.

<ul>
    <li ng-repeat="feed in feedList" repeat-done="layoutDone()" ng-cloak>
    <a href="{{feed}}" title="view at {{feed | hostName}}" data-toggle="tooltip">{{feed | strip_http}}</a>
    </li>
</ul>

JS:

angular.module('Repeat_Demo', [])

    .directive('repeatDone', function() {
        return function(scope, element, attrs) {
            if (scope.$last) { // all are rendered
                scope.$eval(attrs.repeatDone);
            }
        }
    })

    .filter('strip_http', function() {
        return function(str) {
            var http = "http://";
            return (str.indexOf(http) == 0) ? str.substr(http.length) : str;
        }
    })

    .filter('hostName', function() {
        return function(str) {
            var urlParser = document.createElement('a');
            urlParser.href = str;
            return urlParser.hostname;
        }
    })

    .controller('AppCtrl', function($scope, $timeout) {

        $scope.feedList = [
            'http://feeds.feedburner.com/TEDTalks_video',
            'http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/',
            'http://sfbay.craigslist.org/eng/index.rss',
            'http://www.slate.com/blogs/trending.fulltext.all.10.rss',
            'http://feeds.current.com/homepage/en_US.rss',
            'http://feeds.current.com/items/popular.rss',
            'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
        ];

        $scope.layoutDone = function() {
            //$('a[data-toggle="tooltip"]').tooltip(); // NOT CORRECT!
            $timeout(function() { $('a[data-toggle="tooltip"]').tooltip(); }, 0); // wait...
        }

    })

Ответ 4

Нет необходимости создавать директиву, особенно для завершения события ng-repeat.

ng-init делает магию для вас.

  <div ng-repeat="thing in things" ng-init="$last && finished()">

$last гарантирует, что finished запускается только после того, как последний элемент был передан в DOM.

Не забудьте создать событие $scope.finished.

Счастливое кодирование!!

РЕДАКТИРОВАТЬ: 23 октября 2016 г.

Если вы также хотите вызвать функцию finished, когда в массиве нет элемента, вы можете использовать следующее обходное решение

<div style="display:none" ng-init="things.length < 1 && finished()"></div>
//or
<div ng-if="things.length > 0" ng-init="finished()"></div>

Просто добавьте вышеприведенную строку в верхней части элемента ng-repeat. Он проверит, не имеет ли массив никакого значения и вызывает функцию соответственно.

например.

<div ng-if="things.length > 0" ng-init="finished()"></div>
<div ng-repeat="thing in things" ng-init="$last && finished()">

Ответ 5

Вот простой подход с использованием ng-init, который даже не требует специальной директивы. Он работал хорошо для меня в определенных сценариях, например. необходимо автоматически прокручивать div из ng-повторяющихся элементов в определенный элемент при загрузке страницы, поэтому функция прокрутки должна ждать, пока ng-repeat не закончит рендеринг в DOM, прежде чем он сможет запустить.

<div ng-controller="MyCtrl">
    <div ng-repeat="thing in things">
        thing: {{ thing }}
    </div>
    <div ng-init="fireEvent()"></div>
</div>

myModule.controller('MyCtrl', function($scope, $timeout){
    $scope.things = ['A', 'B', 'C'];

    $scope.fireEvent = function(){

        // This will only run after the ng-repeat has rendered its things to the DOM
        $timeout(function(){
            $scope.$broadcast('thingsRendered');
        }, 0);

    };
});

Обратите внимание, что это полезно только для функций, которые нужно вызвать один раз после первоначального рендеринга ng-repeat. Если вам нужно вызывать функцию всякий раз, когда содержимое ng-repeat обновляется, вам придется использовать один из других ответов в этом потоке с помощью настраиваемой директивы.

Ответ 6

Дополняя Pavel ответ, что-то более читаемое и легко понятное:

<ul>
    <li ng-repeat="item in items" 
        ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>

Почему еще, по-вашему, angular.noop есть в первую очередь...?

<сильные > Преимущества:

Вам не нужно писать директиву для этого...

Ответ 7

Возможно, немного более простой подход с ngInit и Lodash debounce без необходимости настраиваемой директивы:

Контроллер:

$scope.items = [1, 2, 3, 4];

$scope.refresh = _.debounce(function() {
    // Debounce has timeout and prevents multiple calls, so this will be called 
    // once the iteration finishes
    console.log('we are done');
}, 0);

Шаблон:

<ul>
    <li ng-repeat="item in items" ng-init="refresh()">{{item}}</li>
</ul>

Update

Существует еще более простое решение с угловым ядром с использованием тройного оператора:

Шаблон:

<ul>
    <li ng-repeat="item in items" ng-init="$last ? doSomething() : null">{{item}}</li>
</ul>

Помните, что ngInit использует фазу предварительной компоновки ссылок, то есть выражение вызывается перед обработкой дочерних директив. Это означает, что может потребоваться асинхронная обработка.

Ответ 8

Также может потребоваться, когда вы проверите переменную scope.$last, чтобы обернуть триггер с помощью setTimeout(someFn, 0). A setTimeout 0 является принятой техникой в ​​javascript, и для моего directive очень важно правильно работать.

Ответ 9

Это улучшает идеи, выраженные в других ответах, чтобы показать, как получить доступ к свойствам ngRepeat ($ index, $first, $middle, $last, $even, $odd) при использовании декларативного синтаксиса и изолировать область действия (рекомендуемая рекомендация Google) с директивой element. Обратите внимание на основное отличие: scope.$parent.$last.

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return {
    restrict: 'E',
    scope: {
      someAttr: '='
    },
    link: function(scope, element, attrs) {
      angular.element(element).css('color','blue');
      if (scope.$parent.$last){
        window.alert("im the last!");
      }
    }
  };
});

Ответ 10

Я хотел бы добавить еще один ответ, так как в предыдущих ответах он понимает, что код, необходимый для запуска после выполнения ngRepeat, представляет собой код angular, который в этом случае дает все более высокое решение, некоторые более общий, чем другие, и в случае его важного этапа жизненного цикла дайджеста вы можете взглянуть на блог Ben Nadel об этом, за исключением используя $parse вместо $eval.

но по моему опыту, как утверждает OP, он обычно запускает некоторые плагины или методы JQuery на финализированном DOM, который в этом случае я обнаружил, что самым простым решением является создание директивы с setTimeout, поскольку setTimeout функция попадает в конец очереди браузера, ее всегда справа после того, как все сделано в angular, обычно ngReapet, который продолжается после того, как его родительские функции postLinking

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

для тех, кто задается вопросом, что в этом случае, почему бы не использовать $timeout, его то, что он вызывает другой цикл дайджеста, совершенно ненужно

Ответ 11

Я сделал это так.

Создать директиву

function finRepeat() {
    return function(scope, element, attrs) {
        if (scope.$last){
            // Here is where already executes the jquery
            $(document).ready(function(){
                $('.materialboxed').materialbox();
                $('.tooltipped').tooltip({delay: 50});
            });
        }
    }
}

angular
    .module("app")
    .directive("finRepeat", finRepeat);

После того, как вы добавите его на ярлык, где этот ng-repeat

<ul>
    <li ng-repeat="(key, value) in data" fin-repeat> {{ value }} </li>
</ul>

И готовый с этим будет запущен в конце ng-repeat.

Ответ 12

<div ng-repeat="i in items">
        <label>{{i.Name}}</label>            
        <div ng-if="$last" ng-init="ngRepeatFinished()"></div>            
</div>

Моим решением было добавить div для вызова функции, если элемент был последним в повторе.

Ответ 13

Мне пришлось отображать формулы, используя MathJax после концов ng-repeat, ни один из вышеперечисленных ответов не решил мою проблему, поэтому я сделал, как показано ниже. Это не хорошее решение, но работало для меня...

<div ng-repeat="formula in controller.formulas">
    <div>{{formula.string}}</div>
    {{$last ? controller.render_formulas() : ""}}
</div>

Ответ 14

Если вы просто хотите изменить имя класса, чтобы оно получилось по-другому, ниже код выполнит трюк.

<div>
<div ng-show="loginsuccess" ng-repeat="i in itemList">
    <div id="{{i.status}}" class="{{i.status}}">
        <div class="listitems">{{i.item}}</div>
        <div class="listitems">{{i.qty}}</div>
        <div class="listitems">{{i.date}}</div>
        <div class="listbutton">
            <button ng-click="UpdateStatus(i.$id)" class="btn"><span>Done</span></button>
            <button ng-click="changeClass()" class="btn"><span>Remove</span></button>
        </div>
    <hr>
</div>

Этот код работал у меня, когда у меня было аналогичное требование для рендеринга покупной позиции в моем списке покупок в шрифте Strick.