এনজি-রিপিট শেষ হয়ে গেলে কোনও ফাংশন কল করা


249

আমি যা বাস্তবায়নের চেষ্টা করছি তা হ'ল হ্যান্ডলারটি মূলত একটি "এনজি রিপিট সমাপ্ত রেন্ডারিং"। কখন এটি সম্পন্ন হয় তা সনাক্ত করতে সক্ষম হয়েছি তবে এটি থেকে কোনও ফাংশনটি কীভাবে ট্রিগার করা যায় তা আমি বুঝতে পারি না।

ভাঁড়টি দেখুন: http://jsfiddle.net/paulocoelho/BsMqq/3/

জাতীয়

var module = angular.module('testApp', [])
    .directive('onFinishRender', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                element.ready(function () {
                    console.log("calling:"+attr.onFinishRender);
                    // CALL TEST HERE!
                });
            }
        }
    }
});

function myC($scope) {
    $scope.ta = [1, 2, 3, 4, 5, 6];
    function test() {
        console.log("test executed");
    }
}

এইচটিএমএল

<div ng-app="testApp" ng-controller="myC">
    <p ng-repeat="t in ta" on-finish-render="test()">{{t}}</p>
</div>

উত্তর : ফিনিশিংমোভ থেকে ফিডিং কাজ করছে: http://jsfiddle.net/paulocoelho/BsMqq/4/


কৌতূহলের বাইরে, element.ready()স্নিপেটের উদ্দেশ্য কী ? আমি বলতে চাইছি .. এটি আপনার কাছে কিছু প্রকারের jQuery প্লাগইন, বা উপাদান প্রস্তুত হওয়ার সাথে সাথে এটি ট্রিগার করা উচিত?
gion_13

1
এটি এনজি-
থ্রির


এর প্রতিলিপি stackoverflow.com/questions/13471129/... , আমার উত্তর সেখানে দেখতে
bresleveloper

উত্তর:


400
var module = angular.module('testApp', [])
    .directive('onFinishRender', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit(attr.onFinishRender);
                });
            }
        }
    }
});

লক্ষ্য করুন যে আমি ব্যবহার করিনি .ready()বরং এটি একটিতে মুড়িয়েছি $timeout$timeoutএনজি-পুনরাবৃত্তি উপাদানগুলি সত্যিই উপস্থাপনা শেষ করার পরে এটি কার্যকর করা হয় তা নিশ্চিত করে (কারণ $timeoutবর্তমান ডাইজেস্ট চক্রের শেষের দিকে এক্সিকিউট করা হবে - এবং এটি $applyপৃথকভাবে অভ্যন্তরীণও কল করবেsetTimeout )। সুতরাং ng-repeatসমাপ্তির পরে , আমরা $emitবাইরের স্কোপগুলিতে (ভাইবাল এবং পিতামাতা স্কোপস) কোনও ইভেন্ট নির্গত করতে ব্যবহার করি ।

এবং তারপরে আপনার নিয়ামকের মধ্যে, আপনি এটির সাথে এটি ধরতে পারেন $on:

$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
    //you also get the actual event object
    //do stuff, execute functions -- whatever...
});

এইচটিএমএল সহ এমন কিছু দেখাচ্ছে:

<div ng-repeat="item in items" on-finish-render="ngRepeatFinished">
    <div>{{item.name}}}<div>
</div>

10
+1, তবে আমি কোনও ইভেন্ট ব্যবহার করার আগে $ ইভালটি ব্যবহার করব - কম সংযুক্তকরণ। আরও তথ্যের জন্য আমার উত্তর দেখুন।
রাজ্জককে

1
@ পিগালেভপ্যাভেল আমি মনে করি আপনি বিভ্রান্ত করছেন $timeout(যা মূলত setTimeout+ কৌনিক এর $scope.$apply()) setInterval। শর্ত $timeoutঅনুযায়ী কেবল একবার কার্যকর করা হবে ifএবং এটি পরবর্তী $digestচক্রের শুরুতে হবে । জাভাস্ক্রিপ্টের সময়সীমা সম্পর্কে আরও তথ্যের জন্য, দেখুন: ejohn.org/blog/how-javascript-timers-work
holographic-

