AngularJS ngClass conditional - html

Is there any way to make an expression for something like ng-class to be a conditional?
For example, I have tried the following:
<span ng-class="{test: 'obj.value1 == \'someothervalue\''}">test</span>
The issue with this code is that no matter what obj.value1 is, the class test is always applied to the element. Doing this:
<span ng-class="{test: obj.value2}">test</span>
As long as obj.value2 does not equal a truthy value, the class in not applied. Now I can work around the issue in the first example by doing this:
<span ng-class="{test: checkValue1()}">test</span>
Where the checkValue1 function looks like this:
$scope.checkValue1 = function() {
return $scope.obj.value === 'somevalue';
}
I am just wondering if this is how ng-class is supposed to work. I am also building a custom directive where I would like to do something similar to this. However, I can't find a way to watch an expression (and maybe that is impossible and the reason why it works like this).
Here is a plnkr to show what I mean.

Your first attempt was almost right, It should work without the quotes.
{test: obj.value1 == 'someothervalue'}
Here is a plnkr.
The ngClass directive will work with any expression that evaluates truthy or falsey, a bit similar to Javascript expressions but with some differences, you can read about here.
If your conditional is too complex, then you can use a function that returns truthy or falsey, as you did in your third attempt.
Just to complement: You can also use logical operators to form logical expressions like
ng-class="{'test': obj.value1 == 'someothervalue' || obj.value2 == 'somethingelse'}"

Using ng-class inside ng-repeat
<table>
<tbody>
<tr ng-repeat="task in todos"
ng-class="{'warning': task.status == 'Hold' , 'success': task.status == 'Completed',
'active': task.status == 'Started', 'danger': task.status == 'Pending' } ">
<td>{{$index + 1}}</td>
<td>{{task.name}}</td>
<td>{{task.date|date:'yyyy-MM-dd'}}</td>
<td>{{task.status}}</td>
</tr>
</tbody>
</table>
For each status in task.status a different class is used for the row.

Angular JS provide this functionality in ng-class Directive. In which you can put condition and also assign conditional class. You can achieve this in two different ways.
Type 1
<div ng-class="{0:'one', 1:'two',2:'three'}[status]"></div>
In this code class will be apply according to value of status value
if status value is 0 then apply class one
if status value is 1 then apply class two
if status value is 2 then apply class three
Type 2
<div ng-class="{1:'test_yes', 0:'test_no'}[status]"></div>
In which class will be apply by value of status
if status value is 1 or true then it will add class test_yes
if status value is 0 or false then it will add class test_no

I see great examples above but they all start with curly brackets (json map). Another option is to return a result based on computation. The result can also be a list of css class names (not just map). Example:
ng-class="(status=='active') ? 'enabled' : 'disabled'"
or
ng-class="(status=='active') ? ['enabled'] : ['disabled', 'alik']"
Explanation: If the status is active, the class enabled will be used. Otherwise, the class disabled will be used.
The list [] is used for using multiple classes (not just one).

There is a simple method which you could use with html class attribute and shorthand if/else. No need to make it so complex. Just use following method.
<div class="{{expression == true ? 'class_if_expression_true' : 'class_if_expression_false' }}">Your Content</div>

I am going to show you two methods by which you can dynamically apply ng-class
Step-1
By using ternary operator
<div ng-class="condition?'class1':'class2'"></div>
Output
If your condition is true then class1 will be applied to your element else class2 will be applied.
Disadvantage
When you will try to change the conditional value at run time the class somehow will not changed. So I will suggest you to go for step2 if you have requirement like dynamic class change.
Step-2
<div ng-class="{value1:'class1', value2:'class2'}[condition]"></div>
Output
if your condition matches with value1 then class1 will be applied to your element, if matches with value2 then class2 will be applied and so on. And dynamic class change will work fine with it.
Hope this will help you.

Angular syntax is to use the : operator to perform the equivalent of an if modifier
<div ng-class="{ 'clearfix' : (row % 2) == 0 }">
Add clearfix class to even rows. Nonetheless, expression could be anything we can have in normal if condition and it should evaluate to either true or false.

