एनजी-रिपीट फिनिश इवेंट


207

मैं टेबल के साथ कुछ jQuery फ़ंक्शन लक्ष्यीकरण div को कॉल करना चाहता हूं। उस तालिका के साथ आबादी है ng-repeat

जब मैं इस पर कॉल करता हूं

$(document).ready()

मेरा कोई नतीजा नहीं है।

भी

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

मदद नहीं करता है।

क्या एनजी-रिपीट जनसंख्या के पूरा होने के बाद फ़ंक्शन को निष्पादित करने का कोई तरीका है? मैंने कस्टम का उपयोग करने के बारे में एक सलाह पढ़ी है directive, लेकिन मुझे कोई सुराग नहीं है कि इसे एनजी-रिपीट और मेरे डिव के साथ कैसे उपयोग किया जाए ...

जवाबों:


241

वास्तव में, आपको निर्देशों का उपयोग करना चाहिए, और एनजी-रिपीट लूप के अंत तक कोई घटना नहीं है (जैसा कि प्रत्येक तत्व व्यक्तिगत रूप से निर्मित होता है, और इसकी अपनी घटना है)। लेकिन एक) निर्देशों का उपयोग करना आप सभी की आवश्यकता हो सकती है और ख) कुछ एनजी-रिपीट विशिष्ट गुण हैं जिनका उपयोग आप अपने "एनजीरिपेट समाप्त" घटना पर कर सकते हैं।

विशेष रूप से, यदि आप चाहते हैं कि सभी तालिका में घटनाओं को स्टाइल / जोड़ दें, तो आप एक निर्देशन का उपयोग करके ऐसा कर सकते हैं जिसमें सभी 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');
  };
});

इसे इस प्लंकर में कार्रवाई में देखें । आशा करता हूँ की ये काम करेगा!


क्या मैं myMainDirectiveलूप के अंत के बाद ही कोड निष्पादित कर सकता हूं ? मुझे पेरेंट डिव का स्क्रॉलबार अपडेट करना होगा।
ChruS

2
बेशक! बस "window.alert" को इवेंट फायरिंग फ़ंक्शन में बदलें, और इसे मुख्य निर्देश के साथ पकड़ें। मैंने एक उदाहरण के रूप में ऐसा करने के लिए डी प्लंकर को अपडेट किया।
टिआगो रोल्डो

9
पहले (एंगुलर से पहले) jQuery लाइब्रेरी को शामिल करने की सिफारिश की गई है। इससे सभी कोणीय तत्व jQuery (jqLite के बजाय) के साथ लिपटे होंगे। इसके बाद कोणीय.सेलेमेंट (तत्व) .css () और $ (तत्व) .children ()। Css () के बजाय आप बस element.css () और element.children ()। Css () लिख सकते हैं। यह भी देखें groups.google.com/d/msg/angular/6A3Skwm59Z4/oJ0WhKGAFK0J
मार्क Rajcok

11
किसी को भी कोई भी पता है कि यह कैसे प्राप्त करने के लिए काम करने के बाद ngRepeat निर्देश पर renders? यानी: लिंक
रेवेनहर्ट्स

1
यह सभी कोणीय निर्देशों के लिए एक नामकरण सम्मेलन है। Docs.angularjs.org/guide/directive#normalization
Tiago

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

एक लाइव डेमो देखें।


यदि मैं सिंटैक्स के रूप में नियंत्रक का उपयोग करता हूं तो क्या यह काम करता है? कृप्या? नहीं, क्या कोई अन्य तरीका है जो नियंत्रक के साथ काम करता है?
डैन गुयेन

63

विशेष रूप से सिर्फ एक ng-repeatपूरी घटना के लिए एक निर्देश बनाने की कोई आवश्यकता नहीं है ।

ng-init आपके लिए जादू करता है।

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

यह $lastसुनिश्चित करता है, कि finishedकेवल निकाल दिया जाता है, जब अंतिम तत्व को डीएम को प्रदान किया गया है।

$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()">

क्या होगा अगर "चीजें" एक खाली सरणी बन जाती हैं?
फ्रेंक पोल्जाक

1
@FranePoljak मैंने उत्तर में समाधान जोड़ दिया है।
विकास बंसल

1
खाली सरणी के मामले में, नियंत्रक में इसे संभाल लें
JSAddict

विकास बंसल उत्तर को ठीक करें: // पहले डिव का उपयोग करें यदि आप चेक करें कि क्या सरणी का कोई मूल्य नहीं है और तदनुसार फ़ंक्शन को कॉल करें। <div ng-if = "? things "length" ng-hide = "true" ng-init = "समाप्त ()"> </ div> <div ng-repeat = "चीजों में" ng-init = "$ अंतिम और& समाप्त () ">
एलेक्स टेस्लान्को

