How to add a conditions to an expression in AngularJS - angularjs

I'm trying to do something in Angular like this:
Your search found {{ searchResults.iTotalRecords > 0 , 0 }} results.
Certainly this doesn't work but you get the idea. If my object is an integer, how can I check if the value is greater than zero or else set it to 0?
Thanks.

Custom Angular filters are well-suited for this purpose if you only want to change the presentation of the data, without changing the underlying (real) data in your application.
Below is an example filter for your use-case. You could put any other logic in your filter.
HTML:
<p>Your search found {{searchResults.iTotalRecords | resultCountFilter}} results.</p>
Javascript:
angular.module('app', []).filter('resultCountFilter', function () {
return function (resultCount) {
return resultCount > 0 ? resultCount : 0;
};
});​
Here's a JSFiddle: http://jsfiddle.net/alexross/ZN87E/
AngularJS Docs on Filters: Understanding Angular Filters

To do that you can just use a function on your $scope:
app.controller('MainCtrl', function($scope) {
$scope.foo = function() {
return $scope.searchResults.iTotalRecords > 0 ? $scope.searchResults.iTotalRecords : 0;
};
});
And in your markup:
<span>{{foo()}}</span>
EDIT: The 100% markup way...
<span ng-show="searchResults.iTotalRecords > 0">{{searchResults.iTotalRecords}}</span>
<span ng-show="searchResults.iTotalRecords <= 0">0</span>
You might also want to check on ngSwitch and ngHide for more advanced cases like this.

Related

AngularJs Inline Check if an array check

Inline in AngularJs is there a way to check if something is an array?
I would have thought this to work:
<div ng-show="Array.isArray(textStuff[0][1])">Hi</div>
I have verified it is in fact an array. Is there something I am missing or another way?
You can put angular.isArray on the scope...
$scope.isArray = angular.isArray;
<div ng-show="isArray(textStuff[0][1])">Hi</div>
Fiddle
You can create global filters to use in your JS or HTML to check against object types. This way you don't pollute your $rootScope or $scopes to use it everywhere, unlike the accepted answer... Angular also has some built in utility functions that can check object types:
angular
.module("yourModule")
.filter("isArray", function() {
return function(input) {
return angular.isArray(input);
};
});
In HTML:
<div ng-show="{{ textStuff[0][1]) | isArray }}">Hi</div>
You may also inject the $filter service into your Controller to access the custom filter by name and compute the filtered results when your controller instance is instantiated (and also when your data changes). This prevents performance issues due to the view expression getting computed rapidly.
angular
.module("yourModule")
.controller("MyController", MyController);
MyController.$inject = ["$filter", "$scope"];
function MyController($filter, $scope) {
this.testStuff = []; // your data
this.filteredResult = $filter("isArray")(this.testStuff[0][1]);
// or if you need to watch for data changes
var vm = this;
$scope.$watchCollection(
function() { return vm.testStuff },
function(newTestStuff) {
vm.filteredResult = $filter("isArray")(newTestStuff[0][1]);
}
);
}
<div ng-controller="MyController as my">
<div ng-show="my.filterResult">Hi</div>
</div>
I would separate logic from the view. Add state in scope and then check it
$scope.showHi = angular.isArray(textStuff[0][1]);
In view
<div ng-show="showHi">Hi</div>

How to use a dynamic value with ngClass