Using function with ng-class is a good option when someone has to run complex logic to decide the appropriate CSS class.
http://jsfiddle.net/ms403Ly8/2/
HTML:
<div ng-app>
<div ng-controller="testCtrl">
<div ng-class="getCSSClass()">Testing ng-class using function</div>
</div>
</div>
CSS:
.testclass { Background: lightBlue}
JavaScript:
function testCtrl($scope) {
$scope.getCSSClass = function() {
return "testclass ";
}
}

For Angular 2, use this
<div [ngClass]="{'active': dashboardComponent.selected_menu == 'mapview'}">Content</div>

use this
<div ng-class="{states}[condition]"></div>
for example if the condition is [2 == 2], states are {true: '...', false: '...'}
<div ng-class="{true: 'ClassA', false: 'ClassB'}[condition]"></div>

ng-class is a Directive of core AngularJs. In which you can use "String Syntax", "Array Syntax", "Evaluated Expression", " Ternary Operator" and many more options described below:
ngClass Using String Syntax
This is the simplest way to use ngClass. You can just add an Angular variable to
ng-class and that is the class that will be used for that element.
<!-- whatever is typed into this input will be used as the class for the div below -->
<input type="text" ng-model="textType">
<!-- the class will be whatever is typed into the input box above -->
<div ng-class="textType">Look! I'm Words!
Demo Example of ngClass Using String Syntax
ngClass Using Array Syntax
This is similar to the string syntax method except you are able to apply multiple classes.
<!-- both input boxes below will be classes for the div -->
<input type="text" ng-model="styleOne">
<input type="text" ng-model="styleTwo">
<!-- this div will take on both classes from above -->
<div ng-class="[styleOne, styleTwo]">Look! I'm Words!
ngClass Using Evaluated Expression
A more advanced method of using ngClass (and one that you will probably use the most) is to evaluate an expression. The way this works is that if a variable or expression evaluates to true, you can apply a certain class. If not, then the class won't be applied.
<!-- input box to toggle a variable to true or false -->
<input type="checkbox" ng-model="awesome"> Are You Awesome?
<input type="checkbox" ng-model="giant"> Are You a Giant?
<!-- add the class 'text-success' if the variable 'awesome' is true -->
<div ng-class="{ 'text-success': awesome, 'text-large': giant }">
Example of ngClass Using Evaluated Expression
ngClass Using Value
This is similar to the evaluated expression method except you just able to compares multiple values with the only variable.
<div ng-class="{value1:'class1', value2:'class2'}[condition]"></div>
ngClass Using the Ternary Operator
The ternary operator allows us to use shorthand to specify two different classes, one if an expression is true and one for false. Here is the basic syntax for the ternary operator:
ng-class="$variableToEvaluate ? 'class-if-true' : 'class-if-false'">
Evaluating First, Last or Specific Number
If you are using the ngRepeat directive and you want to apply classes to the first, last, or a specific number in the list, you can use special properties of ngRepeat. These include $first, $last, $even, $odd, and a few others. Here's an example of how to use these.
<!-- add a class to the first item -->
<ul>
<li ng-class="{ 'text-success': $first }" ng-repeat="item in items">{{ item.name }}</li>
</ul>
<!-- add a class to the last item -->
<ul>
<li ng-class="{ 'text-danger': $last }" ng-repeat="item in items">{{ item.name }}</li>
</ul>
<!-- add a class to the even items and a different class to the odd items -->
<ul>
<li ng-class="{ 'text-info': $even, 'text-danger': $odd }" ng-repeat="item in items">{{ item.name }}</li>
</ul>

Related

Conditionally Display Element in Angular 7

I have a <select> element in my Angular component that, based on the selected item, returns the identifier of that item.
I want to know how I would be able to conditionally display a <div> when the identifier is either not undefinded or 0.
I have the following code
<div *ngIf="this.id != undefined || this.id != null"></div>
However, it still display's the element even though the condition should theoretically be satisfied as, at the point of the <select> not having a value, should be undefined.
Are there any suggestions that would conditionally display an element using the *ngIf directive based on an id returned from a <select>?
It depends on how the select is set up. For eg., if it's of the following form
<select #sel (change)="change(sel.value)" [(ngModel)]="id">
<option [ngValue]="undefined">Undefined</option>
<option *ngFor="let option of options" [ngValue]="option">{{ option }}</option>
</select>
with options = [ 0, 1, 2, 3, 4, 5 ];
And if id is declared as id: any; in the controller, then explicit checks to undefined and null isn't required. You could do null check just with the following
<div *ngIf="id && id !== 0; else undefinedBlock">
ID is defined: {{ id }}
</div>
<ng-template #undefinedBlock>
ID is undefined
</ng-template>
Use ng-template when you want to show something depending on some condition. See example
Instead of:
<div *ngIf="isDisplayed">Item 1</div>
<div *ngIf="!isDisplayed">Item 2</div>
You can do this:
<div *ngIf="isDisplayed; else showItem2">Item 1</div>
<ng-template #showItem2>
Item 2
</ng-template>
I want to know how I would be able to conditionally display a <div>
when the identifier is either undefinded or 0.
First of all make sure this is not undefined in all cases. then you can use sth like this :
<div *ngIf="this.id"></div>
Look at this fiddle which you can manually set id to null or remove it (e.g undefined)