1
@ ফিনিশিংয়েভ হয়ত আমি কিছু বুঝতে পারি না :) তবে দেখুন অন্য এনজি-রিপিটে আমার কাছে এনজি-রিপিট আছে। এবং আমি নিশ্চিত হয়ে জানতে চাই যে এগুলি সব শেষ হলে। আমি যখন আপনার স্ক্রিপ্টটি parent টাইমআউট দিয়ে কেবল পিতামাতার জন্য এনজি-রিপিট ব্যবহার করি তখন সব ঠিকঠাক হয়। তবে আমি যদি $ সময়সীমা ব্যবহার না করি, বাচ্চাদের এনজি-রিপিট শেষ হওয়ার আগেই আমি একটি প্রতিক্রিয়া পেয়েছি। আমি জানতে চাই কেন? এবং আমি কী নিশ্চিত যে আমি যদি আপনার স্ক্রিপ্টটি parent সময়সীমার সাথে প্যারেন্ট এনজি-রিপিটের জন্য ব্যবহার করি তবে সমস্ত এনজি-রিপিটস শেষ হয়ে গেলে আমি সবসময়ই একটি প্রতিক্রিয়া পাব?
পিগালেভ পাভেল

1
আপনি setTimeout()0 দেরির সাথে কেন এমন কিছু ব্যবহার করবেন তা অন্য প্রশ্ন, যদিও আমি অবশ্যই বলব যে আমি কখনই ব্রাউজারের ইভেন্টের সারির জন্য প্রকৃত স্পেসিফিকেশনটি দেখতে পাইনি, কেবল একক থ্রেডনেসড এবং অস্তিত্ব দ্বারা বোঝানো setTimeout()
র্যাকলাইস

1
এই উত্তরের জন্য ধন্যবাদ। আমার একটি প্রশ্ন আছে, কেন আমাদের নিয়োগের দরকার on-finish-render="ngRepeatFinished"? যখন আমি নিয়োগ করি on-finish-render="helloworld", এটি একই কাজ করে।
আরনৌড

89

