Chain multiple expressions in AngularJS ng-class - javascript

I'm trying to chain two different expressions into an ng-class, one of them have a filter applied to it, something like:
<body ng-class="controller | camel2hyphens" ng-class="{overflow: isOverflow}">
But Angular only uses one of the them (which makes sense).
I've tried using an array of expression this way:
<body ng-class="[(controller | camel2hyphens), {overflow: isOverflow}]">
But the CSS class that is set is:
class="login-controller [object Object]"
Is there any way to accomplish this with ng-class (without writing a method in the Controller with the logic).
Thanks!

You can use class in combination with ng-class
<body class="{{controller | camel2hyphens}}" ng-class="{overflow: isOverflow}">

Seems like the way you tried to do it is only supported with Angular >=1.4.
For Angular 1.2 and 1.3:
If the expression evaluates to an array, each element of the array should be a string that is one or more space-delimited class names.
This way, your own suggestion was the best because all code stays inside of ng-class.
ng-class="[(controller | camel2hyphens), isOverflow ? 'overflow' : '']

Related

Using ternary operator notation for ng-class for multiple conditions [duplicate]

I am trying to combine the following two ng-class ternary expressions into one ng-class (so I can control both the color, via btn-success/danger classes, and the width, via width-small/medium/wide class, of my button divs
<div class="btn" ng-class="(rate > 0) ? 'btn-success' : 'btn-danger'">{{rate}}</div>
<div class="btn" ng-class="(priority == 'low') ? 'width-small' : (priority == 'medium') ? 'width-medium' : 'width-wide'">{{rate}}</div>
I realize I could just do something along the lines of below; however, I would like to make use of the ternary expressions available in 1.1.5
<div class="btn" ng-class="{'btn-success': rate > 0, 'btn-danger': rate <= 0, 'width-small': priority == 'low', 'width-medium': priority == 'medium', 'width-wide': prioity == 'high'}">{{rate}}</div>
Space separating the expressions did not work for me, nor did comma separating them within ng-class
Thanks advance for your assistance in determining if/how to have multiple ternary expressions in a single ng-class
For anyone looking for the answer: Yes this is indeed possible, also as mentioned the readability is questionable.
<div ng-class="(conversation.read ? 'read' : 'unread') + ' ' + (conversation.incoming ? 'in' : 'out')"></div>
From the ngClass documentation:
The directive operates in three different ways, depending on which of >three types the expression evaluates to:
If the expression evaluates to a string, the string should be one or >more space-delimited class names.
If the expression evaluates to an array, each element of the array >should >be a string that is one or more space-delimited class names.
If the expression evaluates to an object, then for each key-value pair of >the object with a truthy value the corresponding key is used as a class >name.
Option 1 applies here.
From my understanding using this method will result in 2 watches while using object notation will result in 4, but I might be wrong on this one.
I think you'd rather use a filter. You could write a rateToClass filter that would hold the logic and transform your rate in the class name that you want.
You could also put a rateToClass(rate) function in your scope, but it's ugly.
If you insist on having your logic in the view,
ng-class support multiple classes by using an object (see official doc first example, line 7):
<p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p>
You can have more complexe expression like ng-class='{btn-success: rate > 0}'.
It is possible to add multiple ternary expressions on a single ng-class directive by putting each of the expressions into an array, as items of this array.
Example :
<div ng-class="[
expression1 ? 'class1' : 'class2',
expression2 ? 'class3' : 'class4'
]">

How to use Angular structural directive with multiple inputs

