ng-повторити подію фінішу


207

Я хочу зателефонувати до функції jQuery, націленої на div, з таблицею. Ця таблиця заповненаng-repeat .

Коли я закликаю

$(document).ready()

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

Також

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

не допомагає.

Чи є якийсь спосіб виконати функцію відразу після завершення сукупності ng-повторів? Я прочитав пораду щодо користувальницьких налаштувань directive, але не маю поняття, як її використовувати з ng-повтором і моїм дівом ...

Відповіді:


241

Дійсно, вам слід використовувати директиви, і жодна подія не прив’язана до кінця циклу ng-Repeat (оскільки кожен елемент побудований індивідуально і має власну подію). Але а) використання директив може бути всім, що вам потрібно, і b) є кілька властивостей ng-Repeat, які ви можете використовувати для створення події "on ngRepeat ready".

Зокрема, якщо все, що вам потрібно, - це стиль / додавання подій до всієї таблиці, ви можете зробити це, використовуючи директиву, яка охоплює всі елементи 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 . Сподіваюся, це допомагає!


Чи можу я змусити myMainDirectiveкод виконувати лише після закінчення циклу? Мені потрібно оновити смугу прокрутки батьківського діва.
ChruS

2
Звичайно! Просто змініть "window.alert" на функцію запуску події та знайдіть її за допомогою основної директиви. Я оновив де Плункера, щоб це зробити, як приклад.
Тіаго Ролдао

9
Рекомендується спочатку включити бібліотеку jQuery (перед Angular). Це призведе до того, що всі кутові елементи будуть обмотані jQuery (замість jqLite). Тоді замість angular.element (element) .css () та $ (element) .children (). Css () ви можете просто написати element.css () та element.children (). Css (). Дивіться також groups.google.com/d/msg/angular/6A3Skwm59Z4/oJ0WhKGAFK0J
Марк Райкок

11
Any1 має будь-яку ідею, як змусити це працювати для подальших виправлень директиви ngRepeat? тобто: посилання
RavenHursT

1
Це умовне позначення для всіх кутових директив. Дивіться docs.angularjs.org/guide/directive#normalization
Tiago Roldão

68

Якщо ви просто хочете виконати якийсь код в кінці циклу, ось дещо простіший варіант, який не потребує додаткової обробки подій:

<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');
    }
  };
});

Дивіться демо-версію.


Чи працює це, якщо я використовую контролер як синтаксис? Будь ласка? У ні, чи є інший спосіб, який працює з контролером як?
Dan Nguyen

63

Немає необхідності створювати директиву, особливо просто для ng-repeatповноцінного події.

ng-init робить для вас магію.

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

$lastпереконується, щоfinished тільки звільняють, коли останній елемент надані в DOM.

Не забудьте створити $scope.finished подію.

Щасливе кодування !!

EDIT: 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()">

Що робити, якщо "речі" виявляться порожнім масивом?
Frane Poljak

1
@FranePoljak Я додав рішення у відповідь.
Вікас Бансал

1
У разі порожнього масиву обробіть його в контролері
JSA

Виправте відповідь Вікаса Бансала: // використовуйте перший div, якщо хочете перевірити, чи масив не має жодного значення, і зателефонуйте відповідно до функції. <div ng-if = "! things.length" ng-hid = "true" ng-init = "закінчено ()"> </div> <div ng-repe = "річ у речах" ng-init = "$ останнє && закінчив () ">
Олексій Тесленко

41

Ось повторно виконана директива, яка викликає вказану функцію, коли є істиною. Я виявив, що викликана функція повинна використовувати $ timeout з інтервалом = 0, перш ніж робити DOM маніпуляції, такі як ініціалізація підказок на виведених елементах. jsFiddle: http://jsfiddle.net/tQw6w/

У $ range.layoutDone спробуйте прокоментувати рядок $ timeout та прокоментуйте "НЕ ПРАВИЛЬНО!" рядок, щоб побачити різницю в підказках.

<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...
        }

    })

Я не хотів використовувати $ timeout, але коли ви сказали, що його можна встановити на 0, я вирішив спробувати, і він спрацював добре. Цікаво, чи це питання травлення, і чи є спосіб зробити те, що робить $ timeout, не використовуючи $ timeout.
Jameela Huq

Чи можете ви поясніть, чому це працює? Я намагаюся отримати доступ до динамічних ідентифікаторів, і він працює лише після затримки з 0 затримкою. Я бачив це "хакі" рішення в декількох постах, але ніхто не пояснює, чому це працює.
Джин

30

Ось простий підхід із застосуванням, ng-initякий навіть не вимагає спеціальної директиви. Для мене це добре працює в певних сценаріях, наприклад, потрібно автоматично прокручувати розділ 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-повтор спочатку відображається. Якщо вам потрібно викликати функцію кожного разу, коли вміст ng-повторів оновлюється, тоді вам доведеться використовувати один з інших відповідей у ​​цьому потоці за допомогою спеціальної директиви.


1
Красиво, це зробило роботу. Я хотів автоматично вибрати текст у текстовому полі, і тайм-аут зробив свою справу. В іншому випадку текст {{model.value}} був обраний та скасований, коли було введено пов'язане з даними model.value.
JoshGough