আপনি যদি ডওমটি নির্মাণের পরে আপনার কলব্যাক (যেমন, পরীক্ষা () পরীক্ষা করতে চান তবে ব্রাউজারটি রেন্ডার করার আগে $ evalAsync ব্যবহার করুন । এই দপদপ করে ওঠার প্রতিরোধ করবে - সুত্র

if (scope.$last) {
   scope.$evalAsync(attr.onFinishRender);
}

বেহালার

রেন্ডারিংয়ের পরে যদি আপনি সত্যিই আপনার কলব্যাকটি কল করতে চান তবে $ সময়সীমা ব্যবহার করুন:

if (scope.$last) {
   $timeout(function() { 
      scope.$eval(attr.onFinishRender);
   });
}

আমি ইভেন্টের পরিবর্তে $ eval পছন্দ করি। একটি ইভেন্ট সহ, আমাদের ইভেন্টটির নাম জানতে হবে এবং সেই ইভেন্টের জন্য আমাদের নিয়ামককে কোড যুক্ত করতে হবে। Al eval সহ, নিয়ামক এবং নির্দেশকের মধ্যে কম সংযোগ রয়েছে।


চেকটি কি করে $last? ডক্সে এটি খুঁজে পাচ্ছে না। এটি সরানো হয়েছে?
এরিক অাইনার

2
@ এরিকআিগনার, স্কোপে $lastসংজ্ঞায়িত হয় যদি কোনও ng-repeatউপাদানটিতে সক্রিয় থাকে।
রাজকক

দেখে মনে হচ্ছে আপনার ফিডাল অযথা লাগছে $timeout
bbodenmiller

1
গ্রেট। পারফরম্যান্সের দৃষ্টিকোণ থেকে এমিট / সম্প্রচারের জন্য বেশি ব্যয় দেখে এটি গ্রহণযোগ্য উত্তর হওয়া উচিত।
ফ্লোরিন ভিসটিগ

29

এখনও অবধি দেওয়া উত্তরগুলি কেবল প্রথমবারের জন্য ng-repeatরেন্ডার হয়ে কাজ করবে তবে আপনার যদি একটি গতিশীল থাকে ng-repeat, যার অর্থ আপনি যে আইটেমগুলি যোগ / মুছতে / ফিল্টার করতে চলেছেন এবং আপনাকে প্রতিবার অবহিত করা দরকার যে ng-repeatরেন্ডার হয়ে যায়, সেই সমাধানগুলি আপনার পক্ষে কার্যকর হবে না।

সুতরাং, আপনাকে যদি প্রতিটি সময় অবহিত করার দরকার হয় যেগুলিng-repeat প্রথমবারের মতো পুনরায় রেন্ডার হয়ে যায়, আমি এটি করার একটি উপায় খুঁজে পেয়েছি, এটি বেশ 'হ্যাকি', তবে আপনি কী জানেন তা যদি ঠিকঠাক হয়ে যায় করছেন। ব্যবহার করুন $filterআপনার ng-repeat আগে আপনি অন্য কোন ব্যবহার$filter :

.filter('ngRepeatFinish', function($timeout){
    return function(data){
        var me = this;
        var flagProperty = '__finishedRendering__';
        if(!data[flagProperty]){
            Object.defineProperty(
                data, 
                flagProperty, 
                {enumerable:false, configurable:true, writable: false, value:{}});
            $timeout(function(){
                    delete data[flagProperty];                        
                    me.$emit('ngRepeatFinished');
                },0,false);                
        }
        return data;
    };
})

এটি প্রতিবার যে $emitইভেন্টটিকে রেন্ডার করা হবে তা ডাকা হবে ।ngRepeatFinishedng-repeat

এটি কিভাবে ব্যবহার করতে:

<li ng-repeat="item in (items|ngRepeatFinish) | filter:{name:namedFiltered}" >

ngRepeatFinishফিল্টার একটি সরাসরি প্রয়োগ করা প্রয়োজন Arrayঅথবা একটি Objectআপনার সংজ্ঞায়িত $scope, আপনি পরে অন্যান্য ফিল্টারের আবেদন করতে পারেন।

কীভাবে এটি ব্যবহার করবেন না:

<li ng-repeat="item in (items | filter:{name:namedFiltered}) | ngRepeatFinish" >

প্রথমে অন্যান্য ফিল্টার প্রয়োগ করবেন না এবং তারপরে ngRepeatFinishফিল্টারটি প্রয়োগ করুন ।

আমি কখন এটি ব্যবহার করব?

তালিকাটি রেন্ডারিং শেষ হয়ে যাওয়ার পরে আপনি যদি ডিওএম-তে নির্দিষ্ট সিএসএস শৈলী প্রয়োগ করতে চান তবে আপনার দ্বারা ডিওএম উপাদানগুলির নতুন মাত্রাগুলি বিবেচনা করতে হবে যা দ্বারা পুনরায় রেন্ডার করা হয়েছে ng-repeat। (বিটিডাব্লু: এই ধরণের অপারেশনগুলি কোনও নির্দেশিকার ভিতরে করা উচিত)

ngRepeatFinishedইভেন্টটি পরিচালনা করে এমন ফাংশনে কী করবেন না :

  • $scope.$applyসেই ফাংশনে একটি সম্পাদন করবেন না বা আপনি কৌনিকটিকে একটি অন্তহীন লুপে রাখবেন যা কৌণিক সনাক্ত করতে সক্ষম হবে না।

  • $scopeবৈশিষ্ট্যগুলিতে পরিবর্তন করার জন্য এটি ব্যবহার করবেন না , কারণ পরবর্তী $digestলুপ হওয়া পর্যন্ত এই পরিবর্তনগুলি আপনার দৃষ্টিতে প্রতিফলিত হবে না এবং যেহেতু আপনি কোনও সম্পাদন করতে পারবেন না $scope.$applyসেগুলি কোনও কাজে আসবে না।

"তবে ফিল্টারগুলি সেভাবে ব্যবহার করার জন্য নয় !!"

না, তারা না, এটি একটি হ্যাক, যদি আপনি এটি পছন্দ করেন না তবে এটি ব্যবহার করবেন না। আপনি যদি একই জিনিসটি সম্পাদন করার আরও ভাল উপায় জানেন তবে দয়া করে আমাকে তা জানান।

সংক্ষেপিত

এটি একটি হ্যাক , এবং এটি ভুল উপায়ে ব্যবহার করা বিপজ্জনক, ng-repeatরেন্ডারিং শেষ হওয়ার পরে এটি কেবল স্টাইল প্রয়োগ করার জন্য ব্যবহার করুন এবং আপনার কোনও সমস্যা নেই।


Thx 4 টিপ। যাইহোক, একটি কাজের উদাহরণ সহ একটি plunkr অনেক প্রশংসা করা হবে।
হলোগ্রাফিক্স

2
দুর্ভাগ্যক্রমে এটি আমার পক্ষে কমপক্ষে সর্বশেষতম AngularJS 1.3.7 এর জন্য কাজ করে না। সুতরাং আমি খুঁজে পেয়েছি অন্যটি কার্যকর, সবচেয়ে ভালটি নয়, তবে এটি যদি আপনার জন্য প্রয়োজনীয় হয় তবে এটি কার্যকর হবে। আমি যা করেছি তা হ'ল আমি যখনই উপাদান যুক্ত / পরিবর্তন / মুছি আমি সর্বদা তালিকার শেষে অন্য ডামি উপাদান যুক্ত করি, সুতরাং যেহেতু $lastউপাদানটিও পরিবর্তিত হয়, এখন ngRepeatFinishচেক করে সরল নির্দেশনা $lastকাজ করবে। NgRepeatFinish বলা মাত্রই আমি ডামি আইটেমটি সরিয়ে ফেলব। (আমি এটি সিএসএস দিয়েও আড়াল করি যাতে এটি সংক্ষিপ্ত আকারে প্রদর্শিত না হয়)
ডার্ক্লোভ

এই হ্যাক দুর্দান্ত কিন্তু নিখুঁত নয়; প্রতিবার ইভেন্টের ফিল্টার
কিকগুলি

নীচের একটি Mark Rajcokপুরোপুরি এনজি-রিপিটের প্রতিটি পুনরায় রেন্ডার করে (যেমন মডেল পরিবর্তনের কারণে) works সুতরাং এই হ্যাকি সমাধানের প্রয়োজন নেই।
Dzhu

1
@ জোসেপ আপনি ঠিক বলেছেন - যখন আইটেমগুলি বিভক্ত / আনশিফ্ট করা হয় (যেমন অ্যারেটি রূপান্তরিত করা হয়), এটি কার্যকর হয় না। আমার আগের পরীক্ষাটি সম্ভবত অ্যারের সাথে ছিল যা পুনরায় বরাদ্দ করা হয়েছে (চিন্জ ব্যবহার করে) সুতরাং এটি প্রতিবার কাজ করেছিল ... এটি দেখানোর জন্য ধন্যবাদ
জাজু

10

যদি আপনাকে একই নিয়ামকটিতে বিভিন্ন এনজি-পুনরাবৃত্তিগুলির জন্য বিভিন্ন ফাংশন কল করতে হয় আপনি এই জাতীয় কিছু চেষ্টা করতে পারেন:

নির্দেশনা:

var module = angular.module('testApp', [])
    .directive('onFinishRender', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
            $timeout(function () {
                scope.$emit(attr.broadcasteventname ? attr.broadcasteventname : 'ngRepeatFinished');
            });
            }
        }
    }
});

