Creating orderBy expression that runs a function in ng-repeat - angularjs

I have an array of people with firstNames and lastNames. I'm want to ng-repeat through the them and then orderBy: 'lastName'. This works fine.
However, some persons in my people array only have a first name. I want to be able to order by the last name, and whenever it finds someone with a only a first name, to treat that first name as a last name, ie. the initial of the first name is alphabetised with the rest of the last names (hope that makes sense...)
Is it a case of writing a function in my controller and then calling that variable in the orderBy: expression? I've tried that and it didn't work:
Controller:
self.nameOrder = function(thisOne) {
if (thisOne.lastName == null) {
thisOne.firstName == thisOne.lastName;
}
}
View:
<p ng-repeat="name in people | orderBy: 'lastName.nameOrder()' ">{{lastName}}</p>
I'm aware the above is probably totally wrong, but I thought it better to at least show what I've been attempting so it's clearer what my intentions are :/
Any help would be appreciated. Thanks in advance.

The function is supposed to be a getter, i.e. to return a value that orderBy will use to compare all the users with each other.
Your function doesn't return anything. Moreover, you're not passing the function as argument to orderBy. The code should be:
self.nameOrder = function(thisOne) {
// return lastName, or firstName if lastName is null
return thisOne.lastName || thisOne.firstName;
}
and the html should use
ng-repeat="name in people | orderBy:vm.nameOrder"
(assuming vm is the alias used for your controller)

Maybe the problem is with:
thisOne.firstName == thisOne.lastName;
That line should be:
thisOne.firstName = thisOne.lastName;

Related

ng-bind with AngularJS translate interpolation

I need to render characters such as < and >, which may be included in a user's first or last name. I'm using angular translate to render the name, passing the first and last name as values. However, if the first or last name contains < the value will not be displayed. I think I need to use something like ng-bind="translation_key | translate | values" but this is incorrect. How can I use ng-bind with angular translate interpolation? Is this the correct way to go about rendering a translated value that contains chars such as <, >? Thanks.
*Edit
This almost works but the sanitizer outputs an error.
ng-bind="'locales.user_filter' | translate: { first: user.firstName, last: user.lastName }"
Error: [$sanitize:badparse] The sanitizer was unable to parse the following block of html:
I wound up using a filter before passing the values to the translation key.
.filter('htmlEntities', function () {
return function (input) {
return String(input).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
};
});
var first = $filter('htmlEntities')(scope.user.firstName);
var last = $filter('htmlEntities')(scope.user.lastName);
scope.displayName = $translate.instant('locales.user_filter', { first: first, last: last });
<td ng-bind-html="displayName"></td>

AngularJS v2 is not a function?

I have a very simple piece of code to run a dynamic sortBy over my array. I am using a select with ng-model to return the correct key by which to sort. However, I can change the select once, and the orderBy works. But once I do it again, I get a very strange error
Controller
//change task sort
$scope.changeOrder = 'task_date';
$scope.changeOrder = (filterTask) => {
if (filterTask == "due") {
$scope.changeOrder = 'task_date';
} else if (filterTask == "imp") {
$scope.changeOrder = 'task_importence';
}
};
Template
<select ng-change=changeOrder(filterTask) ng-model="filterTask">
<option value="due">Due First</option>
<option value="imp">Importance</option>
</select>
<task-item ng-repeat="task in $ctrl.user.task | orderBy : changeOrder"></task-item>
Here is the error - There is nothing called "v2" in my system
Welcome to the untyped world that is JavaScript.
Your error is actually quite apparent: $scope.changeOrder becomes a function and a standard variable. Once you select a value in your select drop-down, it ceases to be a function and reverts to a standard variable. Then, you can no longer call it.
You would be wise to split this up into two variables instead. I'd recommend using $scope.orderState and $scope.changeOrder, where orderState just holds the strings and changeOrder is your function.
I think the problem is that both of your $scope variables have the same name. You try to assign a function and a value to $scope.changeOrder. Try splitting it up into two variables

Angular ng repeat filter inside ng repeat