I'm trying to apply a class name that's the same as a scope variable.
For example:
<div ng-class="{item.name : item.name}">
So that the value of item.name is added to the class. This doesn't seem to do anything though. Any suggestions on how to do this?
Thanks!
EDIT:
This is actually being done within a select, using ng-options. For example:
<select ng-options="c.code as c.name for c in countries"></select>
Now, I want to apply a class name that has the value of c.code
I found the following directive, which seems to work, but not with interpolation of the value:
angular.module('directives.app').directive('optionsClass', ['$parse', function ($parse) {
'use strict';
return {
require: 'select',
link: function(scope, elem, attrs, ngSelect) {
// get the source for the items array that populates the select.
var optionsSourceStr = attrs.ngOptions.split(' ').pop(),
// use $parse to get a function from the options-class attribute
// that you can use to evaluate later.
getOptionsClass = $parse(attrs.optionsClass);
scope.$watch(optionsSourceStr, function(items) {
// when the options source changes loop through its items.
angular.forEach(items, function(item, index) {
// evaluate against the item to get a mapping object for
// for your classes.
var classes = getOptionsClass(item),
// also get the option you're going to need. This can be found
// by looking for the option with the appropriate index in the
// value attribute.
option = elem.find('option[value=' + index + ']');
// now loop through the key/value pairs in the mapping object
// and apply the classes that evaluated to be truthy.
angular.forEach(classes, function(add, className) {
if(add) {
angular.element(option).addClass(className);
}
});
});
});
}
};
}]);
Better later than never.
<div ng-class="{'{{item.name}}' : item.condition}">
yes. ' and {{ for classname.
I'm on angular 1.5.5 and none of these solutions worked for me.
It is possible to use the array and map syntax at once though it's only shown in the last example here
<div ng-class="[item.name, {'other-name' : item.condition}]">
Simply using the variable should be sufficient:
<div ng-class="item.name" />
This is also documented in the official documentation.
I think you missed the concept.
A conditional css class looks like this:
<div ng-class="{'<css_class_name>': <bool_condition>}">
And I dont think you want:
<div ng-class="{'true': true}">
You probally want to use:
<div ng-class="item.name"></div>
Angularjs Apply class with condition:
<div ng-class="{true:'class1',false:'class2'}[condition]" >
This can be useful in some cases:
HTML:
<div ng-class="getCssClass()"></div>
JS:
$scope.getCssClass = function () {
return { item.name: item.name };
};

How to conditionally insert HTML after AngularJs Directive paired with ngRepeat?