আপনার নিয়ামকটিতে, $ অন এর সাথে ইভেন্টগুলি ধরুন:

$scope.$on('ngRepeatBroadcast1', function(ngRepeatFinishedEvent) {
// Do something
});

$scope.$on('ngRepeatBroadcast2', function(ngRepeatFinishedEvent) {
// Do something
});

একাধিক এনজি-রিপিট সহ আপনার টেমপ্লেটে

<div ng-repeat="item in collection1" on-finish-render broadcasteventname="ngRepeatBroadcast1">
    <div>{{item.name}}}<div>
</div>

<div ng-repeat="item in collection2" on-finish-render broadcasteventname="ngRepeatBroadcast2">
    <div>{{item.name}}}<div>
</div>

5

অন্যান্য সমাধান প্রাথমিক পৃষ্ঠার লোডে ঠিকঠাক কাজ করবে, তবে নিয়ামককে $ টাইমআউট কল করা একমাত্র উপায় যে মডেল পরিবর্তন হওয়ার পরে আপনার ফাংশনটি ডাকা হবে তা নিশ্চিত করা। এখানে একটি কার্যকারী ফিডল যা $ সময়সীমা ব্যবহার করে। আপনার উদাহরণের জন্য এটি হবে:

.controller('myC', function ($scope, $timeout) {
$scope.$watch("ta", function (newValue, oldValue) {
    $timeout(function () {
       test();
    });
});

এনজিরিপিট কেবলমাত্র সারণির সামগ্রীটি নতুন হলে একটি নির্দেশনার মূল্যায়ন করবে, সুতরাং আপনি যদি তালিকা থেকে আইটেমগুলি সরিয়ে ফেলেন তবে ফিনিশরেন্ডার চালিত হবে না। উদাহরণস্বরূপ, এইসব fiddles ফিল্টার মান লিখে চেষ্টা নির্গত


একইভাবে, মডেলটি ফিডল
জন হারলে

2
কি taমধ্যে $scope.$watch("ta",...?
মুহাম্মদ আদিল জাহিদ

4

আপনি যদি ডাবল ডলারের স্কোপ প্রপস ব্যবহার করতে বিরত না হন এবং আপনি এমন কোনও নির্দেশনা লিখছেন যার কেবলমাত্র বিষয়বস্তু পুনরাবৃত্তি হয় তবে খুব সহজ সমাধান পাওয়া যায় (ধরে নিলে আপনি কেবল প্রাথমিক রেন্ডার সম্পর্কে যত্নশীল)। লিঙ্ক ফাংশনে:

const dereg = scope.$watch('$$childTail.$last', last => {
    if (last) {
        dereg();
        // do yr stuff -- you may still need a $timeout here
    }
});

এটি এমন ক্ষেত্রে কার্যকর যেখানে আপনার কোনও নির্দেশিকা রয়েছে যা রেন্ডার তালিকার সদস্যদের প্রশস্ততা বা উচ্চতার উপর ভিত্তি করে ডোম ম্যানিপ করা দরকার (যা আমি মনে করি যে এটি সম্ভবত এই প্রশ্নটি জিজ্ঞাসা করবে), তবে এটি জেনেরিক নয় প্রস্তাবিত অন্যান্য সমাধান হিসাবে।


1

ফিল্টার করা এনজিরিপিট নিয়ে এই সমস্যার সমাধান মিউটেশন ইভেন্টগুলির সাথে থাকতে পারে তবে সেগুলি অবিলম্বে (তাত্ক্ষণিক প্রতিস্থাপন ব্যতীত) অবহিত করা হয়।

তারপরে আমি আরও একটি সহজ সম্পর্কে ভেবেছিলাম:

app.directive('filtered',function($timeout) {
    return {
        restrict: 'A',link: function (scope,element,attr) {
            var elm = element[0]
                ,nodePrototype = Node.prototype
                ,timeout
                ,slice = Array.prototype.slice
            ;

            elm.insertBefore = alt.bind(null,nodePrototype.insertBefore);
            elm.removeChild = alt.bind(null,nodePrototype.removeChild);

            function alt(fn){
                fn.apply(elm,slice.call(arguments,1));
                timeout&&$timeout.cancel(timeout);
                timeout = $timeout(altDone);
            }

            function altDone(){
                timeout = null;
                console.log('Filtered! ...fire an event or something');
            }
        }
    };
});

একের পর এক পরিবর্তনগুলি দেখার জন্য এক-টিক-টাইমআউট সহ পিতামাত্ত্বিক উপাদানটির নোড.প্রোটোটাইপ পদ্ধতিগুলিতে এটি লুকায়।

এটি বেশিরভাগ ক্ষেত্রে সঠিকভাবে কাজ করে তবে আমি এমন কিছু কেস পেয়েছি যেখানে AltDone কে দুবার ডাকা হবে।

আবার ... এই নির্দেশনাটি এনজিপিটেটের পিতামাতার সাথে যুক্ত করুন।


এটি এখন পর্যন্ত সবচেয়ে ভাল কাজ করে তবে এটি নিখুঁত নয়। উদাহরণস্বরূপ আমি 70 টি আইটেম থেকে 68 এ চলেছি, তবে এটি আগুন দেয় না।
গৌই

1
কি কাজ করতে পারে তা উপরের কোডটিতে appendChildপাশাপাশি যুক্ত করা হচ্ছে (যেহেতু আমি কেবল ব্যবহার করেছি insertBeforeএবং removeChild)।
সিজেটি

1

খুব সহজ, আমি এটি এটি করেছিলাম।

.directive('blockOnRender', function ($blockUI) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            
            if (scope.$first) {
                $blockUI.blockElement($(element).parent());
            }
            if (scope.$last) {
                $blockUI.unblockElement($(element).parent());
            }
        }
    };
})