I've been trying to make a list of geozones, with a select of taxes each (not all taxes apply to all geozones).
So I did a ng-repeat for the geozones, and inside each of them a ng-repeat with all taxes. Problem is I don't know how to send the id of the geozone being filtered at the moment. This is the code right now:
<md-option ng-repeat="tax in taxClasses | filter: filterTax" value="{{ '{{ tax.id }}' }}">{{ '{{ tax.name }}' }}</md-option>
and the JS:
$scope.filterTax = function(tax, n){
angular.forEach(tax.geoZone , function(geo){
if(geo === $scope.prices[n].geozoneId){
return true;
}
});
return false;
};
Need n to be the index of the geozone, or something of the sort. Thanks in advance!
Your idea is not that far off, but using filter: is not even necessary, as the pipe | is already a filter command:
ng-repeat="<var> in <array> | <filterFunction>:<...arguments>"
Thus you can create a filter (see https://docs.angularjs.org/guide/filter for details on that)
ng-repeat="tax in taxClasses | filterTax: <geozoneIndex>"
The value form the collection will be passed as the first argument of your filterTax function. Any further argument is passed separated by a colon :.
When you use this, you have to propagate a filter method like this:
app.module(...).filter('filterTax', function(/* injected services */) {
return function (/* arguments */ input, geozoneIndex) {
// function body
}
});
Alternatively use a filter function from your scope:
// template
ng-repeat="tax in filterTaxes(taxClasses, <geozoneIndex>)"
// script
$scope.filterTaxes = function(taxClasses, geozoneIndex) {
// do the whole filtering, but return an array of matches
return taxClasses.filter(function(taxClass) {
return /* in or out code */;
});
};
This means your geozoneIndex may be either a fixed value or being pulled from a variable, that's available in that scope.
Just be aware that your filterTax function will be called a lot, so if your page is getting slow you might want to consider optimizing that filtering.

Convert an express-like string to an expression in Angular view

Assuming that I have an expression-like string in my scope
$scope.expressionString = 'model.key == "anything"'
I want to use this string as an expression in view, can I do that?
In view, I will have something like
<div ng-if="expressionString"></div> but of course, expressionString should be something else instead.
I appreciate any help. Cheers!
You can use $eval to evaluate your expression , there are two ways to do it in your case
Solution 1
<div ng-if="$eval(expressionString)"></div>
Solution 2
In the controller store the evaluated value of the expression like below
$scope.expressionString = $scope.$eval('model.key == "anything"')
and then in the view simply use it without using $eval in the view
<div ng-if="expressionString"></div>
I found the answer, made a parse filter to parse the string and assign it a scope
angular.module('zehitomo')
.filter('parse', function ($parse) {
return function (expression, scope) {
return $parse(expression)(scope);
};
});
And in view
ng-if="expressionString | parse:this"
You cannot use global variables (or functions) in Angular expressions. Angular expressions are just attributes, so are strings and not Javascript code.
Please see this stackoverflow answer once
Although, you can achieve it using a function instead of a variable:
$scope.expressionString = function(toCompare) {
return $scope.model.key == toCompare;
}
and in your view:
<div ng-if="expressionString('anything')"></div>

How to set class to first item in ng-repeat that has been sorted with orderBy?

I have a list of items, which comes in unsorted, I use orderBy to sort by name alphanumerically.
<li class="ticker-li"
ng-repeat="ticker in tickers | orderBy:'ticker'"
ng-class="{'selected':ticker.selected}">
<div class="ticker"
ng-click="unselectAll(); ticker.selected = !ticker.selected;
selectTicker(ticker);
revealTickerOptions()">
{{ticker.ticker}}
</div>
Now in my controller this is how I'm currently setting the first items selected class:
var vs = $scope;
vs.tickers = data;
vs.tickers[0].selected = true;
^ This worked perfectly until I needed to add the orderBy so that items appear by alpha order:
I found this answer here, however it locks the first item to always have the class.
Modifying my code a bit, I was able to have other buttons gain that class on click, however the $first item still stayed with the class.
ng-class="{'selected':$first || ticker.selected}"
In my controller this is my unselectAll function, which doesn't work with 'selected':$first:
vs.unselectAll = function() {
for (var i = 0; i < vs.tickers.length; i++) {
vs.tickers[i].selected = false;
}
};
How should the code either in the markup or controller need to be updated to fix this issue?
Give this a shot, I'm not sure how it reads the $index on the sort by, but get rid of the $first thing and put this init statement in there.
<li class="ticker-li"
ng-repeat="ticker in tickers | orderBy:'ticker'"
ng-init="$index ? ticker.selected = false : ticker.selected = true"
ng-class="{'selected':ticker.selected}" ng-click="unselectFirst($index)">
I think this is a grey area between a hack or not, you aren't technically aliasing a property in the ng-init, but i think it is a fine line. The other solution would be sort the array in your controller, there is an example in the docs that sort on alphabetic order, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Resources