AngularJS orderBy function being ignored in ng-repeat - angularjs

The orderBy function is being completely ignored. I've added a console.log at one point for testing and the function isn't even being called. The data is still displayed but is unordered.
HTML Code
<div id="tabs">
<a ng-repeat="tab in tabs | orderBy:tabordinal" id="tab-{{tab.tab_id}}" class="{{getClasses(tab)}}" ng-click="tabclick(tab)">{{tab.label}}</a>
</div>
JS Function
$scope.tabordinal = function (tab) {
return $scope.taborder.indexOf(tab.tab_id);
};
Everything else is set up correctly, (i.e. ng-click's work properly, the data is bound properly, and filters are working on the other elements.

A few things...
orderBy takes a string, or an expression that returns a string. That string should be the name of a property you want to order by on the list of objects you're ordering. So if the objects in your array have properties like [ { 0: 'foo', 1: 'bar', '2': 'blah' } ], then you're good to go, I guess. But I doubt they're structured like that.
orderBy:tabOrdinal() if your expression is a function, as yours is in the original post, you need that ().
Outside of that, if you provide a fiddle, I can give you more help.

Related

Angular orderby sorting in loaded orders

By using ng-repeat i'm getting values but i need to sort it in specific order.
that is 'Portfolio', 'Sourcing', 'Delinquency'
can you please help me by providing sorting order.
HTML:
ng-repeat="row in DashboardFilter2" | orderBy: ['Portfolio', 'Sourcing', 'Delinquency']
js:
App.createList({
"qDef": {
"qFieldDefs": %Type
},
"qInitialDataFetch": [
{
"qHeight": 20,
"qWidth": 1
}
],
},function Show2(reply, app){
$scope.DashboardFilter2 = app.field("%Type").getData();
});
ng-repeat looks correct to me.
Would be good to see app.field("%Type").getData() result.
But to be honest, I would not use orderBy with strong reason since it is a filter and evaluates on every $digest/$apply.
Instead, I'd sort as I want in JS before putting it into template using Array.sort or similar method (lodash, underscore).
If the array changes after initialization then just make a function sortArrayYouWant and call every time it changes.
The global idea is NOT TO USE ORDERBY since it is extremely inefficient

Referring to outer scope variables in orderBy inside ng-repeat

I'd like to have an ng-repeat that's sorted by a value in some external lookup table.
For example, suppose I have a list of items with an itemType for each item. I have a separate itemTypePriorities lookup table in the scope, such that itemTypePriorities[someItemType] gives the priority for a given item type. Now, I'd like to display items in order of their priorities. I am trying the following:
<div ng-repeat="item in items | orderBy:'itemTypePriorities[item.itemType]'>
But it doesn't seem to work - my guess is that it's interpreting the orderBy expression in the context of item, i.e. it's trying to order by the nonsensical expression item.itemTypePriorities[item.itemType].
Is there a way to rewrite the ng-repeat to make it wok correctly, without having to modify the items or itemTypePriorities arrays in any way?
You can use of course the orderBy filter but I think you will need to create a custom function to orderit.
For example:
<div ng-repeat="item in items | orderBy:myFunction>
Where myFunction is declared somewhere in your controller as:
$scope.myFunction = function(el){
//el is the current element under evaluation
}
And should return a value that will be compared with <, === and > with the other. In your case the function can be:
$scope.myFunction = function(el){
return itemTypePriorities.indexOf(el);
}
And that should work

How do I ask AngularJS to recalculate the value of a complex function?

I have an AngularJS controller with the following function:
$scope.getExampleValue = function(exampleId) {
// calculate a value using underscore's _.where()
// clause against two JSON arrays on the $scope
// and the exampleId parameter passed in
return computedValue;
}
The function's parameter (exampleId) is rendered from the server, so the resulting HTML looks like this:
<div ng-controller="ExampleController">
...
<span>{{ getExampleValue(3) }}</span>
<span>{{ getExampleValue(4) }}</span>
<span>{{ getExampleValue(5) }}</span>
...
</div>
The problem I have is that AngularJS doesn't know to call getExampleValue() again when the JSON arrays used in the function have changed: they're 2 simple JSON arrays, new JSON items can be added or removed, or properties of existing JSON items in either array can be modified which affect the result.
I've looked at $scope.watch() and $scope.watchCollection() but I'm unsure how I can use them without changing my approach to bind against already computed values rather than against the function I prefer.
Essentially I think I'm asking how to notify AngularJS that a complicated bound value has changed, then I could wrap that notification up in a $scope.watch()..
Thanks for your help.
You are looking for $scope.$apply:
When you change the JSON asyncly, run:
$scope.$apply(function () {
// update the properties on $scope
});

ng-model and ng-options not matching up?

I have a method in my resources object that comes in as:
resources.type
otherstuff: 'more strings'
type:'specifictype'
morestuff: 'morestuff'
The user can change this type with a dropdown / through another call that gets a list of all possible types which looks like resourceList.types which has a list of objects like this json
types:
[
{name:'resourcetype1'},
{name:'resourcetype2'},
etc...
],
my html looks like:
<select ng-model="resources.type" ng-options="name.name for name in resourceList.types">
</select>
The select/drop down box populates with my resourceList.type stuff but when the page loads the ng-model doesn't set to the already selected resource type. It actually selects a blank entry at the top of the drop down when you click. Is angular picky about this? How can I get the ng-model to behave the way I want it to?
I tried messing around with ng-options with the different ways of getting the repeat but I feel like this is more how angular connects the model. Does it just match the string to the ng-options list?
Here's the plnkr as you can see it's not defaulting to type1
http://plnkr.co/edit/NyWACtFQuyndR6CG8lpN?p=info
In Angular, the model is the single source of truth.
This means that if you want a value selected (and bound to your ngModel) you need to assign it to the model:
<select ng-model="resources.type"
ng-options="type.name as type.name for type in resourceList.types">
</select>
$scope.resources = {...};
$scope.resourceList = {
...
types: [
{name: 'resourcetype1'},
{name: 'resourcetype2'},
...
]
};
// Set the model to the desired value
$scope.resources.type = $scope.resourceList.types[0].name;
See, also, this short demo.
You don't have to set your model's value to the reference object in resourceList. In fact, the accepted answer works fine without this line:
$scope.resources.type = $scope.resourceList.types[0].name;
How is it working? Thanks to the "as" notation in the ngOptions. Without the "as", the match is made on the full type element, which is an object, so the match is made on the reference's object, not the name's value.
With the "as" the match is made on the element's property, name.
I've forked the plunker: http://plnkr.co/edit/kORfxGdsWBUlFWHXp6Ry?p=preview
in my case it didnt work since ngOptions was an array of integers and i was trying to set ngModal to string type (2the year 2014).
the solution is simple: parseInt function

unshifting to ng-repeat array not working while using orderBy

Here is my problem. I've got a comment roll thats using ng-repeat to display the content of a comment array. When the user submits a comment I wan't to unshift to that array in order to display the most recent comment at the top of the list. This works perfectly, but when I add the orderBy filter to the repeat the new comment is applied to the bottom of the repeat.
Here is the comment array HTML:
<ul ng-repeat="comment in comments | filter:{page_id:raceID} | orderBy:'id':'reverse' track by comment.id ">
<li>{{comment.id}}</li>
<li>{{comment.page_id}}</li>
<li>{{comment.user_name}}</li>
<li>{{comment.comment_copy}}</li>
</ul>
Here is the corresponding Controller:
$scope.comment = new newComments({page_id:3, comment_copy:'test comment copy'});
$scope.comment.$save(function(data) {
$scope.comments.unshift($scope.comment);
console.log($scope.comment.id);
});
.....
I scrapped the
orderBy:'id':'reverse'
and instead used a custom filer left on another post here, Angular ng-repeat in reverse. Here is the custom function
app.filter('reverse', function() {
return function(items) {
return items.slice().reverse();
};
});
Now the most recent comment was still not showing up at the top of the page so I had to change from unshift to push. This worked perfectly. Here's the code:
$scope.comment.$save(function(data) {
$scope.comments.push($scope.comment);
});
A few things are wrong in your original post:
orderBy:'id':'reverse' should be orderBy:'id':reverse, where reverse is a boolean (so either replace it by a variable available on the scope, or by a hard-coded true/false). Currently, it defaults to undefined and is interpreted as false.
In your controller code, the field comment.id is not assigned. It will default to undefined and that's the reason sorting does not work as expected.
Additionally:
Array unshift or push will not make a difference in your use case if you correct the aforementioned and the orderBy function is invoked.
In my experience in Angular 1.5.8, track by $index in fact prevented the orderBy function from working. Replacing it by a unique identifier on the object to be repeated, i.e. track by comment.id is preferred. In terms of performance, they should be analogous. Not using the track by clause is the slowest option though. (https://docs.angularjs.org/api/ng/directive/ngRepeat)
that worked for me, would love to know how it affects performance though:
change:
track by comment.id
to:
track by $index
both unshift() and push() should work now, but again, not sure about how much it slows down the DOM regeneration.

Resources