=================
First of I just want to say that I'm new to this AngularJs business (started earlier today) and I probably over complicate things.
With that being said let's get down to business, shall we?
I have the following jsFiddle and the html output is as follows.
<div ng-app="test" ng-controller="About" class="hex-grid clearfix ng-scope">
<div class="hex odd" ng-repeat="person in ledning">
<a href="">
<div class="inner">
<h3 class="ng-binding">Just a name</h3>
<hr class="grid-1 center">
<p class="ng-binding">a title</p>
</div>
<div class="hex-1">
<span class="after"></span>
</div>
<div class="hex-2">
<span class="after"></span>
</div>
<span class="after"></span>
</a>
</div>
<!-- and 5 times more -->
</div>
Now, What I want to achieve is this -->http://jsfiddle.net/engstrumpan/yCv79/4/embedded/result/.
This is just plain html (no AngularJs) just to demonstrate what I want to achieve. This particular layout was achieved by inserting <br /> after hexagon 3 and 5
Imagine now if I want a layout like so
1 1 1 1
1 1 1 1 or even 1 1
1 1 1 1 1 1
1 1 1 1
1 1 1
1 1
1
How would one go about implementing this?
This directive is used multiple times so I want it to be as generic as possible.
What I've tried so far is is the following
var app = angular.module('test', []);
app.controller('About', function ($scope) {
$scope.ledning = [
{
...
shouldBreak: true
}
});
app.directive('hexagon', function () {
var tmpl = '<div>
<!-- directive template snippet -->
</div>
<br ng-show="{{data.shouldBreak}}" />';
// rest of code
});
This <br ng-show="{{data.shouldBreak}}" /> won't work since AngularJs throws an exception with the following message Template must have exactly one root element.
Also messing with compile on the directive like so but that results only in the <br /> being inserted after the very last iteration (after the repeater is done working)
var app = angular.module('test', []);
app.directive('hexagon', function () {
return {
...
compile: function($scope, el, attrs){
return function ($scope, el, attrs){
if($scope.data.shouldBreak !== undefined){
el.after('<br />');
}
};
}
}
});
I've read the documentation when it comes to directives and either I'm just stupid or I'm missing the obvious so please help me out.
End of walloftext and maybe some sleep will do the trick. Jump back on the horse tomorrow so to speak.
So I managed to come to a solution after being away for a couple of days and then when I returned to town I tackled my problem again.
The solution
The view
<hexagon ng-repeat="person in ledning" data="person" layout="layout"></hexagon>
The controller:
var app = angular.module('test', []);
app.controller('About', function ($scope) {
...
$scope.layout = [2,4]; // what index (inside ngRepeat) should we break on
}
});
and the directive
app.directive('hexagon', function ($timeout) {
...
return {
...
scope: {
data: '=',
layout: '=' // grab the data bound to 'layout'
},
link: function ($scope, el, attrs) {
$timeout(function(){
for (var i = 0; i < $scope.layout.length; i++) {
if ($scope.layout[i] == $scope.$parent.$index)
el.after('<br />');
}
}, 0);
}
};
});
A working example can be seen here http://jsfiddle.net/engstrumpan/yCv79/5/.
So if I for instance were to work with 10 items I could set $scope.layout = [2,6] to get the following output in this jsFiddle
So how did I end up with this solution? Well I have to thank this guy (Lorenz Merdian) and his blog for it. At least when it comes down tho the use of $timeout to give the browser enough time to render the DOM for the element so I could insert my <br />
It might not be the most elegant solution but it works for me in this particular scenario
You could use pseudoclasses to insert a carriage return "\a" either before :before or after :after the element in question (although if you are supporting legacy browsers, this ain't for you).
CSS:
.last:before {
content: "\A";
white-space:pre;
}
And a conditional ng-class to set the line-breaking class, where item.lineBreak contained true or false depending on its order in the group:
ng-class="{last:item.lineBreak}"
That is the basic answer, and here's the working plunk with the ng-class on the element.
Fork of the plunk with the ng-class on the root element in the template. Same result.
..
..
The tricky/fun part is being able to dynamically insert that class/pseudoclass into the correct elements without hardcoding them. It looked like you wanted a pyramid (Fibonacci wannabe) design (which is what looked interesting about this question).
What I did was use a pretty basic looping process to insert the true property - (from the above example item.lineBreak) - into the items which were the 1st, 3rd, 6th, 10th, (and on) items in the ng-repeat. I put this in a $watch of a select-box model so you could choose the number of items in the group.
Here's the $watch and
var findFib = function(index){
if(index==increment){
rowLength += 1;
increment = rowLength + increment;
return true;
}
return false;
}
$scope.$watch('collectionAmount.value', function(val){
$scope.collection = [];
rowLength = 1;
increment = 1;
var i = 1;
//loop through the items, using the Fibonacci-like
//method to determine which items will recieve the
//lineBreak property of true;
while(i<=val){
var f = findFib(i);
$scope.collection.push({number: i, lineBreak: f});
console.log($scope.collection[i-1]);
i++;
}
//reverse the array if using :before pseudoclass to get upside-down pyramid:
$scope.collection.reverse();
})
From there, the ng-class conditionally applied the .last class which contained the :before pseudoclass that inserted the return. This could easily be scaled to fit your more complex markup.

inline conditionals in angular.js

I was wondering if there is a way in angular to conditionally display content other than using ng-show etc. For example in backbone.js I could do something with inline content in a template like:
<% if (myVar === "two") { %> show this<% } %>
but in angular, I seem to be limited to showing and hiding things wrapped in html tags
<p ng-hide="true">I'm hidden</p>
<p ng-show="true">I'm shown</p>
What is the recommended way in angular to conditionally show and hide inline content in angular just using {{}} rather than wrapping the content in html tags?
Angular 1.1.5 added support for ternary operators:
{{myVar === "two" ? "it's true" : "it's false"}}
EDIT: 2Toad's answer below is what you're looking for! Upvote that thing
If you're using Angular <= 1.1.4 then this answer will do:
One more answer for this. I'm posting a separate answer, because it's more of an "exact" attempt at a solution than a list of possible solutions:
Here's a filter that will do an "immediate if" (aka iif):
app.filter('iif', function () {
return function(input, trueValue, falseValue) {
return input ? trueValue : falseValue;
};
});
and can be used like this:
{{foo == "bar" | iif : "it's true" : "no, it's not"}}
Thousands of ways to skin this cat. I realize you're asking about between {{}} speifically, but for others that come here, I think it's worth showing some of the other options.
function on your $scope (IMO, this is your best bet in most scenarios):
app.controller('MyCtrl', function($scope) {
$scope.foo = 1;
$scope.showSomething = function(input) {
return input == 1 ? 'Foo' : 'Bar';
};
});
<span>{{showSomething(foo)}}</span>
ng-show and ng-hide of course:
<span ng-show="foo == 1">Foo</span><span ng-hide="foo == 1">Bar</span>
ngSwitch
<div ng-switch on="foo">
<span ng-switch-when="1">Foo</span>
<span ng-switch-when="2">Bar</span>
<span ng-switch-default>What?</span>
</div>
A custom filter as Bertrand suggested. (this is your best choice if you have to do the same thing over and over)
app.filter('myFilter', function() {
return function(input) {
return input == 1 ? 'Foo' : 'Bar';
}
}
{{foo | myFilter}}
Or A custom directive:
app.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
link: function(scope, elem, attrs) {
scope.$watch(attrs.value, function(v) {
elem.text(v == 1 ? 'Foo': 'Bar');
});
}
};
});
<my-directive value="foo"></my-directive>
Personally, in most cases I'd go with a function on my scope, it keeps the markup pretty clean, and it's quick and easy to implement. Unless, that is, you're going to be doing the same exact thing over and over again, in which case I'd go with Bertrand's suggestion and create a filter or possibly a directive, depending on the circumstances.
As always, the most important thing is that your solution is easy to maintain, and is hopefully testable. And that is going to depend completely on your specific situation.
I am using the following to conditionally set the class attr when ng-class can't be used (for example when styling SVG):
ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"
The same approach should work for other attribute types.
(I think you need to be on latest unstable Angular to use ng-attr-, I'm currently on 1.1.4)
I have published an article on working with AngularJS+SVG that talks about this and related issues. http://www.codeproject.com/Articles/709340/Implementing-a-Flowchart-with-SVG-and-AngularJS
For checking a variable content and have a default text, you can use:
<span>{{myVar || 'Text'}}</span>
If I understood you well I think you have two ways of doing it.
First you could try ngSwitch and the second possible way would be creating you own filter. Probably ngSwitch is the right aproach but if you want to hide or show inline content just using {{}} filter is the way to go.
Here is a fiddle with a simple filter as an example.
<div ng-app="exapleOfFilter">
<div ng-controller="Ctrl">
<input ng-model="greeting" type="greeting">
<br><br>
<h1>{{greeting|isHello}}</h1>
</div>
</div>
angular.module('exapleOfFilter', []).
filter('isHello', function() {
return function(input) {
// conditional you want to apply
if (input === 'hello') {
return input;
}
return '';
}
});
function Ctrl($scope) {
$scope.greeting = 'hello';
}
Angular UI library has built-in directive ui-if for condition in template/Views upto angular ui 1.1.4
Example:
Support in Angular UI upto ui 1.1.4
<div ui-if="array.length>0"></div>
ng-if available in all the angular version after 1.1.4
<div ng-if="array.length>0"></div>
if you have any data in array variable then only the div will appear
if you want to display "None" when value is "0", you can use as:
<span> {{ $scope.amount === "0" ? $scope.amount : "None" }} </span>
or true false in angular js
<span> {{ $scope.amount === "0" ? "False" : "True" }} </span>
So with Angular 1.5.1 ( had existing app dependency on some other MEAN stack dependencies is why I'm not currently using 1.6.4 )
This works for me like the OP saying {{myVar === "two" ? "it's true" : "it's false"}}
{{vm.StateName === "AA" ? "ALL" : vm.StateName}}
Works even in exotic Situations:
<br ng-show="myCondition == true" />
I'll throw mine in the mix:
https://gist.github.com/btm1/6802312
this evaluates the if statement once and adds no watch listener BUT you can add an additional attribute to the element that has the set-if called wait-for="somedata.prop" and it will wait for that data or property to be set before evaluating the if statement once. that additional attribute can be very handy if you're waiting for data from an XHR request.
angular.module('setIf',[]).directive('setIf',function () {
return {
transclude: 'element',
priority: 1000,
terminal: true,
restrict: 'A',
compile: function (element, attr, linker) {
return function (scope, iterStartElement, attr) {
if(attr.waitFor) {
var wait = scope.$watch(attr.waitFor,function(nv,ov){
if(nv) {
build();
wait();
}
});
} else {
build();
}
function build() {
iterStartElement[0].doNotMove = true;
var expression = attr.setIf;
var value = scope.$eval(expression);
if (value) {
linker(scope, function (clone) {
iterStartElement.after(clone);
clone.removeAttr('set-if');
clone.removeAttr('wait-for');
});
}
}
};
}
};
});
More recent angular, 6+ I think. You have ng-template with the else conditional hooking up to a tag identifier:
<div *ngIf="myVar else myVarNo">Yes</div>
<ng-template #myVarNo><div>No</div></ng-template>

Math functions in AngularJS bindings

Is there a way to use math functions in AngularJS bindings?
e.g.
<p>The percentage is {{Math.round(100*count/total)}}%</p>
This fiddle shows the problem
http://jsfiddle.net/ricick/jtA99/1/
You have to inject Math into your scope, if you need to use it as
$scope know nothing about Math.
Simplest way, you can do
$scope.Math = window.Math;
in your controller.
Angular way to do this correctly would be create a Math service, I guess.
While the accepted answer is right that you can inject Math to use it in angular, for this particular problem, the more conventional/angular way is the number filter:
<p>The percentage is {{(100*count/total)| number:0}}%</p>
You can read more about the number filter here: http://docs.angularjs.org/api/ng/filter/number
I think the best way to do it is by creating a filter, like this:
myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});
then the markup looks like this:
<p>The percentage is {{ (100*count/total) | ceil }}%</p>
Updated fiddle: http://jsfiddle.net/BB4T4/
This is a hairy one to answer, because you didn't give the full context of what you're doing. The accepted answer will work, but in some cases will cause poor performance. That, and it's going to be harder to test.
If you're doing this as part of a static form, fine. The accepted answer will work, even if it isn't easy to test, and it's hinky.
If you want to be "Angular" about this:
You'll want to keep any "business logic" (i.e. logic that alters data to be displayed) out of your views. This is so you can unit test your logic, and so you don't end up tightly coupling your controller and your view. Theoretically, you should be able to point your controller at another view and use the same values from the scopes. (if that makes sense).
You'll also want to consider that any function calls inside of a binding (such as {{}} or ng-bind or ng-bind-html) will have to be evaluated on every digest, because angular has no way of knowing if the value has changed or not like it would with a property on the scope.
The "angular" way to do this would be to cache the value in a property on the scope on change using an ng-change event or even a $watch.
For example with a static form:
angular.controller('MainCtrl', function($scope, $window) {
$scope.count = 0;
$scope.total = 1;
$scope.updatePercentage = function () {
$scope.percentage = $window.Math.round((100 * $scope.count) / $scope.total);
};
});
<form name="calcForm">
<label>Count <input name="count" ng-model="count"
ng-change="updatePercentage()"
type="number" min="0" required/></label><br/>
<label>Total <input name="total" ng-model="total"
ng-change="updatePercentage()"
type="number" min="1" required/></label><br/>
<hr/>
Percentage: {{percentage}}
</form>
And now you can test it!
describe('Testing percentage controller', function() {
var $scope = null;
var ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
}));
it('should calculate percentages properly', function() {
$scope.count = 1;
$scope.total = 1;
$scope.updatePercentage();
expect($scope.percentage).toEqual(100);
$scope.count = 1;
$scope.total = 2;
$scope.updatePercentage();
expect($scope.percentage).toEqual(50);
$scope.count = 497;
$scope.total = 10000;
$scope.updatePercentage();
expect($scope.percentage).toEqual(5); //4.97% rounded up.
$scope.count = 231;
$scope.total = 10000;
$scope.updatePercentage();
expect($scope.percentage).toEqual(2); //2.31% rounded down.
});
});
Why not wrap the whole math obj in a filter?
var app = angular.module('fMathFilters',[]);
function math() {
return function(input,arg) {
if(input) {
return Math[arg](input);
}
return 0;
}
}
return app.filter('math',[math]);
and to use:
{{ number_var | math:'ceil'}}
Better option is to use :
{{(100*score/questionCounter) || 0 | number:0}}
It sets default value of equation to 0 in the case when values are not initialized.
If you're looking to do a simple round in Angular you can easily set the filter inside your expression. For example:
{{ val | number:0 }}
See this CodePen example & for other number filter options.
Angular Docs on using Number Filters
The easiest way to do simple math with Angular is directly in the HTML markup for individual bindings as needed, assuming you don't need to do mass calculations on your page. Here's an example:
{{(data.input/data.input2)| number}}
In this case you just do the math in the () and then use a filter | to get a number answer. Here's more info on formatting Angular numbers as text:
https://docs.angularjs.org/api/ng/filter
Either bind the global Math object onto the scope (remember to use $window not window)
$scope.abs = $window.Math.abs;
Use the binding in your HTML:
<p>Distance from zero: {{abs(distance)}}</p>
Or create a filter for the specific Math function you're after:
module.filter('abs', ['$window', function($window) {
return function(n) {
return $window.Math.abs($window.parseInt(n));
};
});
Use the filter in your HTML:
<p>Distance from zero: {{distance | abs}}</p>
That doesn't look like a very Angular way of doing it. I'm not entirely sure why it doesn't work, but you'd probably need to access the scope in order to use a function like that.
My suggestion would be to create a filter. That's the Angular way.
myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});
Then in your HTML do this:
<p>The percentage is {{ (100*count/total) | ceil }}%</p>
The number filter formats the number with thousands separators, so it's not strictly a math function.
Moreover, its decimal 'limiter' doesn't ceil any chopped decimals (as some other answers would lead you to believe), but rather rounding them.
So for any math function you want, you can inject it (easier to mock than injecting the whole Math object) like so:
myModule.filter('ceil', function () {
return Math.ceil;
});
No need to wrap it in another function either.
This is more or less a summary of three answers (by Sara Inés Calderón, klaxon and Gothburz), but as they all added something important, I consider it worth joining the solutions and adding some more explanation.
Considering your example, you can do calculations in your template using:
{{ 100 * (count/total) }}
However, this may result in a whole lot of decimal places, so using filters is a good way to go:
{{ 100 * (count/total) | number }}
By default, the number filter will leave up to three fractional digits, this is where the fractionSize argument comes in quite handy
({{ 100 * (count/total) | number:fractionSize }}), which in your case would be:
{{ 100 * (count/total) | number:0 }}
This will also round the result already:
angular.module('numberFilterExample', [])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.val = 1234.56789;
}
]);
<!doctype html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="numberFilterExample">
<table ng-controller="ExampleController">
<tr>
<td>No formatting:</td>
<td>
<span>{{ val }}</span>
</td>
</tr>
<tr>
<td>3 Decimal places:</td>
<td>
<span>{{ val | number }}</span> (default)
</td>
</tr>
<tr>
<td>2 Decimal places:</td>
<td><span>{{ val | number:2 }}</span></td>
</tr>
<tr>
<td>No fractions: </td>
<td><span>{{ val | number:0 }}</span> (rounded)</td>
</tr>
</table>
</body>
</html>
Last thing to mention, if you rely on an external data source, it probably is good practise to provide a proper fallback value (otherwise you may see NaN or nothing on your site):
{{ (100 * (count/total) | number:0) || 0 }}
Sidenote: Depending on your specifications, you may even be able to be more precise with your fallbacks/define fallbacks on lower levels already (e.g. {{(100 * (count || 10)/ (total || 100) | number:2)}}). Though, this may not not always make sense..
Angular Typescript example using a pipe.
math.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'math',
})
export class MathPipe implements PipeTransform {
transform(value: number, args: any = null):any {
if(value) {
return Math[args](value);
}
return 0;
}
}
Add to #NgModule declarations
#NgModule({
declarations: [
MathPipe,
then use in your template like so:
{{(100*count/total) | math:'round'}}

Resources