expression inside ng-class - javascript

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.

Related

Append index dynamically to attribute

I have this button element:
<button v-on:click="changeRecord(element)" v-b-modal.modal-5>Aendern</button>
it is generated dynamically inside a v-for loop.
Instead of hard coding the attribute name like above v-b-modal.modal-5 I want to concatenate it like this:
v-b-modal.modal-{{index}}
Is there a way to do this?
I'm using vue-cli 3 and bootstrap-vue.
I haven't used this framework before but looking at the second example from the docs I think something like the following should work.
<button v-on:click="changeRecord(element)" v-b-modal="`modal-${index}`">Aendern</button>
You will need to ensure that the variable index is made available when you set up the v-for
EDIT: For clarity, the above works because in VueJS the input to a directive is evaluated as an expression. The above example uses backticks string interpolation but the same can be done using pretty much any valid expression like "'modal-'+index" or based on some property on the item we are looping over "`modal-${item.id}`".
Unlike directives, class or other attributes are interpreted as plain strings unless they are bound using v-bind in which case they are treated as expressions. The example in the docs uses a simple string as an input so it's hard to tell from that particular example that it can be used in this way.
It is possible to add dynamic attributes like following
<p class="text" v-bind="options">{{ message }}</p>
Inside the computed, define the value for options
export default {
data:()=> {
return {
message: 'Hello world!',
id: 12345
}
},
computed: {
options() {
return {
[`v-b-modal.modal-${this.id}`]: "whatever"
}
}
}
}

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'] }"

Chain multiple expressions in AngularJS ng-class

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' : '']

jQuery conditional that checks the class and if it contains that class it executes

I am working with wordpress and I have to display a menu based on a product category but it goes by term and there is no way for me to add a class. So I have term-maincat-subcat as the class and the subcat changes depending on where your at but the main cat stays the same as it is the brand. I was looking into the jQuery wildcards and there is a way to select an element with a certain string in a class. How would I go about running a conditional to check if it has that string in the class. Here's what I've got...
if($('body').is('body[class~="brand"]')) {
$('.brand-cat-nav').show();
}
The above doesn't seems to work and like I said, unfortunately, there is no way of adding a straight "brand" class to it to make this a little easier with being able to just use .hasClass(). Any suggestions?
PS: the class that it's outputting to the body is some thing to likes of "term-maincat-subcat".
Class is really easy to check:
if ($('body').is('.brand')) { ... }
or:
if ($('body').hasClass('brand')) { ... }
edit — if you mean that you want to find out if a string is part of a class, then that's going to require you going to the "className" property and checking with a regex:
if (/brand/.test($('body').prop('className'))) { ... }
If it's not always "brand", then you can make a regular expression from a string:
function whatever( someClassFragment ) {
if (new RegExp(someClassFragment).test($('body').prop('className')) { ... }
If the string might have non-alpha characters in it then you'd have to deal with that.
You can try this:
if( $('body').filter("[class^='brand']").length )​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ {
// body has class starting with 'brand'
}
This filters the body with a class name that starts with 'brand'.
If you want to test if body has a class containing 'brand' use class*="brand" as filter instead.

How do templating engines in JavaScript work?

Could you please explain me, how do templating engines in JavaScript work? Thank you.
JSON
{ "color" : "red"}
Template
<strong><%=color%></strong>
Result
<strong>Red</strong>
As a starting point I would recommend you to give a look to the String.prototype.replace method and specially using its callback function:
function replaceTokens(str, replacement) {
return str.replace(/<\%=([^%>]+)\%>/g, function (str, match) {
return replacement[match];
});
}
var input = "<strong><%=color%></strong>";
replaceTokens(input, { "color" : "Red"});
// returns <strong>Red</strong>
replaceTokens("<%=var1%> <%=var2%>", { "var1" : "Hello", "var2": "world!"});
// returns "Hello world!"
Give a look to these articles:
Search and Don't Replace
John Resig's Micro-Templating Engine
Better JavaScript Templates (JSP-like syntax)
jQuery Templates Proposal
They may vary by implementation, but the one you're talking about looks like it works by doing the following:
Parse the page looking for keys in <%= %> tags
Match the key to the key/value pair in the JSON
Replace the tags/key with the value.
It's not very different from other templating solutions (at the conceptual level).
{ "color" : "red"}
Specifies a color attribute with the value red.
<strong><%=color%></strong>
Means "Use the value of color wherever I have <%=color%>. Based on wahat you have, the templating-engine probably walks the DOM and finds nodes that have values that match <%=somestring%>. Then, it checks to see if there is an attribute that matches the somestring value. If there is one, it replaces the value of <%=somestring%> with the value defined in the JSON (which, in this case is red).
This finally gives you:
<strong>Red</strong>

Categories

Resources