How to display saved filters in headers with PrimeNG?

I am able to save the filters locally using stateStorage="local" stateKey="myKey". So when the user leaves the component and returns, the data is still filtered based upon whatever filters they have set.
The problem is, the user has no idea what they are filtering on anymore, as the filter headers do not show them anything anymore, just the default label. I can access the filters via local storage, and delete them using localStorage.removeItem("myKey");, but I cannot for the life of me figure out how to get this filter information to display in the filter headers. We are not using lazyLoad, as suggested in another answer. You'd think this would be built in any time a filter is saved because not knowing what you are filtering on seems like a major flaw.
For more clarity, I have attached the primeFaces documentation below. If you select 'Red' 'White' and 'Green' from the multiselect dropdown, it will display your selected filter in the header (Red, White, Green) above. I need this information to display anytime the user enters the component if they have filters saved (both with text input, and with the dropdowns).
https://www.primefaces.org/primeng/#/table/filter
I am using multi-select dropdown filters, text input, as well as calendar filters. Here is a snippet of the html, which includes examples of these three filter types:
<th *ngFor="let col of columns" [ngSwitch]="col.field">
<input *ngSwitchCase="'userID'" pInputText type="text" size="12" placeholder="contains" (input)="table.filter($event.target.value, col.field, col.filterMatchMode)" [value]="table.filters['userID'] ? table.filters['userID'].value : ''">
<div class="ui-g ui-fluid">
<p-calendar #calendar1 class="ui-fluid" *ngSwitchCase="'myDate'" [monthNavigator]="true" [showOnFocus]="false" [yearNavigator]="true" yearRange="2010:2060" [showIcon]="true"
[showOtherMonths]="false" [showButtonBar]="true" [appendTo]="attach" [style]="{'overflow': 'visible'}"
[(ngModel)]="calendar1Filter"
(ngModelChange)="table.filter($event, 'myDate', calendar1Option)"
(onSelect)="table.filter($event, 'myDate', calendar1Option)">
<p-footer>
<div class="ui-grid-row">
<div class="ui-grid-col-3"><label style="font-weight: bold; color: #337ab7">Mode:</label></div>
<div class="ui-grid-col-6"><p-dropdown [options]="calendar1Options" [style]="{'width':'60px', 'padding-top': '0px'}" [(ngModel)]="calendar1Option" (onChange)="onChangeModCalOpt(calendar1, 1)" ></p-dropdown> </div>
</div>
</p-footer>
</p-calendar>
</div>
<div class="ui-fluid">
<p-multiSelect *ngSwitchCase="'myDropdown'" appendTo="body" [options]="dropdownOptions" pInputText type="text" size="12" [style]="{'width':'100%'}" defaultLabel="All" [(ngModel)]="myDropdownFilter" (onChange)="table.filter($event.value, col.field, 'in')"></p-multiSelect>
</div>
</th>
I did this almost a year ago; it involved some tricky code, as you'll see below, because the table element was beneath an *ngIf directive. I'm not sure if your case is the same, but if it is, here's what I had to to do get it to work. In the example, I have a fooTable that has a custom filter on the status column.
foo.component.ts:
import { ChangeDetectorRef, Component, ViewChild } from "#angular/core";
#Component({
selector : 'foo',
templateUrl : 'foo.component.html'
})
export class FooComponent {
// members /////////////////////////////////////////////////////////////////////////////////////////////////////////
showFooTable: boolean = true;
statusFilter: string[] = [];
// constructor(s) //////////////////////////////////////////////////////////////////////////////////////////////////
constructor(private cd: ChangeDetectorRef) { }
// getters and setters /////////////////////////////////////////////////////////////////////////////////////////////
/**
* Due to the Angular lifecycle, we have to do some tricky things here to pull the filters from the session,
* if they are present. The workarounds done in this function are as follows:
*
* 1) Wait until the element is accessible. This is not until the *ngIf is rendered, which is the second call to
* this function. The first call is simply 'undefined', which is why we check for that and ignore it.
* 2) Check and see if the filters for this object are even part of the template. If not, just skip this step.
* 3) If we find the filters in the session, then change this object's filters model to equal it and call the change
* detection manually to prevent Angular from throwing an ExpressionChangedAfterItHasBeenCheckedError error
* #param fooTable the reference to the foo table template
*/
#ViewChild('fooTable') set fooTable(fooTable: any) {
if(fooTable != undefined) {
let filters = fooTable.filters['status'];
if (filters != undefined && filters.value != undefined) {
this.statusFilter = filters.value;
}
this.cd.detectChanges();
}
}
}
foo.component.html:
<ng-container *ngIf="showFooTable">
<div id="filters">
<p-checkbox [(ngModel)]="statusFilter" value="up" (onChange)="fooTable.filter(statusFilter, 'status', 'in')"></p-checkbox> Up
<p-checkbox [(ngModel)]="statusFilter" value="down" (onChange)="fooTable.filter(statusFilter, 'status', 'in')"></p-checkbox> Down
<p-checkbox [(ngModel)]="statusFilter" value="unknown" (onChange)="fooTable.filter(statusFilter, 'status', 'in')"></p-checkbox> Unknown
</div>
<p-table #fooTable
stateStorage="session"
stateKey="foo-table-state">
</p-table>
</ng-container>
Turbo tables filters can be access like so. table.filters['myColumn']?.value. You will need to set the input values in the header, [value]="table.filters[col.field]?.value"
...
<tr>
<th *ngFor="let col of columns" [ngSwitch]="col.field" class="ui-fluid">
<input pInputText
type="text"
(input)="table.filter($event.target.value, col.field, col.filterMatchMode)"
[value]="table.filters[col.field]?.value">
</th>
</tr>
...
https://www.primefaces.org/primeng/#/table/state
Just figured this out. What I ended up doing was attaching the default label to a model like so:
<p-multiSelect [defaultLabel]="table.filters[col.field]?.value || 'All'">
If the table has the filter value in its state, it will populate the label else default to 'All'.

