How to sort an angularFireCollection? - angularjs

I'm having trouble sorting larger Arrays with an angularFireCollection binding:
$scope.offers = angularFireCollection(new Firebase(url));
while having in my template the code:
<tr ng-repeat="offer in offers | limitTo:100 | orderBy:'createdTimestamp':true">
While the offers.length is < 100, new items are correctly displayed on top. After 100 items, the sorting stops working at all.

The issue is your expression order. "offer in offers | limitTo:100 | orderBy:'createdTimestamp':true" first gets the first 100 elements of offers, then orders. What you want is to order, then limit, so you want to use the string "offer in offers | orderBy:'createdTimestamp':true | limitTo:100". You can see what I mean in the following jsFiddle, where the first list limits the array and then tries ordering while the second orders, then limits: http://jsfiddle.net/qE5P9/1/.

Related

Weighted sorting of results from Angular's built-in filter

What is the most efficient way to do a weighted sort of the results of Angular's built-in filter?
One of the first lessons we encounter when learning Angular is to filter an ng-repeat list based on the contents of a text box:
<input type="text" ng-model="textFilter" />
<ul>
<li ng-repeat="band in favoriteBands | filter:textFilter">
{{ band.name }} ({{ band.singer }}, {{ band.guitar }}, {{ band.etc }})
<li>
</ul>
In this case, entering "Hendrix" in the text box could match on Jimi Hendrix for the guitar field and on The Jimi Hendrix Experience for the band name. In this case, I want all of the matches on band.name to sort to the top. Matches on band.guitar should come next, but matches on both should go to the very top.
What's the best way to set up an orderBy to do this? Should I write a function to inspect each field in each record to look for a match and assign a weighted relevance score? That seems like I'm doing the filter work all over again, but I haven't found a better approach. Are there better ways to do it?
EDIT: To clarify, this question is not about whether or not to show a result conditionally, but rather about sorting the items that do pass the filter. I want to give fields in each object different weights. Let's say band.name has a weight of 5, the band.guitarist has a weight of 2, and the other fields have a weight of 1. If I search for "Tom Petty", I'd get a total of 8 for Tom Petty and the Heartbreakers - 5 for band.name, 1 for Tom Petty in band.singer, and 2 for Tom Petty in band.guitar. For the Travelling Wilburies, I'd only get 3 (1 for band.singer and 2 for band.guitar). I can then use those scores to determine the sorting. There are any number of sorting algorithms you could use - this is just an example.

AngularJS ng-repeat long array, sorted by date but limiting to only 10 rows

I've got a long array with over 100 objects. Each object has a member.date, but some are null, and some have a date string.
<tr ng-repeat="(k, member) in provider.allMembers | orderBy:'ended':true" ng-if="member.ended">
my problem is I only want to show the latest 10, but when I add limitTo:10 I get zero results (because the first entry that is displayed is the 42nd object in the array.
You can make your own filter which check if the property is null or not and returns what you want :)
eg. https://scotch.io/tutorials/building-custom-angularjs-filters
I ended up resorting the array by end date, then simply limiting the ng-repeat to 10.
// sort all members by ended date
$rootScope.provider.allMembers.sort(function(a,b){
return new Date(b.ended) - new Date(a.ended);
});
then using this ng-repeat:
<tr ng-repeat="(k, member) in provider.allMembers | limitTo:10">

Angular4 - Show next 5 items in array through click

I have an array filled with TV shows. TV shows which I have watched recently:
tvShows = ['Firefly', 'Fargo', 'Game of Thrones', 'Breaking Bad', 'The Sopranos', 'House of Cards', 'Prison Break'];
As you can see, the array contains more than 5 items. So I did this to show only 5 tv shows:
<div class="tvShow_row" *ngFor="let tvShowName of (tvShows | slice:tvShows.length - 5)">
<span class="tvShow_name">{{tvShowName}}</span>
</div>
Now, I want to have a simple button for displaying the next 5 items in the array together with my current 5 items. Obviously, the list gets longer based on the items in the array, but the principle is to show the next 5. The problem is, I don't know how to load the next 5 items from my array. Also, if I don't have 5 (because there are 7 items in the array for example), then I still want to show the rest of the array. I tried showing more items by printing the array, but then it only shows the first 5 items again, which makes sense, I guess. Does anybody know how to tackle this problem?
Maybe something like this:
*ngFor="let item of items | slice:0:[max]"
toggle(): void {
this.max = this.max + 5;
}
So setting max will show more items in the array. Without copying array and other stuff.

Angular Counter with limitTo and filter Issues

I am wanting to put a counter at the bottom of a table that displays something along the lines of "Displaying 100 of 250 names." Currently, I have a search bar and automatically limit what is displayed to 100 when the page loads.
Here is the code that I have right now.
<tr ng-repeat="names in pool" |filter:searchNames | limitTo:100"
...
<p> Displaying {{(pool|filter:searchNames).length}} of {{pool.lenght}} names. </p>
Instead of getting "Displaying 100 of 250 names." I am getting "Displaying 250 of 250 names.". I am guessing that this is due to the fact that I am using filter:searchNames to get the first piece of data.
Can anyone help me out on how to display the number that I am using in the limitTo in case the query is larger than what is being displayed?
Thanks!
I was able to figure this one out pretty quickly. All I needed to do was put in the limitTo:100 like below:
<tr ng-repeat="names in pool" |filter:searchNames | limitTo:100"
...
<p> Displaying {{(pool|filter:searchNames|limitTo:100).length}} of {{pool.lenght}} names. </p>

Angular JS ng-repeat consumes more browser memory

I have the following code
<table>
<thead><td>Id</td><td>Name</td><td>Ratings</td></thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td><div ng-repeat="item in items">{{item.rating}}</div></td>
</tr>
</tbody>
</table>
users is an array of user objects with only id and name. number of user objects in array - 150
items is an array of item objects with only id and rating. number of item objects in array - 150
When i render this in browser, it takes about 250MB of heap memory when i tried profiling in my chrome - v23.0.1271.95.
I am using AngularJS v1.0.3.
Is there an issue with angular or am i doing anything wrong here?
Here is the JS fiddle
http://jsfiddle.net/JSWorld/WqSGR/5/
Well it's not the ng-repeat per se. I think it's the fact that you are adding bindings with the {{item.rating}}.
All those bindings register watches on the scope so:
150 * 2 = 300(for the 2 user infos)
150 * 150 = 22500(for the rating info)
Total of 22800 watch functions + 22800 dom elements.
That would push the memory to a conceivable value of 250MB
From Databinding in angularjs
You can't really show more than about 2000 pieces of information to a
human on a single page. Anything more than that is really bad UI, and
humans can't process this anyway.
I want to say the leak is in the second array because you are potentially looping through the same array and displaying every item for every user row in users so depending on how large your test data is that view could get rather large. I could do a little more investigating. btw your fiddle is something entirely different.
Right now you are looping through 150 X 150 = 22500 items. And registering a watch (or through a directive just adding item rating) to each one.
Instead - consider adding the user's rating to the user object itself. It will increase the size of each user object but you will only loop through 150 items and register watches only on them.
Also - consider looking into Indexes. It's apparent that there could be similar users or item ratings. Just index them, so instead of looping through heavy objects, you can reduce them.
One more thing - if you are going to be running the directive the same instance, at least change the code:
var text = myTemplate.replace("{{rating}}",myItem.rating);
to a concat style string calculation:
var text = '<div>' + myItem.rating + '</div>';
This will save you a HUGE chunk on calculation. I've made a JSperf for this case, notice the difference, it's about 99% faster ;-)

Resources