27

Доповнюючи Павла відповідь , що - то більш читабельним і легко зрозумілого б:

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

Чому ще ти думаєш angular.noop, що там на першому місці ...?

Переваги:

Для цього вам не потрібно писати директиву ...


20
Навіщо користуватися потрійником, коли можна використовувати булеву логіку:ng-init="$last && doSomething( )"
Нейт Уіттакер

Його легше читати @NateWhittaker (і важче читати, ніж потрійний оператор, вражає). Добре мати чистий і простий для розуміння код
Джастін

21

Можливо, трохи простіший підхід ngInitі з Лодашем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>

Оновлення

Існує навіть простіше чисте рішення AngularJS з використанням потрійного оператора:

Шаблон:

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

Майте на увазі, що ngInit використовує етап компіляції перед посиланням - тобто вираз викликається перед обробкою дочірніх директив. Це означає, що все-таки може знадобитися асинхронна обробка.


Слід зазначити, що для роботи цього вам потрібен lodash.js :) Також: ", 20" частина $ range.refresh =, це число міліс перед тим, як цей метод буде викликаний після останньої ітерації?
Jørgen Skår Fischer

Додано Lodash перед протіводребезговой посилання. Так, 20 - це затримка перед виконанням методу - змінено на 0, оскільки це не має значення, поки виклик асинхронізований.
Павло Горал

1
Також думати про це, оскільки потрійний оператор дозволений у виразах простих, також ng-init="$last ? refresh() : false"буде добре. І цього Лодаш навіть не вимагає.
Павло Гораль

<div ng-repeat = "я в [1,2,4]" ng-init = "doSomething ($ last)"> </div> $ range.doSomething = функція (lastElement) {if (lastElem) бла-бла-бла }
Рішення JSA

4

Також може знадобитися, коли ви перевіряєте scope.$lastзмінну, щоб обернути ваш тригер символом a setTimeout(someFn, 0). A setTimeout 0- це прийнята методика в javascript, і для мене було обов'язково directiveправильно працювати.


Я знайшов те саме питання. і в магістралі + угловому, якщо вам потрібно зробити щось на кшталт читання значень властивості css, застосованих до елемента DOM, я не міг розібратися із способом злому тайм-аута.
чов

3

Це вдосконалення ідей, виражених в інших відповідях, щоб показати, як отримати доступ до властивостей ngRepeat ($ index, $ first, $ middle, $ last, $ even, $pard) при використанні декларативного синтаксису та ізоляції області ( Google рекомендував кращі практики) з елементами-директивою. Зверніть увагу на основну різницю: 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!");
      }
    }
  };
});

Хіба не було б краще використовувати директиви як атрибути, ніж елементи? тобто обмежити: "A"?
Tejasvi Hegde

2
Суть полягала в тому, щоб показати декларативний синтаксис + ізолювати область ... так, ви можете використовувати тут директиву атрибутів, якщо хочете. Є час і місце для кожного, залежно від вашої мети.
JoshuaDavid

3

Я зробив це так.

Створіть директиву

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-повторення

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

І готові з цим будуть запущені в кінці ng-повтору.


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

Моє рішення полягало в тому, щоб додати div для виклику функції, якщо елемент був останнім у повторі.


2

Я хотів би додати ще одну відповідь, оскільки попередні відповіді передбачають, що код, необхідний для запуску після ngRepeat, є кутовим кодом, і в цьому випадку всі відповіді вище дають чудове і просте рішення, деякі більш загальні, ніж інші, і на випадок, коли це важливий етап життєвого циклу, ви можете подивитися в блозі Бена Наделя , за винятком використання $ parse замість $ eval.

але, на моєму досвіді, як заявляє ОП, його зазвичай використовують деякі плагіни JQuery або методи на остаточно складеному DOM, і в цьому випадку я виявив, що найпростішим рішенням є створення директиви з setTimeout, оскільки функція setTimeout стає натиснутою до кінця черги браузера, його завжди правильно після того, як все робиться у кутовому, як правило, ngReapet, який продовжується після того, як його батьки виконують функцію PostLinking

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

для тих, хто цікавиться, що в такому випадку, чому б не використовувати $ timeout, це спричиняє черговий цикл переробки, який є абсолютно непотрібним


1

Мені довелося виводити формули за допомогою MathJax після закінчення ng-повторів, жодна з наведених вище відповідей не вирішила мою проблему, тому я зробив, як нижче. Це не приємне рішення , але працювало для мене ...

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

0

Я знайшов відповідь тут добре практикованою, але все ж потрібно було додати затримку

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

angular.module('MyApp').directive('emitLastRepeaterElement', function() {
return function(scope) {
    if (scope.$last){
        scope.$emit('LastRepeaterElement');
    }
}; });

Додайте його до свого ретранслятора як атрибут, наприклад:

<div ng-repeat="item in items" emit-last-repeater-element></div>

За словами Раду:

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {mycode});

Для мене це працює, але мені все одно потрібно додати setTimeout

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {
setTimeout(function() { 
    mycode
}, 100); });

-4

Якщо ви просто хочете змінити ім'я класу, щоб воно відображалося інакше, нижче код буде робити хитрість.

<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 корита.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.