ANGULARJS: Function I have in ng-checked directive runs infinitely

Trying to do something I thought is pretty simple but it's turning out to be pretty annoying. I'm just trying to have a function that runs when you click on a checkbox using the ng-checked directive.
This is the HTML:
<div class="form-group">
<label class="col-sm-2 control-label">Make Payment Optional</label>
<div class="col-sm-4 center-checkbox">
<input type="checkbox"
class="center-checkbox"
ng-model="formData.optionalPayment"
ng-checked="optionalPaymentCheckbox();"
validate-servererror="featured"/>
</div>
</div>
And this is the Angular:
if($scope.formData.optionalPayment === undefined) {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.OPTIONAL;
}
(This check is just for when I load the page for the first time.)
$scope.optionalPaymentCheckbox = function () {
if($scope.formData.optionalPayment === TournamentConst.PAYMENT.OPTIONAL) {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.MANDATORY;
} else {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.OPTIONAL;
}
};
When I load the page, this ng-checked function runs infinitely. Is there something about the ng-checked directive I don't know, or some minor detail or forgot? Thanks in advance.
You are misunderstanding the intention of ng-checked. What you think it does is "execute this expression when the checkbox is checked" - an event handler directive.
What it actually does is set the checked property based on the expression. This means it sets up a watch on the expression and evaluates it every digest. If the value changes, it sets or unsets the checked property accordingly.
In fact, the documentation for ng-checked says this:
Note that this directive should not be used together with ngModel, as this can lead to unexpected behavior.
As #JB Nizet correctly pointed out, you can achieve the desired effect in your particular case by using ng-true-value and ng-false-value and removing ng-checked altogether.
So your HTML becomes:
<div class="form-group">
<label class="col-sm-2 control-label">Make Payment Optional</label>
<div class="col-sm-4 center-checkbox">
<input type="checkbox"
class="center-checkbox"
ng-model="formData.optionalPayment"
ng-true-value="TournamentConst.PAYMENT.MANDATORY"
ng-false-value="TournamentConst.PAYMENT.OPTIONAL"
validate-servererror="featured"/>
</div>
</div>
Then, in your controller, populate your TournamentConst object in the scope, so the template can see it:
$scope.TournamentConst = TournamentConst;
(or you can just populate the bits you need)
Finally, get rid of the whole $scope.optionalPaymentCheckbox function. You will still need the code to set the default value, though.
One last thing: It is confusing that the property is called optionalPayment, when it is really more like paymentType, but that is not related to the current problem.