I want to implement something similar with angular-permisssion. And with requirement to control the element's existance, I need to use angular structural directive.
At the beginning, i think such syntax would work:
<h2 *permissionIf [permissionIfExcept]="'Read'">Except</h2>
However, it doesn't work that way.
Moreover, the offical guide only teach you how to write custom structural directive with single input.
With multi-inputs, some third-party tutorials involve a bit. But that's using the angular template micro-syntax to achieve data binding. Then one problem occurs:
template syntax doesn't support pure key-value inputs:
<h2 *permissionIf="except: map.except;only: 'test'">Except</h2>
It expands into this(which is illegal):
<h2 template="permissionIf except: map.except;only: 'test'">Except</h2>
A stupid temporary solution is add a useless variable declaration.
<h2 *permissionIf="let i;except: map.except;only: 'test'">Except</h2>
Another inconvenient way is to use template element to wrap the code.
<template permissionIf [permissionIfExcept]="'Read'">
<h2>Except</h2>
</template>
The above all are not accepetable enough. But I can't find a bette way to resolve it.
Hope some guys can give some suggestion:).
The input names need all to be prefixed with the selector of the directive, followed by the input name capitalized (i.e. permissionIfExcept). Example:
#Directive({
selector: '[permissionIf]'
})
export class PermissionIfDirective implements AfterContentInit {
private _permissionIf:string[];
#Input()
set permissionIf(value: string[]) {
this._permissionIf=value;
console.log('permissionIf: ', value);
}
private _except:string;
#Input()
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
}
To use them with the '*' syntax:
<div *permissionIf="permissions;except:'Read'"></div>
Note here you're using the name following the prefix uncapitalized (i.e. except). Also note the : in the assignment.
The explicit syntax (using template) would look like this:
<template [permissionIf]="permissions" [permissionIfExcept]="'Read'">
</div></div>
</template>
but with <ng-container> it could look like
<ng-container *permissionIf="permissions;except:'Read'">
<div></div>
</ng-container>
Plunker example
See also the source of NgFor as an example.
#Günter Zöchbauer answer is almost correct.
Actually right now to make his answer working you need to explicitly rename the secondary #Input name.
So it should be:
#Input("permissionIfExcept")
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
Then one problem occurs: template syntax doesn't support pure key-value inputs:
True
A stupid temporary solution is add a useless variable declaration.
I think you are using this in a way it was not meant to be.
From the docs:
The microsyntax parser title-cases all directives and prefixes them with the directive's attribute name, such as ngFor. For example, the ngFor input properties, of and trackBy, become ngForOf and ngForTrackBy, respectively. That's how the directive learns that the list is heroes and the track-by function is trackById.
https://angular.io/guide/structural-directives#microsyntax-examples
Bottom line is in the context of your question, the microsyntax accepts "expression", followed by optional "keyed expression"s and I'm afraid those are your only options.
One could of course pass an object as the first expression—similar to ngIf—, the difference being you can teach your directive how to evaluate the expression:
*permissionIf="{ only: 'whatever', except: ['things', 'stuff'] }"

How to use ng-translate with variables resolved from controller?

I'm using ng-tranlate for i18n.
I'd like to combine a translated label together with a variable resolved from controller binding. How can I achieve the following?
<div translate="my.lang.text">some more: {{controller.attribute}}</div>
This does not work, and ng-translate ignores any content between the divs. why?
The translate directive will replace the content of the element with the translation you pass to it.
The use case you are describing looks like a parameterized translation. If you want to keep the use of the directive, you could pass the variable through the translate-values directive:
<div translate="my.lang.text"
translate-values="{value: 'some more: ' + controller.attribute}"></div>
You have to specify that your translation is parameterized:
JSON
"my.lang.text": "This is a parameterized string {value}"
I believe the translate directive replaces all the element's content with the translation.
In this case, you probably want to use the translate filter instead.
<div>{{'my.lang.text' | translate}} some more: {{controller.attribute}}</div>
As an alternative, you could also avoid this issue by giving the translated value it's own element.
<div><span translate="my.lang.text"></span> some more: {{controller.attribute}}</div>
If the translation is always intended to have have a value appended to it, then using a parameterized translation is probably the best solution (as suggested by Michael https://stackoverflow.com/a/33419608/90305)

Can I use a ternary operator with angularjs outside of the class attribute?

I'm looping through a json object with AngularJS, and having trouble dealing with nulls.
I'm new to Angular.
...
In the event of related being null:
{
"related":null
}
I want to put a "-1" in its place:
...
Tried ternary operator but it isn't evaluating... it's just displaying it as plain text.
Simply you could use ng-attr to add custom attribute after evaluation of {{}} interpolation directive
Markup
...
For the sake of completeness, this is the answer the OP's original question ("how do you use a ternary in a directive"):
Add {{}} around the entire ternary statement, not just the variable. For example, this
...
should be
...
Otherwise, Angular won't parse it -- you'll get the value of product.related, followed by the literal text ? product.related : '-1'.
use or operator. this code will help :
...
Just use this
...
It is something like "try to use product.related if it exists. if it doesnt, use -1 insted"

expression inside ng-class

I have a very simple Angular App and I'm trying to use ng-repeat in conjunction with ng-class to repeat a template and apply a different class to the outer div depending on one of the properties of the data being bound.
this worked when I used a simple...
ng-class="message.type"
...but unfortunately I need to concatenate a string to the start of the message type.
I tried to create a JSfiddle here...
http://jsfiddle.net/XuYGN/5/
... but it's my first attempt at making a JSfiddle too and I must be doing something wrong because the Angular stuff doesn't seem to be running. It does show what I'm trying to do with the expression though.
Any help would really be appreciated.
html :
<div ng-controller="mainCtrl">
<div ng-repeat="message in data.messages" ng-class="'className-' + message.type">
Repeat Me
</div>
</div>
</div>
javascript :
var mainCtrl=function($scope) {
$scope.data = {}
$scope.data.messages = [
{
"type": "phone"},
{
"type": "email"},
{
"type": "meeting"},
{
"type": "note"}
]
}​
in the fiddle you put some {{}} around the expression dont do it because it is an expression.
FYI, an alternative to what #camus answered:
class="{{'className-' + message.type}}"
When using class, the expression (inside {{}}s) must evaluate to a string of space-delimited class names.
When using ng-class, the expression must evaluate to one of the following:
a string of space-delimited class names, or
and array of class names, or
a map/object of class names to boolean values.

Categories

Resources