41

यहाँ एक दोहरावदार निर्देश दिया गया है जो सच होने पर एक निर्दिष्ट फ़ंक्शन को कॉल करता है। मैंने पाया है कि डोम फ़ंक्शन को DOM हेरफेर करने से पहले इंटरवल = 0 के साथ $ टाइमआउट का उपयोग करना चाहिए, जैसे कि रेंडर किए गए तत्वों पर टूलटिप्स को इनिशियलाइज़ करना। jsFiddle: http://jsfiddle.net/tQw6w/

$ गुंजाइश.layoutDone में, $ टाइमआउट लाइन पर टिप्पणी करने का प्रयास करें और "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>

जे एस:

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

    })

मैं $ टाइमआउट का उपयोग नहीं करना चाहता था, लेकिन जब आपने कहा कि इसे 0 पर सेट किया जा सकता है, तो मैंने इसे एक कोशिश देने का फैसला किया और यह अच्छी तरह से काम किया। मुझे आश्चर्य है कि अगर यह पाचन का मामला है, और अगर $ टाइमआउट का उपयोग किए बिना $ टाइमआउट करने का एक तरीका है।
जमीला हक

क्या आप बता सकते हैं कि यह काम क्यों करता है? मैं डायनामिक आईडी एक्सेस करने की कोशिश कर रहा हूं और यह केवल 0 विलंब के साथ टाइमआउट होने के बाद काम करता है। मैंने कई पोस्टों में इसका "हैकी" समाधान देखा, लेकिन कोई भी यह नहीं बताता कि यह क्यों काम करता है।
जिन

30

यहां एक सरल दृष्टिकोण का उपयोग किया गया है ng-initजिसे कस्टम निर्देश की भी आवश्यकता नहीं है। यह मेरे लिए कुछ खास परिदृश्यों में अच्छी तरह से काम कर रहा है, जैसे पृष्ठ लोड पर किसी विशेष आइटम के लिए एनजी-दोहराया वस्तुओं के एक ऑटो को स्क्रॉल करने की आवश्यकता है, इसलिए स्क्रॉल फ़ंक्शन को तब तक इंतजार करना पड़ता है जब तक ng-repeatकि वह आग लगाने से पहले डोम को प्रदान नहीं कर देता।

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

    };
});

ध्यान दें कि यह केवल उन कार्यों के लिए उपयोगी है जिन्हें आपको एनजी-रिपीटर रेंडर के बाद एक बार कॉल करने की आवश्यकता है। यदि आपको किसी फ़ंक्शन को कॉल करने की आवश्यकता होती है, जब भी एनजी-रिपीट सामग्री अपडेट की जाती है, तो आपको इस धागे पर अन्य निर्देशों में से एक का उपयोग कस्टम निर्देश के साथ करना होगा।


1
सुंदर, यह काम किया। मैं टेक्स्टबॉक्स में टेक्स्ट को ऑटो सेलेक्ट करना चाहता था, और टाइमआउट ने चाल चली। अन्यथा, {{model.value}} पाठ चयनित हो गया और फिर डेटा-बाउंड मॉडल ।value इंजेक्ट होने पर रद्द कर दिया गया।
जोशफोर

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>

अपडेट करें

टर्नररी ऑपरेटर का उपयोग करते हुए सरल एंगुलरजेएस समाधान भी सरल है:

टेम्पलेट:

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

ध्यान रखें कि ngInit प्री-लिंक संकलन चरण का उपयोग करता है - अर्थात बच्चे के निर्देशों को संसाधित करने से पहले अभिव्यक्ति को लागू किया जाता है। इसका मतलब है कि अभी भी एक अतुल्यकालिक प्रसंस्करण की आवश्यकता हो सकती है।


ध्यान दें कि काम करने के लिए आपको इसके लिए lodash.js की आवश्यकता है :) इसके अलावा: $ गुंजाइश का ", 20" हिस्सा ।refresh =, क्या इस विधि को अंतिम पुनरावृत्ति के बाद बुलाया जाने से पहले मिली की संख्या है?
जोर्जेन स्केचर फिशर

जोड़ा Lodash के सामने debounce लिंक। हां, विधि निष्पादित होने से पहले 20 देरी हो रही है - 0 में परिवर्तित हो जाती है क्योंकि कॉल के रूप में यह तब तक कोई फर्क नहीं पड़ता जब तक कि कॉल कॉल न हो जाए।
पावेल होराल

1
इस बारे में सोचकर भी, जैसा कि टर्नरी ऑपरेटर की अनुमति है अभिव्यक्ति सरल ng-init="$last ? refresh() : false"रूप में भी काम करेगी। और इसके लिए लोदश की भी आवश्यकता नहीं है।
पावेल होराल

