ng-show expression: comparing multiple strings using || - angularjs

Can someone please explain why this works:
<div ng-repeat="fruit in fruits">
<span ng-hide="fruit.type == 'apple' || fruit.type == 'banana'">
{{fruit.type}}
</span>
</div>
Renders:
pear lemon
But this doesn't:
<div ng-repeat="fruit in fruits">
<span ng-hide="fruit.type == 'apple' || 'banana'">
{{fruit.type}}
</span>
</div>
Renders:
// nothing

In Javascript (and most languages I know), comparisons aren't communicable. For every comparison (in this case equality), you unfortunately need a full statement.
In fruit.type == 'apple' || 'banana',
1. fruit.type == 'apple' is evaluated.
2. After that, it || compares the result of that to 'banana', which in Javascript is a truthy value ('' is the only "falsy" string, all other strings are "truthy").
In essence, you end up with fruit.type == 'apple' || TRUE, which will always trigger ng-hide.

in this case its a bad expression fruit.type == 'apple' || 'banana'
|| are used in javascript for "OR" and define a default value to a variable when some reference values is undefined or null for example:
var name = null || "mottaman85";
consolog.log(name);
"mottaman85"
function test(nombreV){
var name = nombreV || "mottaman85";
console.log(name)
}
test();
"mottaman85"

Related

AngularJS ng-if with a true or false object value

Not entirely sure why this isn't working. I'm trying to hide a col is the value of driveThru is false OR equals to 'N'.
ng-if="location.driveThru === false || location.driveThru === 'N'"
However the === false isn't working and I cant determine why.
if its a boolean change as ,
"!location.driveThru' || location.driveThru === 'N'
and === with string enclosed in quotes if its a string a follows,
ng-if="location.driveThru === 'false' || location.driveThru === 'N'"

Angular Sorting with null objects

I need to take records with null values on top , when I'm sorting by ASC
<tr ng-repeat="footballer in footballers=(footballers | orderBy:predicate)">
predicate : ['team.name','id]
Some footballers have no team, so team object == null and team.name==null, and I need to have them on the top
I wanted to rewrite sort function, but I need to save predicate
You can use something like this in your controller:
$scope.nullsToTop = function(obj) {
return (angular.isDefined(obj.team) ? 0 : -1);
};
And on the HTML:
<tr ng-repeat="footballer in footballers | orderBy:[nullsToTop].concat(predicate)">
This way you can maintain your predicate separately. Just concat the nullsToTop function in the orderBy expression to run it first.
Plunker
Create a function in the controller that will receive as parameter the entity and will return either ['team.name','id] either [] or other values that will help push the non-sortable elements to top/bottom of the list.
EDIT (example)
HTML:
<li ng-repeat="item in items | orderBy:getItemOrder track by $index">...
AngularJS Ctrl:
$scope.getItemOrder = function (entity) {
if (entity.id === null || .... === null) {
return 0;
}
return ['team.name','id];
}
I was able to do it by returning a '+' or a '-' if the value is null, close to what bmleite did but is more contained in the function itself.
1. create a function that adds a + or a - to the null values depending on the sort order (asc or desc) so that they'd always remain at either the top or bottom
function compareAndExcludeNull(element){
var compareWith = $scope.order.propertyName; // in your case that's 'predicate'
var operatorToBottom = $scope.order.reverse ? '+' : '-'; // If decending then consider the null values as smallest, if ascending then consider them biggest
var operatorToTop = $scope.order.reverse ? '-' : '+';
if(element[compareWith] == null){
return operatorToTop;
} else {
return operatorToBottom+element[compareWith];
}
}
if you want to have the null values at the bottom of the sorted array then just switch the operatorToTop with operatorToBottom and vice versa to get:
function compareAndExcludeNull(element){
var compareWith = $scope.order.propertyName; // in your case that's '
var operatorToBottom = $scope.order.reverse ? '+' : '-'; // If decending then consider the null values as smallest, if ascending then consider them biggest
var operatorToTop = $scope.order.reverse ? '-' : '+';
if(element[compareWith] == null){
return operatorToBottom;
} else {
return operatorToTop+element[compareWith];
}
}
2. Call the function and pass the array you're sorting
Javascript:
In my case I am calling this function from HTML and passing different propertyName depending on the column the user is sorting by, in your case you'd just need to pass 'predicate' as the propertyName
$scope.order.sortBy = function(propertyName){
$scope.order.reverse = (propertyName !== null && $scope.order.propertyName === propertyName) ? !$scope.order.reverse : false;
$scope.order.propertyName = propertyName;
$scope.resources = $filter('orderBy')($scope.resources,compareAndExcludeNull,$scope.order.reverse);
};
HTML:
<div>
<span ng-if="showKeywords" class="label label-info" ng-click="order.sortBy('keyword')" style="margin-right: 5px">
Order by Keywords
<span ng-if="order.propertyName=='keyword'" class="glyphicon glyphicon-triangle-{{order.reverse? 'bottom' : 'top'}}">
</span>
</span>
<span ng-if="showWebsites" class="label label-info" ng-click="order.sortBy('res_title')">
Order by Website
<span ng-if="order.propertyName=='res_title'" class="glyphicon glyphicon-triangle-{{order.reverse? 'bottom' : 'top'}}">
</span>
</span>
</div>
I know this is an old post but there is a better way for doing this and could not see it anywhere.
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', ["$scope", function($scope){
$scope.footballers = [
{id: 0, qt: 13},
{id: 1, qt: 2},
{id: 2, qt: 124},
{id: 3, qt: 12125},
{id: null , qt: 122},
{id: 4, qt: -124},
{id: 5, qt: -5235},
{id: 6, qt: 43},
]
}])
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="footballer in footballers | orderBy:['id==null || id']">
{{footballer.id}}, {{footballer.qt}}
</div>
</div>
One can also use this to show 'id' with any specific value on top of others