angular2 custom directives inputs syntax

I create a custom directive and set the selector value to be "[unless-directive]".
The directive get a Boolean and use it to change the view as so:
import {Directive, TemplateRef, ViewContainerRef} from 'angular2/core';
#Directive({
selector: '[unless-directive]',
inputs: ['givenBoolean : myDirectiveFunction']
})
export class UnlessDirective {
private _templateRef: TemplateRef;
private _viewContainerRef: ViewContainerRef;
constructor(_templateRef: TemplateRef, _viewContainerRef: ViewContainerRef) {
this._templateRef = _templateRef;
this._viewContainerRef = _viewContainerRef;
}
set myDirectiveFunction(condition: boolean) {
!condition ? this._viewContainerRef.createEmbeddedView(this._templateRef)
: this._viewContainerRef.clear();
}
}
In my template I tried to pass the condition like so:
<div name="customDirective">
<h2>Custom Directive</h2>
<div>
Enter true or false:
<br/>
<input type="text" #condition (keyup)="0"/>
<div *unless-directive [givenBoolean]="condition.value != 'false'">
Only shown if 'false' wad enterded!
</div>
</div>
</div>
When I running the code I get this error:
EXCEPTION: Template parse errors: Can't bind to 'givenBoolean' since
it isn't a known native property (" ... Only shown if 'false' wad enterded!"): StructualDirectivesComponent#47:39
I guess my syntax is wrong, but I can't find where or why?
I looked it up on Angular2 Docs, but the example use the same name for the input and the selector, the thing that I'm trying to avoid.
Can anyone know a better way or can find my syntax problem?
Thanks.
The * prefix syntax is only a syntatic sugar. It expands the directive declaration.
The * prefix syntax is a convenient way to skip the <template> wrapper tags and focus directly on the HTML element to repeat or include. Angular sees the * and expands the HTML into the <template> tags for us.
This is documented in * and <template> and Directive decorator/Lifecycle hooks.
So, in your case, the [givenBoolean] property is not expected to be in the directive. In other words, this:
<div *unless-directive [givenBoolean]="condition.value != 'false'">
Only shown if 'false' wad enterded!
</div>
Becomes, actually:
<template [unless-directive]="">
<div [givenBoolean]="condition.value != 'false'">
Only shown if 'false' wad enterded!
</div>
</template>
And since givenBoolean is not a property in the component (not the directive), the error appears.
So if you want custom behavior, I suggest you experiment using the expanded version and only after it works you go to the * syntax, it will be simpler to reason about.

Show element if observable value is null

Is it possible to use the Knockout's visible: or if: data bindings to check to see if an observable's value is (explicitly) null?
I've got two radio buttons, and if either one is checked, it sets an observable's value to either "True" or "False". Otherwise the observable's value is null. I'd like an element to conditionally display if the observable value is null. The following doesn't seem to work:
<div data-bind="visible: specificObservable === null"> Example </div>
<!-- shows the element when null, but not false, nor 'False' -->
Guessing that specificObservable is an observable, try:
<div data-bind="visible: specificObservable() === null"> Example </div>
You need to call the observable to get the actual value it contains. specificObservable is a function and therefore not null, even if the value it contains is null.
This is something that can trip you up in knockout because knockout will automatically unwrap observables if they are used by themselves. So if you did:
<div data-bind="visible: specificObservable"> Example </div>
And it will call specificObservable for you and be visible if specificObservable() is truthy. But once you start using it in a longer statement you need to explicitly unwrap it yourself.
Don't forget the closing double quote " after the call to function specificObservable()