<div ng-repeat = "i in [1,2,4]" ng-init = "doSomething ($ last)"> </ div> $ गुंजाइश.doSomething = function (lastElement) {if (lastElem): blah blah blah }
JSAddict

4

यह तब भी आवश्यक हो सकता है जब आप scope.$lastअपने ट्रिगर को एक के साथ लपेटने के लिए चर की जांच करते हैं setTimeout(someFn, 0)। एक setTimeout 0जावास्क्रिप्ट में स्वीकृत तकनीक है और यह जरूरी था मेरे लिए directiveसही ढंग से चलाने के लिए।


मुझे वही मुद्दा मिला। दोनों backbone + कोणीय में, यदि आपको कुछ करने की ज़रूरत है जैसे कि DOM तत्व पर लागू css गुण मान पढ़ें, तो मैं टाइमआउट हैक w / o का तरीका नहीं समझ सका।
चॉवी

3

यह घोषणात्मक सिंटैक्स और आइसोलेट स्कोप का उपयोग करते समय नेकप्रेट गुण ($ सूचकांक, $ प्रथम, $ मध्य, $ अंतिम, $ भी, $ विषम) तक पहुंच प्राप्त करने के तरीके को दिखाने के लिए अन्य उत्तरों में व्यक्त किए गए विचारों का सुधार है। 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!");
      }
    }
  };
});

क्या तत्वों की तुलना में निर्देश के रूप में उपयोग करना बेहतर नहीं होगा? यानी प्रतिबंधित: 'ए'?
तेजस्वी हेगड़े

2
यह बिंदु घोषणात्मक वाक्यविन्यास + अलग-अलग गुंजाइश दिखाने के लिए था ... हाँ, यदि आप चाहें तो यहां एक निर्देश निर्देश का उपयोग करने के लिए आपका स्वागत है। आपके उद्देश्य के आधार पर या तो समय और स्थान है।
जोशुआ डेविड

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

आपके द्वारा लेबल पर जोड़ने के बाद यह एनजी-रिपीट होता है

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

और इसके साथ तैयार एनजी-रिपीट के अंत में चलाया जाएगा।


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

मेरा समाधान एक फ़ंक्शन को कॉल करने के लिए एक डिव को जोड़ना था यदि आइटम एक दोहराने में आखिरी था।


2

मैं एक और उत्तर जोड़ना चाहूंगा, क्योंकि पूर्ववर्ती उत्तर यह लेता है कि ngRepeat के बाद चलाने के लिए आवश्यक कोड एक कोणीय कोड है, जो उस स्थिति में उपरोक्त सभी उत्तर एक महान और सरल समाधान देते हैं, दूसरों की तुलना में कुछ अधिक सामान्य। और इसके महत्वपूर्ण पचाने के जीवन चक्र के चरण में आप इसके बारे में बेन नडेल के ब्लॉग पर एक नज़र डाल सकते हैं , $ eval के बजाय $ parse का उपयोग करने के अपवाद के साथ।

लेकिन मेरे अनुभव में, जैसा कि ओपी ने कहा है, आम तौर पर इसके लिए कुछ JQuery प्लगइन्स या विधियाँ चल रही हैं, जो डोमिनल कंपाइल किए गए DOM पर हैं, उस स्थिति में मैंने पाया कि सबसे सरल समाधान सेटटाइमआउट के साथ एक निर्देशन बनाना है, क्योंकि setTimeout पुश हो जाता है। ब्राउज़र की कतार के अंत में, इसके हमेशा सही होने के बाद सब कुछ कोणीय में किया जाता है, आमतौर पर ngReapet जो उसके माता-पिता के बाद कार्य जारी रहता है

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

जो कोई भी सोच रहा है कि उस मामले में $ टाइमआउट का उपयोग क्यों नहीं करना है, तो इसका कारण यह है कि यह एक और पाचन चक्र का कारण बनता है जो पूरी तरह से अनावश्यक है


1

मुझे एनजी-रिपीट के बाद मैथजैक्स का उपयोग करके फॉर्मूले सौंपने थे, ऊपर दिए गए जवाबों में से किसी ने भी मेरी समस्या हल नहीं की, इसलिए मैंने नीचे की तरह बनाया। यह एक अच्छा समाधान नहीं है , लेकिन मेरे लिए काम ...

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

मेरे लिए यह काम करता है, लेकिन मुझे अभी भी एक सेटटाइमआउट जोड़ना होगा

$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>

इस कोड ने मेरे लिए तब काम किया जब मुझे स्ट्रीक ट्राउट फ़ॉन्ट में अपनी खरीदारी सूची में खरीदारी की गई वस्तु को प्रस्तुत करने की समान आवश्यकता थी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.