I'm trying to create a custom AngularJS repeat directive, because ngRepeat doesn't fit my needs in this particular case, where I'm creating a sort of templating language based on angular directives, and I can't afford to freely create DOM elements.
In particular, these are the problems I have with ngRepeat and I'm trying to solve:
I need the repeater element to be the parent of the elements being repeated
the repeated elements have different templates, and I can't use wrapper elements or ngSwitches to provide different markup
I have created a custom directive that should:
empty the repeater DOM element
watch the current scope and at each call loop through an array, create a new DOM element per iteration, and compile the element with a childscope
keep the whole thing bind to the model
The problem with this approach is that I'm creating an infinite loop (I get the 10 $digest error) because I'm modifying the scope (creating the child scope) at each scope $watch fn call.
How can I approach this problem ? I tried looking at the ngRepeat source but that's pretty difficult to understand for me...
Sidequestion: A minor (for now) problem would be the performance of re-rendering the whole thing each time the scope is modified... How difficult would be to implement different actions for array's adding, removal and update of items ?
Related
The question I am asking is to figure out which is the best practice for communication from the child to the parent controller in a certain scenario. Say for example we have an element with a controller called "ListController", inside which we have a list of directives populated through ng-repeat. The directive is linked to a different controller called "ListItemController" and has an isolated scope, so can no directly use the functions of the parent ListController. In this case with my beginner knowledge of angular I've narrowed it down to 2 options that seem the most intuitive. The first option would be to $emit an event in the ListItemController and catch it $on the ListController, but events are commonly considered a poor choice for communication. The second possible option would be to access the parent ListController through the use of the $parent field of the ListItemController, but, assuming because of the fact that our list items are created through ng-repeat, we can only reach our ListController by using $scope.$parent.$parent, which doesn't really smell of good code either. I would like to know which option out of these two or others would be considered the best practice and why?
I have a situation in which I get data over a web socket, and performance is important. From the docs I understand that there are various ways of "pushing" the data I'm receiving to my Polymer elements, but I'm curious which one will be most efficient. So far I've created an element that can be included in a template, where the parent element will observe any changes in the data property and react accordingly. I've also been experimenting with using a Behavior to accomplish the same thing, though instead of needing to include a "data-element" in its template, it could just observe its own data property. I realize I could also use something like iron-signals to "push" the data via an event.
I'm not sure any of these methods are very efficient, since most of the time the changes to the "data" object will only apply to a small subset of all the observers. Another possible solution would be to "observe" a dynamic path, so like data.pathx instead of data.*, which would drastically reduce the number of times the observer callback gets fired, but I haven't come across anything that leads me to think that's possible, since each of my elements won't know if it should observe pathx or pathz until creation.
Like I said, performance is vital, and I feel there is way too much inefficiency if I have a small to medium sized dom-repeat of elements each observing a large data object of another element or individually holding a copy of that data on their own (like I assume a behavior would accomplish?).
I've looked at iron-meta, but I haven't been able to successfully data-bind to it, and from what I can tell from the docs, this data needs to be queried, whereas I need to be notified of changes.
Polymer doesn't really "observe" changes in elements. It just sets a setter for each property, and when it's called the UI is updated. So a dom-repeat template will not observe any change inside an object bound to it.
What could impact performance is unnecessary DOM manipulation, so if just a small subset of the data changes, re assigning all the array to the property is not ideal, and you should use notifyPath with just the sub property path and value that changed. Polymer will only update the DOM nodes affected.
If you have a way of knowing what sub properties changed in your data then you could obtain the object paths that have changed and call notifyPath for each of those and only a small number of DOM nodes will be changed.
Additional note:
If the number of elements in your array change, (added/removed) you should use the Polymer base array manipulation methods to update the property of your Polymer element, so it will change the DOM efficiently.
As outlined here:
http://docs.angularjs.org/guide/directive
Angular js directives take two different types of link functions:
Pre-linking function
Executed before the child elements are linked. Not safe to do DOM transformation since the compiler linking function will fail to locate the correct elements for linking.
Post-linking function
Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
Additionally, it appears that the default key of link will bind to postLink if given an anonymous function.
When and why would I ever want to use a pre link function?
The only time you'd want to use a pre link is when you need to perform some preparation on the scope before any child elements compile.
My team has used it when writing a grid directive to define the grid object on the scope and setup some of its properties that are needed before any of the child row and cell objects are compiled.
Hope that helps!
The problem. I have a table of entries($scope.entries), each row(ng-repeat) with 5 columns, 2 of those columns have custom made filter for various transformations.
Now in the same scope I have active_entry($scope.active_entry), which is changing every seconds, because of it and how angular works(I guess), the whole scope is being constantly checked and my filters executed.
This causes Watch Expressions in Batarang to go sky high over the time.
How can I use create some sort of isolated scope for the active_entry so my filters are not rended over and over again every second?
Is making a directive the only way to create an isolated scope? Would it work? What if I needed values from the isolated scope later on in the controller?
You are asking quite a few questions in your question. It would be better to ask them each in separate SO questions.
Anytime an Angular digest cycle runs, all watches and filters will run. Changing things "inside" of Angualar will cause a digest cycle to run. How are you changing $scope.active_entry? If you can change it outside of Angular, you might be able to do what you want. As you mentioned, you'll also have to put that property into a new child scope (scope: true) or isolate scope (scope: {...}), then you could call $scope.digest() (after you change active_entry) to only digest that scope.
Creating a directive would be the best way to create an isolate scope. You can create a child scope with ng-controller or a directive.
See also https://groups.google.com/d/topic/angular/1XtEAX93ces/discussion
What is the proper way in AngularJS to disconnect bindings?
I have a none-angular application which is loading a component that uses angularjs to do data-binding. At some point I want to destroy the component and want to be sure that there are no memory leaks. How do I tell angular to remove all event listeners from that part of the DOM?
Would $(node).remove() do the trick, or does angular do other things in memory that need to be cleaned up...? Any other tips on avoiding mem-leaks in angular would be appreciated.
Just removing a DOM element that has a Scope with something like remove() will not get rid of the Scope in memory. You can confirm this by removing an element and looking in Batarang, or by getting the parent scope and examining it's children. You'll see the scope is still there.... So you'll also want to call $destroy() on the Scope itself.
More information about $destroy() can be found here.
EDIT: One thing I'm not sure of, is if it deletes the scope entirely, or just disconnects it and lets JavaScript GC take care of the rest.