Call a javascript function when ng-repeat finishes - javascript

This is my first attempt at Angular so please bear with me.
I have a jQuery slider that usually is initialized at document ready. However this doesn't work when the images are being populated by angular since it will not have finished rendering the DOM.
Is there a way to detect when ng-repeat finished rendering the DOM so that I can call my function?
Thanks in advance

Check out this answer Stack Overflow. Setting a timeout is never a good way to wait til something finishes. I would suggest a custom directive checking to see if you are scope.$last, then you can broadcast an event to the controller.

Related

Angular and calling a JS event only once after document.ready()?

One of the things I'm still hung up on with Angular is understanding all of the lifecycle hooks and when to use what when.
I often need to stick a little plain'ol JS into a component to deal with some DOM issue that, alas, I can't handle via Angular (usually because I'm working within a component that needs to access some elements from a parent component's rendered DOM that we have no access to the Angular code for...I do realize this isn't the 'proper' Angular way but...).
An example right now is a few pages I'm working on that use a component need that needs to hide a DOM element on the page that isn't a part of this component. I need to use JS for this (a whole other story why CSS isn't the solution for this one).
But I only want to do this once the DOM is fully rendered.
Sometimes this seems to work when inserted into ngAfterViewInit -- but sometimes not. It seems that there's no guarantee the full DOM is ready using that lifecycle.
Moving that logic into ngAfterViewChecked does work. However, the issue with ngAfterViewChecked is that it's getting called dozens of times on some pages--and the first few times it's called, the DOM isn't even ready. Not the end of the world, but there's no reason for me to be attempting to grab the same DOM object 40 times per page render. I somewhat remedy this by adding a boolean flag to tell this bit of JS to stop running once it finds the DOM elements but that's hacky.
So my question is: What is the proper (if there is one) way to handle JS manipulation of the DOM after the DOM is fully rendered in the context of an Angular component? Is it to use one of the angular lifecycle events? Something else? Or this whole idea of manipulating DOM objets outside of the component I'm working in just anathema to the 'Angular way' so just isn't something accommodated within Angular?

How to define finish render ngRepeat and run callback?

Good day! However I have read on stackoverflow a lot of article but nothing found. I have a little question about ngRepeat I mean about its execution. I want to be notifeid when ngRepeat iterate has finished. How to define finish render ngRepeat and run callback? And don't propose me to use $timeout.
This question seems close to this one : Angular JS callback after ng-repeat is completed dynamically where Shohel answered to use a $broadcast on a custom directive so you can catch an event on your controller. Hope it will helped you.

js newbie trying to understand the dom via ajax confusion (conceptual)

I'm a python/data guy mucking around in a little bit of web, fundamentally clueless about JS and the dom and all that. So something really weird happened, and even though I found a solution, I'm trying to understand the mechanics of why.
The scene: a common problem
There's like a million prior SO questions that go along the lines of "I had some working jQuery/JS that manipulated some HTML. Then instead of hard-coding the relevant HTML, I created it programatically somewhere else and dragged it in via AJAX. Suddenly everything broke."
Invariably, the answer goes like this: "you can't do that. Hook up your code to something higher up the parent-child chain, event delegation is a magic thing and it will save you."
So that happened to me, I spent an hour or so reading prior SOs and learning about event delegation, and, indeed, it saved me, and my code worked.
But I don't understand why it was broken in the first place. So I'm hoping someone can explain the underlying theory to me, and that way I'll come to a deeper understanding of the whole dom bit.
Some Broken Code
$(document).ready(function(){
$("#autopubs").load("pubslist.html");
// Obviously, the stuff in pubslist.html is what the next line was
// supposed to work on
$('.collapse').on('show.bs.collapse', function () {
$('.collapse.in').collapse('hide');
});
});
probably no surprises about the solution. wrap autopubs in an outer div and hook the collapse thing to that. Done, worked, deeply dissatisfied.
But I still don't understand why this is necessary. Here's my mental model of what the broken code should have been doing.
Ok, the document's finished loading! Let's execute our code!
First line: let's go get this file and add it to the DOM. This is out there in the world as state, and now we're going to mutate it, as one does with state.
Ok, now it's part of the DOM. We're done with that line. Let's go to the next line.
Ok, now we want to hook up a bunch of event listeners to everything with the class collapse. Cool, let's look up the present state of the DOM. Hey look, there's all this stuff in there with the collapse class. Bam. Hooked up.
Except 4 never happened, because the second line of code apparently couldn't see all the stuff that the first line added.
Intuitively, there are two plausible reasons for this:
The second line executed before the first one finished getting the file in. If that's true, then I've learned something important about javascript (or jquery, or ajax, or something): lines don't always execute in order, or at least don't always finish before the next one starts.
The first line didn't actually mutate any state at all. The DOM isn't state. It's something else, something... maybe even immutable? The first line tinkered around with something else entirely, something the second line couldn't touch because it was trying to tinker with the DOM.
Honestly, both of those possibilities seem kind of bizarre to me. But there's obviously something I fundamentally don't understand going on under the hood here. Can someone help me?
JavaScript makes heavy use of asynchronous behaviour. Lines execute in order within the same function, but that doesn't mean they "complete" their action.
If you look at the documentation for load (http://api.jquery.com/load/) you can see that it takes an optional complete parameter. That is a callback. It is a function that will be ran when the operation completes. Calling load itself only makes the loading start, sort of "in the background".
So you could do this:
$(document).ready(function(){
$("#autopubs").load("pubslist.html", function() {
$('.collapse').on('show.bs.collapse', function () {
$('.collapse.in').collapse('hide');
});
});
});
The callback to load is ran after it completes.
Why your original hack-solution actually works? It attaches the event handler to an existing outer div, and the loaded HTML is put inside later, when it loads.
Your first assumption is correct. When calling ajax its asynchronous which means it will run a callback when complete. In this case jquery already calls a callback to append the content to the div but that happens after a delay of maybe 100 to 200ms or so.
The other code however can be run instantly but doesn't find anything because the async call isnt finished yet.
In jQuery if you check the documentation for load you will see something along the lines of (arg1, [arg2], [complete]) where complete will be a closure or callback function.
If you wrap the code within that callback function instead of below it than that code will be executed after the content is loaded and added.
EDIT
The load load is also one of the convenience / shorthand method in jquery. For more documentation check Ajax
Furthermore the DOM is very mutable and your first line that does the request does perform an operation on the dom. Due to the fact that it is async it will tell the script to continue running while it waits for its data which is why it fails.
Hope this helps abit! You've got the right picture :)
If I correctly understand, you should refer the new loaded DOM only on a 'complete' callback. Take a look here:
http://api.jquery.com/load/
$( "#result" ).load( "ajax/test.html", function() {
alert( "Load was performed." );
});
The first line did get executed, it changed the state. After that, you hooked an event to the change and that will get called everytime there is a change, but not for the first one since it has already happened before the event was hooked up.
jQuery.load is asynchronous, meaning you need to provide any code to execute after the content is added via callback. You have a working example at the bottom of the documentation page.
As others stated JavaScript wont wait with finishing the load function before executing the following lines. Therefore, you can use the callback parameter of the load function (or any other function that takes time, like AJAX or animations). So your first assumption is correct. AJAX (asynchronous JS) is a huge thing, you should dig into it :)
Additionally, you still can use delegation in your case:
$(document).on('show.bs.collapse', '.collapse', function () {
var collapse = $(this); // the individual $('.collapse') element triggering the event
});
This will bind the show.bs.collapse event to all .collapse elements, regardless if they exist now or are added later. Here the original selector is the $(document). In simple words:
"Hey document, whenever the event show.bs.collapse gets fired on an element with the selector '.collapse', run the following code."
You distinguish between direct and indirect event delegation or binding. In your example, you use direct binding, since you are ultimately selecting all now-existing .collapse elements and then do stuff with them:
$('.collapse').on()
You have to understand that this works only for currently existing elements that get selected when running that line. This wont apply for dynamically added elements of the same class.

Polymer 1.0 Lifecycle Event for Complete Rendered DOM

Background
I've been working with Polymer for a while. I've been converting from .5 and building new elements for a production app. We are currently using Polymer 1.0.6, and this particular issue is also using jQuery 2.x.x and typeahead.js.
Issue
We have an element that builds a dynamic list of label and inputs provided by a data source. in the ready function we get a list of input data, and set that to a local list variable that is bound to a foreach template to create the labels and inputs.
I was unable to find a Polymer element I really liked for typeahead, for Polymer 1.0. So I defaulted to using typeahead.js. my problem is that I cannot find a lifecycle event or workaround, to call the typeahead function after the dom has processed setting the bound list in the ready function.
Code
The easiest way to demonstrate this issue, was to create a HEAVILY trimmed down version in a jsbin. I know the element looks bad, it was cut down as much as possible to demo the core issue I'm facing.
http://jsbin.com/zivano/edit?html,output
What Have I Tried?
I've tried using the attached event, and while it does process after the ready function, the dom changes from ready have not taken effect. I found similar issues on SO domReady vs ready - Migrating to Polymer 1.0 I've tried both suggestions, the second is still being used in the jsbin, without success.
I have also bound the click event of my inputs to a function calling the typeahead setup code, to prove that if the calls are made after the dom is rendered it will work correctly.
Summary
If update a data bound, local variable in the ready function, is there a lifecycle event I can call that will guarantee that those dom changes will be rendered, so I can make a dom query against those new items? Or is there a work around that will let me call a js function on a dom element, one time after the element dom fully renders?
my problem is that I cannot find a lifecycle event or workaround, to
call the typeahead function after the dom has processed setting the
bound list in the ready function.
I think I had a problem like this . For my problem I found a solution using the following :
var self = this;
window.addEventListener('WebComponentsReady', function(e) {
// imports are loaded and elements have been registered
/*Example*/
console.log('Components are ready');
var p = self.getElementsByTagName("paper-item");//paper-item created dynamically
console.log(p);//can access and use this paper-item
/*Finish example*/
//here you can call typeahead because the dom has been processed
});
Sorry for my English or if I dont understand your question, my English is bad.
The Issue I had was that the data-bound list was populated through an ajax function, which was completed after the attached function, even if I made an async call inside of the attached function, it would still fail because of race conditions.
It's worth noting the answer by Flavio Ochoa, will work. I personally preffered to not have my custom elements add listeners to the Window. So i went a different route.
Since my issues we're predicated on guaranteeing that the bound list was updated, I wrapped the ajax call in a Promise, and added the typeahead init logic to the then statement. That solution appears to be working.
I do have some concerns whether the promise can guarantee that the bound list will have propagated to the DOM by the time the then statement is processed. But so far it has worked consistently. I'll edit this answer if I can prove otherwise.

ng-repeat loop more than the number of elements

I am trying to implement ng-repeat with ng-style. It works perfectly. However, I do not understand why my method is called more than the number of elements in my ng-repeat!
Do you know why?
To complete my explanation, I created a : JSFiddle
I think that when your html code is compiled, it executes ng-style directive even if there is no data (when items == null). After that your controller changes $scope.items, forcing other calls to $scope.getStyle().
I think if you put a ng-if="items != null" within ng-repeat, your function will be called only 5 times.
Solution looks fine. The reason for multiple calls is angular digest loop. You can read about it here: http://www.benlesh.com/2013/08/angularjs-watch-digest-and-apply-oh-my.html
It works by running the loop and looking if the values changed for the watches. When they stabilize it ends. You can have multiple passes of the event loop in angular app and that is pretty normal. The limit is set afaik for 10 iterations. If bindings do not stabilize then exception is thrown.
Additional reading, highly recommended:
http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html
Part Keep Digesting While Dirty is the answer to your question i believe.
So it is by design.

Categories