AngularJS undefined, null and 0

Consider the following code:
<div ng-controller="SomeCtrl">
<div ng-show="someVariable.someProperty">Value of someProperty is set</div>
</div>
Now, when I set in my SomeCtrl controller:
$scope.someVariable.someProperty = 1;
It will show the div, as expected. But when I set:
$scope.someVariable.someProperty = 0;
It does not, while I only want to hide it in case $scope.someVariable.someProperty is either undefined or null. Of course I could write a simple Javascript helper-function to give me the expected result, or write something like:
<div ng-show="someVariable.someProperty || someVariable.someProperty == 0">Value of someProperty is set</div>
But isn't there a more elegant way to
handle this in AngularJS?
I only want to hide it in case $scope.someVariable.someProperty is either undefined or null
In JavaScript, undefined == null, but neither == 0. No need for extra functions or an extra conditional
ng-show="someVariable.someProperty != null"
Change your logic to be something more like this then:
ng-show="someVariable.someProperty !== null && someVariable.someProperty >= 0">
That way null or undefined will be falsy.
I almost forgot that null >= 0 === true (silly Javascript :)
Put a function in the controller like this:
$scope.shouldShow = function() {
if($scope.someVariable.someProperty === null ||
$scope.someVariable.someProperty === "undefined") {
return false;
}
return true;
}
and in the html:
<div ng-show="shouldShow()">Value of someProperty is set</div>
As mentioned by #tymeJV, "0" is evaluated as false in JS, so ng-show="0" will evaluate to hiding the div.
In SomeCtrl, you could write a function which encapsulates the logic to determine whether someProperty is null or undefined and return true or false based on that evaluation.
angular.module("app")
.controller("SomeCtrl", function($scope) {
$scope.someVariable = {};
$scope.someVariable.someProperty = null;
$scope.isNullorUndefined = function(value) {
return value === null || angular.isUndefined(value);
};
});
<div ng-show="!isNullOrUndefined(someVariable.someProperty)">Value of someProperty is set</div>

'f' character is considered as true in a ng-show

When using angular, I had a very strange issue:
<div ng-app>
<textarea ng-model="resp" placeholder="Response"></textarea>
<a ng-show="resp">Respond</a>
</div>
Fiddle
When writing something into the textarea, the Respond link is shown.
However, strangely, when writing the letter 'f', the Respond button doesn't show.
A workaround for this is to use the condition resp.length>0, but I wonder why the letter 'f' behaves in a special way.
Actually 'f' is considered as false.
AngularJS uses the toBoolean function internally to evaluate ng-show / ng-hide expressions.
You should get the same behaviour for "f", "false", "0", "n", "no" and "[]".
You can read more about it here: https://github.com/angular/angular.js/issues/1229.
This is angular's toBoolean function:
function toBoolean(value) {
if (typeof value === 'function') {
value = true;
} else if (value && value.length !== 0) {
var v = lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
} else {
value = false;
}
return value;
}
Update:
In AngularJS 1.3+ this behaviour has been removed.
Quote: Due to bdfc9c02, values 'f', '0', 'false', 'no', 'n', '[]' are no longer treated as falsy. The only JavaScript values that are treated as falsy by the expression parser are now: false, null, undefined, NaN, 0 and "".
My recommendation would be to check to see if resp is an empty string. Since your textarea is a string value if the string is not empty then your respond text will be displayed.
<div ng-app>
<textarea ng-model="resp" placeholder="Response"></textarea>
<a ng-show="resp != ''">Respond</a>
</div>
Check out using strict checks for conditions. 'F', 'f', 'N', 'n', '0' also don't work in your fiddle. They are considered to be shortcuts for 'false'. Typing out 'false' also fails to show the 'Respond' button.
It's just the opposite, "f" is treated as false. Note that so are all the following:
"F"
"false"
"FALSE"
"FaLsE"
" faLse "
""
" "
" "
(i.e., case and leading/trailing whitespace do not matter)
However, partial words (e.g., "fa", "fal", "fals") are not.

