I am using ng-repeat for my Model render in angular ,
if i Push new Element in Model/Collection , Will angular only deal with Update or Loop through the whole model again.??
My experience (using Angular version 1.2.7) is such that whenever something changed in collection, the whole ng-repeat was repainted.
You can easily test it by modifying the generated DOM. For example by adding custom attributes like my-id="1", my-id="2" etc to the DOM elements.
Then change something in your collection, which will trigger new $digest cycle.
Angular will pick up the change and redraw the DOM -> your custom attributes will disappear.
So I would argue that Angular loops through the whole model.
In the documentation it is clearly mentioned:
ngRepeat
ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope
Angular associates an identity to every item, the elements already associated to the existing items of the collection won't be recreated.
Hence, only elements that are removed from the collection, are removed from the DOM. Furthermore, if an item moves within the collection it doesn’t need a new scope, but it needs to be moved in the DOM.
Related
I have a ng-repeat that lists users and uses a custom directive.
My custom directive has some event handlers like click handlers etc.
This vm.UserList will get modified every time a drop down list value is changed in the UI. My question is, will the event handlers get updated whenever the UserList is modified by a drop down list?
The dropdownlist on change will make a remote API request and then set the JSON result of the users to the value of the UserList.
<div ng-repeat="u in vm.UserList">
<div><div my-custom-directive user-id="{u.id}" >{u.email}</div></div>
</div>
</div>
Yes, it does. According to angularjs documentation:
ngRepeat uses $watchCollection to detect changes in the collection.
When a change happens, ngRepeat then makes the corresponding changes
to the DOM:
1- When an item is added, a new instance of the template is added to the
DOM.
2-When an item is removed, its template instance is removed from
the DOM.
3-When items are reordered, their respective templates are
reordered in the DOM.
So your directive gets added to the page including all of the event handlers.
I have a dropdown element directive to display in essence a styled dropdown list with additional capabilities.
My dropdown controller has a function called openDropdownItems that get's executed when the list should be displayed.
Then I also have another attribute directive called setInViewWhen that provides an expression when the element should be scrolled into view when condition is true.
<x set-in-view-when="something.item === selectedItem">
This is just an example of some X element with my attribute directive applied.
The thing is that I would like my dropdown list items (LIs namely) to have this directive on them so when user navigates over them using a keyboard, it would automatically scroll them in view when they get passed visible viewport. Whether these items are displayed within a scrollable container or as a whole in a list longer than the browser viewport isn't really relevant.
The main idea is for scrolling to follow dropdown list selection. Whether that should scroll the main window.
The problem
I can make my setInViewWhen directive completely independent but that means that I would have to search for the closest scrollable container whenever condition changes on an item. This seems to be quite a bit of processing that I would like to avoid to repeat (I need to traverse the DOM upwards, checking each node's calculated stylesheet property OverflowY + some additional checks.
This basically seems redundant because whenever I get the closest scrolling ancestor all sibling elements with the same directive could reuse the calculation result.
Question 1
How can I share this knowledge between sibling directives? If I was to fire an event I could not know whether receivers are siblings or not without any additional processing.
Question 2
Instead of checking for scrollable container every time when my directive's condition becomes true I could theoretically change dropdown parent's openDropdownItems to first complete it's original execution an then also execute the scrollability check and use the result of it along with my directive's condition.
I can gain access to dropdown's controller in my directive via directive requires property and adjust it in the post-link phase.
But this also means that I couldn't use my directive outside of dropdown which I would like to as it is a generally usable directive I could attach on several elements in my app to scroll elements into view under certain conditions.
What would you suggest how to do this?
To sum up the comments:
For Question 1, there is no direct way for sibling directives to communicate with each other. I usually create a wrapper parent directive to relay the messages.
As per the comments, the condition something.item === selectedItem introduces a watch; for a list of select items this can be many watches and detrimental to performance. Even if it doesn't occur now, it is a trap and someone may be lured into using this for a long list of items in the future. I would rather have a single watch in the parent of the <li>s and add the "scroll into view" logic there.
With the previous point in mind, you could still have a standalone setInViewWhen directive (seems useful) and have the controller of your X-select replacement directive share code with it. E.g.:
a service containing the common functionality,
an angular value containing a base class (Typescript or JS) and have the controller of both the setInViewWhen and X directives extend from it
or any other solution that is convenient for your case
Related to Question 2: The children could require their parent and change a method in it (this technique is even endorsed by Angular, see ng-model and custom controls where they override ngModel.$render() by replacing it). If you do that however, there would be many children changing the method of the parent, which could lead to a mess.
By referring the this link; I am reloading the handlebars partial template from marionette view within my application.
In my marionette view I have defined ui object as below
ui: {
updateCarrierRow: ".hook--edit-carrier-row",
dispatchEquipment: ".hook--dispatch-equipment",
editButton: ".hook--edit-button",
trailerText: "#trailer-text",
tractorText: "#tractor-text"
},
From which trailerText & tractorText variables are referencing the elements from handlebars template loaded within current view's html template using Handlebars expression
{{> dispatchedEquipement}}
application user will be editing some fields from section rendered with this partial template so on changes submitted to server I need to reload this partial template with modified values from parent model.
So by referring link mentioned above I have reloaded partial template on the parent view using following code segment
this.ui.dispatchEquipment.empty().html(Handlebars.compileClean(dispatchEquipmentSectionPartial)({
"dispatchInformation": that.model.get("dispatchInformation"), "displayText": localizedText.displayText
}));
With this code I have successfully reloaded the partial view on my parent view but on subsequent edit operations when I try to access values of input elements within partial template or trying to change / add css classes it wont work with following code statment
this.ui.trailerText.val();
or
this.ui.tractorText.val();
It gives me empty value though text boxes contains proper values. and same happens with adding or removing css class of these elements with the help of this.ui object of parent view for example
this.ui.tractorText.addClass("hidden")
wont add hidden css class to element.
As of now I have managed to get things working with the help of jQuery id selector for those elements. But I would like to know how should I resolve this issue?
Thanks in Advance.
I reckon it is because the ui elements are bound when the view is initialized but during the life cycle of the view you empty and replace the html thereby no longer having your ui elements bound to what is now on screen.
you could try calling this.bindUIElements() afterwards but not fully sure as i've never had to use it like that.
I've seen a lot of questions about the order in which ng-repeat finishes compared to other directives or things going on in Angular land, but I haven't been able to find an answer to how exactly it accomplishes this.
I have two ideas of how it could work.
First Way:
When ng-repeat's watcher triggers, it removes all elements it created from the DOM then creates all new elements in their place, even if many of those elements are the same (e.g. in the case of 1 item added to the backing array).
Second way: Since ng-repeat already keeps track of which elements go with which items in its backing collection, it simply removes the items that no longer exist in the collection and creates new elements for items that are new to the collection.
Which is it and why?
It's the second way: Angular tries to be smart about creating/removing DOM elements:
The ngRepeat directive provides a way to render a collection of items given a template. To do this, AngularJS compiles the given template and then clones it for each unique item in the collection. As the collection is mutated by the Controller, AngularJS adds, removes, and updates the relevant DOM elements as needed.
But, how does AngularJS know which actions to perform when? If you start to test the rendering, you'll discover that AngularJS doesn't brute force DOM creation; that is, it doesn't recreate the DOM for every rendering. Instead, it only creates a new DOM element when a completely new item has been introduced to the collection. If an existing item has been updated, AngularJS merely updates the relevant DOM properties rather than creating a new DOM node.
This can still impact performance unnecessarily, i.e. when passing elements by-value in a collection (there's an excellent example of this in the blog post linked above). That's why Angular supports "track by" for ngRepeat since version 1.2: It's a way to help Angular decide when DOM creation is necessary:
With this association in place, AngularJS will not $destroy and re-create DOM nodes unnecessarily. This can have a huge performance and user experience benefit.
The official documentation states:
You can also provide an optional tracking function which can be used to associate the objects in the collection with the DOM elements. If no tracking function is specified the ng-repeat associates elements by identity in the collection.
For example: item in items track by item.id is a typical pattern when the items come from the database. In this case the object identity does not matter. Two objects are considered equivalent as long as their id property is same.
I bind the collection to a DOM element '#collection_view'.
When collection fetching data and update the view, the view changed to be EMPTY for a second and then filled with correct HTML.
In the collection there are addAll, addOne methods to append list element to the list.
How can I skip the empty, replace old HTML with new HTML directly?
This is how fetch() works.. it will empty the collection and addAll elements at once. I suppose you have a this.$el.empty() call (or something like) in your addAll event binding function. And what is happening is that the $el DOM element will be cleaned and after that repopulated.
If you want to refresh a collection instead of reset it you should check the answers here.
Looks like someone proposed to add this soft update to the Backbone Collection but was denied by Backbone maintainers.