how to resolve $rootScope:infdig - angularjs

A sample application is at: http://jsfiddle.net/qvkyrg7k/8/, why do I get $rootScope:infdig?
Basically I'm trying to take an array of items, filter by a search term and then group based on a property so I can show in a tree like way.
The problematic line is
<tr ng-repeat-start="(family, members) in data | filter:search | groupByFamily">

See https://github.com/angular/angular.js/issues/10738 for the reason and track. Basically, ngRepeat watches the result of the pipe using $watchCollection. Since groupByFamily creates new arrays each time it is run, angular thinks the whole model changed and triggers a rerender which then makes groupByFamily run again, etc.

Related

Angularjs ng-repeat with datepicker filter executing many times

I finally managed to make my date filters work but now I have a problem...
I have a simple table that is supposed to be filtered by name and date. It is filtering correctly but the date filter is extremely slow. After debugging and researching I know the problem is that my custom dates range (from - to) filter is called many times.
It is called:
When I click on the calendar to choose a date but before actually showing the calendar control
Right after showing the calendar
After selecting a date
It even runs the first 2 times if I don't chose any date after opening the calendar. It also runs every time I change from one month to another.
Also, for each of those 3 times the filter is called, it also runs once for each row I have in the list. In the example I have only 10 rows (it is actually fast) but in my application it has maaany more and it takes a long time.
The ideal is that this code gets called only once after selecting a date.
For simplicity I'm showing here a few lines of my code, but you can see the whole example on this Plunker :
HTML Code:
<!-- This is the datepicker control filter -->
<ng-datepicker ng-model="DateRegisteredFrom" view-format="MM/DD/YYYY" placeholder="mm/dd/yyyy"></ng-datepicker>
<!-- Here is the table -->
<tr ng-repeat="item in model.People | filter:searchText | dateRegisteredFilter:DateRegisteredFrom:DateRegisteredTo | orderBy:orderByField:reverseSort ">
</tr>
And this is my javascript code:
.filter("dateRegisteredFilter", function($filter, $rootScope) {
return function(items, from, to) {
return $filter('filter')(items, "DateRegistered", function(v) {
// Filter code...
});
};
});
After a lot of reading and researching I learned 2 things:
My code gets called this many times due to "dirty checking" done by angular (I read it thanks to this answer: ng-repeat execute many times )
The actual comparision of dates is slow (this would be ok if the code was executed only once but not many)
I would appreciate a lot if somebody could point me on the right direction or if I'm doing something wrong as I've been reading and I only find the reason why this happens but I can't find a solution or a suggestion.
I think you need to change how you are handling your ng-repeat. As you are aware, these filters is constantly running, and because of that it is causing things to run slowly. What you should do is transform the data in model.People separately in your controller instead of piping it through multiple filters. Doing it that way, you can better control when and where the filtering gets done.
This link has a cool gif that helps to show why things are so slow as well https://medium.com/#lightingbeetle/some-best-practices-when-building-a-large-angular-application-c346734a4e9c

Need a way to query MongoDB collection, and ngRepeat beginning at specific point

is there a way to query a MongoDB collection, return the results, use an AngularJS ng-repeat to iterate through the results, BUT BEGIN the iteration at a specific position in the results, somewhere in the middle for example?
I am currently returning a query to an angular view; a category of materials. Then i have my view set up to paginate(ng-repeat) through the results. However, no matter what material I click on to bring me into the view (from a different view), the ng-repeat always starts at the beginning of the materials list, rather than on the material i clicked. Any thoughts?
I do not believe the mongodb part has anything to do with the actual question but ill give you 2 options:
The quick way:
<div ng-repeat="item in items" ng-show="$index >= myCtrl.startPoint">
While this will do the trick you might want to do the ng-show expression with a filter. In addition you might run into performance issues. You should also use the track by feature.
A better way would be to have a filteredData model for the ng-repeat and do the correct filtering once per action. You would have to make sure yourself that the model is updated on every data change and every user input.
There are many choices in between these 2 options but what to use depends on your needs. For instance - does user input change frequently? Is your data updated frequently? etc.

Date greater than the current time in Angular