angularjs search and ignore spanish characters

I'm adding a simple sort on a page. The idea is to search products. These products are written in spanish language and has accents. For example: 'Jamón'.
Here is my code:
<div class="form-inline">
<label>Search</label>
<input type="text" ng-model="q"/>
</div>
<div ng-repeat="product in products_filtered = (category.products | filter:q | orderBy:'name')">
....
</div>
The only problem i have is that you have to type in "Jamón" in order to find the product "Jamón". What I want is to be more flexible, if the user types in "Jamon", the results must include "Jamón".
How can i search with angular filters and forget about accents? Any Idea?
Thanks in advance.
You'll need to create a filter function (or a full filter). This is the simplest thing that could possibly work:
HTML
<div ng-app ng-controller="Ctrl">
<input type="text" ng-model="search">
<ul>
<li ng-repeat="name in names | filter:ignoreAccents">{{ name }}</li>
</ul>
</div>
Javascript
function Ctrl($scope) {
function removeAccents(value) {
return value
.replace(/á/g, 'a')
.replace(/é/g, 'e')
.replace(/í/g, 'i')
.replace(/ó/g, 'o')
.replace(/ú/g, 'u');
}
$scope.ignoreAccents = function(item) {
if (!$scope.search) return true;
var text = removeAccents(item.toLowerCase())
var search = removeAccents($scope.search.toLowerCase());
return text.indexOf(search) > -1;
};
$scope.names = ['Jamón', 'Andrés', 'Cristián', 'Fernán', 'Raúl', 'Agustín'];
};
jsFiddle here.
Please notice that this only works for arrays of strings. If you want to filter a list of objects (and search in every property of every object, like Angular does) you'll have to enhance the filter function. I think this example should get you started.
JavaScript has a neat function for this, called localeCompare, where you can specify sensitivity = base, an option that makes it treat á and a as equivalent, but it is not widely supported. I think your only option is to create a filter function wherein you normalise both strings manually (replacing ó with o and so on) and compare the results.
Here is a slightly enhanced version (not counting the extra foreign characters it searches for, of course) of the above. I put it straight into my controller and it allowed me to search all the data that I wanted to search. Thanks Bernardo for your input into this!
Hope it helps somebody out.
function removeAccents(value) {
return value
.replace(/á/g, 'a')
.replace(/â/g, 'a')
.replace(/é/g, 'e')
.replace(/è/g, 'e')
.replace(/ê/g, 'e')
.replace(/í/g, 'i')
.replace(/ï/g, 'i')
.replace(/ì/g, 'i')
.replace(/ó/g, 'o')
.replace(/ô/g, 'o')
.replace(/ú/g, 'u')
.replace(/ü/g, 'u')
.replace(/ç/g, 'c')
.replace(/ß/g, 's');
}
$scope.ignoreAccents = function (item) {
if ($scope.search) {
var search = removeAccents($scope.search).toLowerCase();
var find = removeAccents(item.name + ' ' + item.description+ ' ' + item.producer+ ' ' + item.region).toLowerCase();
return find.indexOf(search) > -1;
}
return true;
};
The method, proposed by #Michael Benford has a big disadvantage: it is a stateful filter. This practice is strongly discouraged by angular. Plus, the method doesn’t work with deep arrays and objects.
I prefer to extend the standard angular filter:
HTML:
<div ng-app ng-controller="Ctrl">
<input type="text" ng-model="search">
<ul>
<li ng-repeat="person in people | filter: search : searchFn">{{ person.names }} {{ person.surnames }}</li>
</ul>
</div>
Javascript:
function Ctrl($scope) {
$scope.people = [{names: 'Jose', surnames: 'Benítez'}, {names: 'José María', surnames: 'Núñez Rico'}, {names: 'Rodrigo', surnames: 'Núñez'}];
$scope.searchFn = function(actual, expected) {
if (angular.isObject(actual)) return false;
function removeAccents(value) {
return value.toString().replace(/á/g, 'a').replace(/é/g, 'e').replace(/í/g, 'i').replace(/ó/g, 'o').replace(/ú/g, 'u').replace(/ñ/g, 'n');
}
actual = removeAccents(angular.lowercase('' + actual));
expected = removeAccents(angular.lowercase('' + expected));
return actual.indexOf(expected) !== -1;
}
}
Check here for solutions to filter a list of objects and search keywords in every property of every object. It also maintain angular like search and also search with/ without accent characters.
Following is the filter definition:-
// ignore accents filter
$scope.ignoreAccents = function (item) {
if (!$scope.search) return true;
var objects = [];
var jsonstr = JSON.stringify(item);
var parsejson = JSON.parse(jsonstr);
var searchterm = $scope.search.replace(/[!#$%&'()*+,-./:;?#[\\\]_`{|}~]/g, ''); // skip replace if not required (it removes special characters)
objects = getNoOfResults(parsejson, searchterm);
return objects.length > 0;
};

Resources