I have an expression
{{ product.SelectedProduct.BasePrice - product.SelectedProduct.Discount | currency }}
I need to be able to order my products by the value of baseprice - discount. Is there a way to do this? So I want something like
{{product.SelectedProduct.ProductName |
orderBy : product.SelectedProduct.BasePrice - product.SelectedProduct.Discount }}
New code:
<div class="row-fluid">
<span class="mktpl_productnm" ng-show="product.SelectedProduct.ProductName">{{product.SelectedProduct.ProductName || 'product not available' | orderBy: price }}</span>
<span ng-hide="product.SelectedProduct.ProductName" class="field-validation-error">Product not available</span>
<span ng-show="product.SelectedProduct.remaining < product.SelectedProduct.prodterm.minQuantity" class="field-validation-error">
(Out of stock)
</span>
</div>
vm.price = function (product) {
debugger;
return product.SelectedProduct.BasePrice - product.SelectedProduct.Discount;
};
First of all, your example doesn't make much sense: orderBy applies to an array, to order the elements of this array. But product.SelectedProduct.ProductName is probably not an array.
Second: the documentation says:
expression function()stringArray.<(function()|string)>=
A predicate to be used by the comparator to determine the order of elements.
Can be one of:
function: Getter function. The result of this function will be sorted using the <, ===, > operator.
[...]
So, all you need is a function in the scope that returns the element to compare for a given element of the array:
$scope.reducedPrice = function(product) {
return product.SelectedProduct.BasePrice - product.SelectedProduct.Discount;
};
and in the view:
{{ productArray | orderBy:reducedPrice }}
Another option would be to precompute the reducedPrice and store it as an attribute or the product, and then simply use
{{ productArray | orderBy:'reducedPrice' }}
Related
I have a function that checks if a value is true or false and a function that changes value.
checkValue(item){
if item.exists{
return 1;
}
else {
return 0;
}
}
and in my html I have a ng-repeat that orders by this function:
<span ng-repeat="item in items | orderBy: checkValue"></span>
<a ng-click="changeItemExists(selectedItem)">
now when item.exists change the orderBy does not work again and order the spans again.
How can I make the orderBy to watch for any changes in the item.exsist?
You don't need the checkValue function.
Just do:
<span ng-repeat="item in items | orderBy: 'exists'"></span>
Then the orderBy will refresh the order automatic on any changes of the 'exists' property.
I am successfully using ng-repeat and its filter and orderBy functions
<div ng-repeat="center in centers | filter: subjectName | orderBy:[\'name\',\'parent']">'
However when user decides to print/download the "centers" filtered and ordered by angular, the printing method fails because it knows only the original unsorted unfilterd array. Is there a way to capture the object resulted by the filtered/orderBy angular methods so I could use it directly in my printing method?
<div ng-repeat="center in centers | filter: subjectName | orderBy:[\'name\',\'parent']">'
'<span>{{center.name}</span>'+
</div>
....
// what I have so far
<li><a ng-click="print(centers)"><span class="glyphicon glyphicon-print"></span> Print All </a></li>
...
// what I would like to have
...
<li><a ng-click="print(filteredOrderedObject)"><span class="glyphicon glyphicon-print"></span> Print All </a></li>
...
// what I have
scope.print = function(ocw) {
//printing
}
// what I would like to have
scope.print = function(filteredOrderedObject) {
//printing
}
You can use the $filter service directly in your code. Something like this.
var filtered = $filter('filter')(centers, subjectName);
filtered = $filter('orderBy')(filtered, ['name', 'parent']);
https://docs.angularjs.org/api/ng/filter/filter
You can use $eval to evaluate expression in controller.
Following is the syntax
$scope.centers = $scope.$eval("center in centers | filter: subjectName | orderBy:[\'name\',\'parent']");
I'm trying to filter data based on the select and input fields. Here is part of my code:
<select ng-change="students.changeValue(changeFilter)" ng-model="changeFilter">
<option ng-selected="true" value="name">Name</option>
<option value="age">Age</option>
</select>
<input ng-model="searchPhrase" />
name and age are example keys that I have. Here is my data generator structure:
<div ng-class="{breakLine: $index % 3 === 0}"
class="student-item col-md-4"
ng-repeat="s in students.studentsList | filter:{searchFilter: searchPhrase}">
The searchFilter is supposed to be a key that is set from a variable but it doesn't work. If I make there something like: filter:{name: searchPhrase} then it works because I have such keys in my data structures.
Here is a part of the controller:
.controller('StudentsListCtrl', ['$scope', function($scope) {
$scope.searchFilter = '';
this.changeValue = function(val) {
console.log(val); ---> gives key or age on change
$scope.searchFilter = val;
}
So when I manually write e.g.: | filter:{name: searchPhrase} then it works. But when I pass 'name' (i.e. the key) in a variable like: | filter:{searchFilter: searchPhrase} then it's broken...
How can I fix this?
You should use the last option described in filter documentation for the expression:
function(value, index, array): A predicate function can be used to write arbitrary filters. The function is called for each element of the array, with the element, its index, and the entire array itself as arguments.
In the controller, you define the predicate, e.g.
$scope.personFilter = function(person) {
// return true if this person should be displayed
// according to the defined filter
}
and use it in the ng-repeat filter, e.g.
<ul>
<li ng-repeat="p in persons | filter:personFilter">{{p.name}} ({{p.age}})</li>
</ul>
See the plunker for a demo. Note I choose age as a string to simplify the predicate function. You can refine it according to your needs.
Write a function in your $scope to generate the object that is passed to the filter:
$scope.getFilterObject = function() {
var filterObject = {};
filterObject[$scope.searchFilter] = $scope.searchPhrase;
return filterObject;
}
Use it as the argument in the filter:
ng-repeat="s in students.studentsList | filter:getFilterObject()"
I am using ng-repeat to create a table of data:
<div class="divTable" ng-repeat="expense in exp.expenses | filter:exp.query">
<div>{{expense.amount | ldCurrency : true}}</div>
...
</div>
A couple of the cells that I am creating are being modified through an Angular filter. In the example above, I am changing the integer to a currency. So the original 4 is changed to $4.00. When I filter the entire list with my exp.query, it does not modify the exp.query search term through the ldCurrency.
The means that if I search on $4, it will not find it, because the backing data is 4, even though $4 is on the page.
I know this is confusing, with the two types of filters that I am talking about here.
How can I search on the data that is being shown on the page and not on the backing data?
You have to create you own filter. What you want to do to is a bad idea, because you are melding the view layer and the model layer.
A example of a filter.
The html:
<input ng-model="query" ng-trim="true">
<span>Finding: </span><span>{{ query }}</span>
<div ng-repeat="product in products | productsFilter: query">
<strong>{{ $index }}</strong>
<span>{{ product.name }}</span>
<span>{{ product.price | currency: '$'}}</span>
</div>
The custom filter:
.filter('productsFilter', [function () {
// Private function: It removes the dollar sign.
var clearQuery = function (dirtyQuery) {
var index = dirtyQuery.indexOf('$');
if (index === -1)
return dirtyQuery;
return dirtyQuery.substr(index+1, dirtyQuery.length-index)
};
// The Custom filter
return function (products, query) {
if (query === '') return products;
var newProducts = [];
angular.forEach(products, function (product) {
var cleanQuery = clearQuery(query);
var strProductPrice = '' + product.price;
var index = strProductPrice.indexOf(cleanQuery);
if (index !== -1) {
newProducts.push(product);
}
});
return newProducts;
};
}]);
The key is in the angular.forEach. There I decide if the product will belong to the new filtered collection. Here you can do the match you want.
You can find the complete example in full plucker example and see a lot of filters in the a8m's angular-filter
I have the following ng-repeat:
<div ng-repeat="location in truckDetail.locations track by $index | orderBy:location.startDate" class="locationLoopRow">
<span class="rowEdit"><i ng-click="location.editMode=!location.editMode" class="fa {{location.editMode?'fa-ban':'fa-pencil'}}"></i></span>
<span class="rowDate">{{location.startDate|date:'dd.MM.yyyy'}}</span>
<span class="rowDate">{{location.endDate|date:'dd.MM.yyyy'}}</span>
<span class="rowLocation">{{location.name}}</span>
</div>
The orderBy seems to be ignored completely as seen in the screenshot.
I also tried to solve this by using a sort-function:
| orderBy:dateOrderBy(location.startDate)
$scope.dateOrderBy=function(date) {
return date.getFullYear()+'/'+date.getMonth()+'/'+date.getDate();
},
In debug mode I can see that this message returns values like '2015/4/29'. Still: The list isn't sorted at all
You don't need to call dateOrderBy function in ng-repeat, you only need to specify it's name:
| orderBy:dateOrderBy
Then in your controller your sort function will receive the location object:
$scope.dateOrderBy = function(location) {
return location.startDate;
};
Example in plunkr.
UPD: this one should work as well:
| orderBy:'startDate'
UPD 2: track by $index should always go in the end of expression:
location in truckDetail.locations | orderBy:'startDate' track by $index