I'm having to move a project from rails to angular, Much to my disgrace.
How would i go about changing this
<% if souporder.datefrom > Time.now %>
into angular terms?
heres my code
<tr ng-repeat="soup in souporders">
<td>{{soup.id}}</td>
<td>{{soup.soup}}</td>
<td>{{soup.datefrom}}</td>
<td>{{soup.dateto}}</td>
</tr>
Edit
This is in my view
<div ng-repeat="soup in souporders">
<tr ng-if="date == soup.datefrom">
And this is in my controller
$scope.date = new Date();
You clarified in the comments that you only need this calculated on page refresh, which is pretty simple and doesn't require much angular magic.
On initializing the view's controller, simply set a variable whether or not the order date is in the future or not and expose it on the scope. Then bind directly to it, and use the bindonce functionality {{:myvariable}} to improve performance because it won't be changing anyway.
Depending on your needs, you can even have the server calculate that value and return it as a property of each order in your JSON array of orders. (I'm guessing a bit at what you're doing, but you can adapt the answer if you're not doing what I think you're doing.)
Update: I just realized that perhaps your objective is not to do something special with each row based on the date, but to filter the list based on the date. Most of my answer still stands, but you can forget about the bindonce spiel. What you need is called an angular filter on ng-repeat and is done using the pipe (|) character. You can read all about it by searching the topic. There are examples in the angularjs docs. But basically, instead of:
<tr ng-repeat="soup in souporders">
<td>{{soup.id}}</td>
<td>{{soup.soup}}</td>
<td>{{soup.datefrom}}</td>
<td>{{soup.dateto}}</td>
<td>{{:soup.active}}</> <!--do something nice here instead of just displaying true/false --->
</tr>
You might want:
<tr ng-repeat="soup in souporders | filter:{current: true}">
...etc
</tr>
Either on the server or on the client, when you load the data from the server, you can add a boolean property to the items in the souporders array that says whether it's current or not.
What this accomplishes:
You have a filtered list of items based on a property that does NOT change dynamically and requires a page reload to update. But it's fast (but not the fastest it could be if you really need to go crazy - don't prematurely optimize, you'll know what to do when the time comes to do it, and that time isn't now).
Why it's not that great:
It isn't dynamic and it isn't angular-y or declarative. But based on your requirements, it's a solution. I'd spend some time considering if you want this to be dynamic, and if not, why the items that aren't showing are even returned from the server. Are they used elsewhere on the page?

How to correlate view order (using orderBy) with model order

I have an obviously simple but yet challenging problem in my AngularJS app:
When using orderBy on the view I loose the correlation between the order of the related model and the view order.
What I want to do: My view is a table. I want to set a highlight class for the selected row and I want to use cursor up/down keys to move this highlight.
I tried to decouple $index from the tracking by using track by currentDocument.objectid, but that doesn't seem to work.
How can I correlate the currently highlighted row in the view with the currently highlighted element of the related model in a way that I can use -1 or +1 with the cursor keys?
As the order of the data is important to your business process, it makes sense to make the order part of your actual model. If you read the last example of orderby on the angular api (https://docs.angularjs.org/api/ng/filter/orderBy), you will notice they achieve this very thing. Basically, instead of binding orderby directly to some model value, create your own order function and manually order your model.
I have created a plunker here: http://plnkr.co/edit/oM97ZTAIHTjI6YFiGDwI?p=preview Just click on the persons name to see the example (and reorder the list and try again).
Basically you create a manual sort function like this:
$scope.order = function(predicate, reverse) {
$scope.friends = orderBy($scope.friends, predicate, reverse);
};
Which actually reorders the model itself, rather than just the view array. You can call it however you like. Then you can simply pass $index from the view, and it will correlate correctly to the view order.

Ember Sort Models

Hi I have an array of models(images) which is the model property of an ArrayController.
I want to be able to sort the the array such that when click on the image it automatically moves itself to a predefined index. (E.g. when I click an item on the array it should move to middle of the array) and the UI is updated to reflect the new position.
Here is an example of what I am trying to achieve:
http://emberjs.jsbin.com/vesakafaca/1/edit?html,js,output
Also since ArrayController is/will be depreciated from v1.13 is there alternative way of doing this without using ArrayController ?
Here's an updated emberjsbin of what I think you're looking for -
http://emberjs.jsbin.com/qujihihivi/edit?html,js,output
ArrayController vs. ObjectController doesn't matter anymore, you can just use Ember.Controller.extend as you'll see in the updated bin.
I did the sorting in the model by creating an Ember Array for the model and tagging the .sortBy("index") onto it.
I also updated the templates with new syntax, you don't need {{bind-attr}} anymore and you should use {{#each itemCollection as |item|}} instead of just {{#each itemCollection}}, makes for easier to read templates and provides consistent scope.
Hope the updated bin is helpful!

Resources