এই কোডটি অবশ্যই কাজ করে যদি আপনার নিজস্ব $ blockUI পরিষেবা থাকে।
সিফেলফু

0

অনুগ্রহ করে ফিডলটি দেখুন, http://jsfiddle.net/yNXS2/ । যেহেতু আপনি যে নির্দেশনাটি তৈরি করেছেন তা কোনও নতুন সুযোগ তৈরি করেনি আমি সেই পথে চালিয়ে গিয়েছি।

$scope.test = function(){... যে ঘটেছে।


ভুল ভুল? প্রশ্নে একই।
জেমস এম

হুম, আপনি কি ফিডল আপডেট করেছেন? এটি বর্তমানে আমার মূল কোডটির একটি অনুলিপি দেখাচ্ছে।
পিসিওহেলো

0

এই প্রশ্নের উত্তরের মধ্যে সবচেয়ে সহজ সমাধানটি না দেখে আমি খুব অবাক হয়েছি। আপনি যা করতে চান তা হল ngInitআপনার পুনরাবৃত্ত উপাদানগুলির ( ngRepeatনির্দেশাবলীর সাথে উপাদানটি ) যাচাইয়ের জন্য একটি নির্দেশ যুক্ত করুন ( স্কোপটিতে $lastএকটি বিশেষ পরিবর্তনশীল সেট ngRepeatযা নির্দেশ করে যে পুনরাবৃত্তি উপাদান তালিকার সর্বশেষতম)। যদি $lastসত্য হয়, আমরা শেষ উপাদানটি রেন্ডার করছি এবং আমরা আমাদের ফাংশনটি কল করতে পারি।

ng-init="$last && test()"

আপনার এইচটিএমএল মার্কআপের জন্য সম্পূর্ণ কোডটি হ'ল:

<div ng-app="testApp" ng-controller="myC">
    <p ng-repeat="t in ta" ng-init="$last && test()">{{t}}</p>
</div>

অ্যাঙ্গুলার.জেএস সরবরাহ করে testযেহেতু আপনি কল করতে চান তার স্কোপ ফাংশন (এই ক্ষেত্রে, ) ছাড়াও আপনার অ্যাপ্লিকেশনটিতে কোনও অতিরিক্ত জেএস কোডের ngInitদরকার নেই। testসুযোগটি কেবল আপনার ফাংশনটিতে রয়েছে তা নিশ্চিত করুন যাতে এটি টেমপ্লেট থেকে অ্যাক্সেস করা যায়:

$scope.test = function test() {
    console.log("test executed");
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.