How can I have several statements inside a structural directive on Angular2 - javascript

I'm new to Angular 2 and haven't worked front end before. So I'm not sure how to write a template structural directives. I need to translate this from ejs/angular 1 to Angular 2
for (var i = 0; i < n; i++) {
<% var classes = item.columns[i].fields.skin.indexOf("sign-in") > -1 ? "sign-in" : "" %>
<div class="dropdown menu-standout desktop <%= classes %>">
<a><%= item.columns[i].fields.title %></a>
</div>
}
My best guess was this, but I'm sure I can't have curly brackets twice inside ngFor. Any help?
<li *ngFor="let column of item.columns">
{{var classes = column.fields.skin.indexOf("sign-in") > -1 ? "sign-in":""}}
<div class="dropdown menu-standout desktop" {{ classes }}>
<a>{{column.fields.title}}</a>
</div>
</li>

You can not have binding assignment in template interpolation directive.
So, it should be like this
{{column.fields.skin.indexOf("sign-in") > -1 ? "sign-in":""}}
You can not declared variable inside interpolation directive
Remove the var classes = part from template otherwise it will template parse error.

you want to add a class dynamically, so try this :
<li *ngFor="let column of item.columns">
<div [class.active] = "isActive = 'column.fields.skin.indexOf("sign-in") > -1 ? "sign-in":"" ' " class="dropdown menu-standout desktop">
<a>{{column.fields.title}}</a>
</div>
</li>
it is not good practice to have js code in your view.
in your component
export class MyComponent {
isActive: boolean = false;
}

I fixed the syntax by writing an extra function
<li *ngFor="let column of item[0].fields.columns">
<div class="dropdown menu-standout desktop {{ checkIndex(column) }}" >
<a>{{column.fields.title}}</a>
</div>
</li>
export class PageDropdownComponent {
constructor() {}
checkIndex(classes:any) {
return classes.fields.skin.indexOf("sign-in") > -1 ? "sign-in" : "";
}
Thanks everyone else for the help, it was useful for understanding the syntax

Related

How to get 1st true value for *ngIf inside *ngFor

I have an array of items that need to be displayed based on roles. I need the first value which will fulfil the ngIf condition.
I am adding my code below:
My Array(kind of how it will originally look):
parentTabList = [
{
name: 'abc',
label: 'abc',
icon : 'question_answer',
role : ['vend_perm','vend_temp','vend_subs']
},
{
name: 'xyz',
label: 'xyz',
icon : 'question_answer',
role : ['vend_perm','vend_subs']
}
]
My Html: -
<ng-container *ngFor="let form of parentTabList let i = index">
<li *ngIf="form.role.includes(userRole)">
<a (click)="methodName(form)">
{{form.label}}
</a>
</li>
</ng-container>
UserRole is a string value that I get when a user logs-in.
I need to add a ngClass to the anchor tag if it is the first anchor to be displayed.
(I am a noob at StackOverflow, please let me know if any more explanation is required).
You can identify first element of the array with index.
But as per my understanding you need filter this array with roles and then apply ngClass to first element from filtered list.
So add method to return filtered array with respect to roles
In Template:
filterParentTabList(parentList: any) {
return parentList.filter(form => form.role.includes(this.userRole));
}
In View:
<ng-container *ngFor="let form of filterParentTabList(parentTabList); let i = index">
<li>
<a [ngClass]="{ 'addYourClaaName': i === 0 }" (click)="methodName(form)">
{{form.label}}
</a>
</li>
</ng-container>
Happy Coding.. :)
You can write like this. In this code, f represents the first position of your array.
<ng-container *ngFor="let form of parentTabList; let i = index; let f = first">
<li *ngIf="f">
<a (click)="methodName(f)">
`{{f.label}}`
</a>
</li>
</ng-container>
If you want other position of your array, you can write like you mentioned above.
You can define a getter that will get you the index. This can then be used in the html
get firstIndex() {
return this.parentTabList.indexOf(this.parentTabList.find(({role}) =>
role.includes(this.userRole)))
}
Now in your html
<ng-container *ngFor="let form of parentTabList let i = index">
<li *ngIf="form.role.includes(userRole)">
<a [ngClass]="{redText: firstIndex === i}" (click)="methodName(form)">
{{form.label}}
</a>
</li>
</ng-container>
See Stackblitz Demo Here

Add 'check' class to previous item after clicking a button

I'm trying to add a class unlocked on previous item when I click button and my current moves to next item, but I can't figure out how. Current functionality is working fine but I want to add unlocked class. Any help would be great.
<ul>
<li class="locked" *ngFor="let subLecture of lectureList; let j = index"
[ngClass]="{ 'current': lectureIndex == j}"
(click)="lectureItemClick(j)">
<a>{{subLecture}}</a>
</li>
</ul>
<button (click)="nextLectureSecond()">Next</button>
Here's an attached demo: https://stackblitz.com/edit/angular-ivy-cdiev5
You can add another condition as well in the [ngClass], something like 'unlocked': lectureIndex > j if you wish to apply the class to all the previous items.
See if this helps:
<ul>
<li class="locked" *ngFor="let subLecture of lectureList; let j = index"
[ngClass]="{ 'current': lectureIndex == j, 'unlocked': lectureIndex > j}"
(click)="lectureItemClick(j)">
<a>{{subLecture}}</a>
</li>
</ul>
Similarly, you can change the logic to suit your scenario.
Stackblitz: https://stackblitz.com/edit/angular-ivy-62422113
Although It's not recommended to use the document keyword if you are using server-side rendering, you can use another workaround as well, you can follow the below way as well.
<li class="locked" *ngFor="let subLecture of lectureList; let j = index" [id]="j">
....
</li>
nextLectureSecond() {
....
let ele = document.getElementById(this.lectureIndex.toString());
(<HTMLParagraphElement>ele).classList.add("unlocked");
const a = this.lectureIndex++;
....
}
Working Example
Just the previous item?
Change your ng-class declaration like this :
[ngClass]="{ 'current': lectureIndex == j, 'unlocked' : (lectureIndex!=0 && lectureIndex - 1 == j )}"
Se here

React - Optionally wrap an item in a link

I have a React component that displays a title and some text. I want to optionally wrap the title in a link (the same component is used in more than one place), and would appreciate guidance for the best way to do it.
My component looks like this:
var FeedItem = React.createClass({
renderRawMarkup: function(text) { ... },
render: function() {
var item = this.props.item,
rawBody = this.renderRawMarkup(item.body);
return (
<article className="feed-item">
<h2 className="feed-item__title">{item.title{</h2>
<div className="feed-item__body" dangerouslySetInnerHTML={rawBody} >
</div>
</article>
);
});
Am I best to create a new component just for the title? Or can I use an if inside the return, e.g.:
<h2 className="feed-item__title">
{if (item.path) { <a href={item.path}> }}
{item.title}
{if (item.path) { </a> }}
</h2>
I'm a bit of a React novice so apologies if I'm approaching the problem from completely the wrong angle!
You can't use if statements inside jsx, but you can make use of ternary expressions. In your case, you can use:
<h2 className="feed-item__title">
{ item.path ? <a href={item.path}>{item.title}</a> : {item.title} }
</h2>
This is stated in the official documentation: React docs

Angular: conditional class with *ngClass

What is wrong with my Angular code? I am getting the following error:
Cannot read property 'remove' of undefined at BrowserDomAdapter.removeClass
<ol>
<li *ngClass="{active: step==='step1'}" (click)="step='step1'">Step1</li>
<li *ngClass="{active: step==='step2'}" (click)="step='step2'">Step2</li>
<li *ngClass="{active: step==='step3'}" (click)="step='step3'">Step3</li>
</ol>
Angular version 2+ provides several ways to add classes conditionally:
type one
[class.my_class] = "step === 'step1'"
type two
[ngClass]="{'my_class': step === 'step1'}"
and multiple option:
[ngClass]="{'my_class': step === 'step1', 'my_class2' : step === 'step2' }"
type three
[ngClass]="{1 : 'my_class1', 2 : 'my_class2', 3 : 'my_class4'}[step]"
type four
[ngClass]="step == 'step1' ? 'my_class1' : 'my_class2'"
You can find these examples on the documentation page
[ngClass]=... instead of *ngClass.
* is only for the shorthand syntax for structural directives where you can for example use
<div *ngFor="let item of items">{{item}}</div>
instead of the longer equivalent version
<template ngFor let-item [ngForOf]="items">
<div>{{item}}</div>
</template>
See also https://angular.io/docs/ts/latest/api/common/index/NgClass-directive.html
<some-element [ngClass]="'first second'">...</some-element>
<some-element [ngClass]="['first', 'second']">...</some-element>
<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
<some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
<some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
See also https://angular.io/docs/ts/latest/guide/template-syntax.html
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
[class.special]="!isSpecial">This one is not so special</div>
<!-- reset/override all class names with a binding -->
<div class="bad curly special"
[class]="badCurly">Bad curly</div>
Another solution would be using [class.active].
Example :
<ol class="breadcrumb">
<li [class.active]="step=='step1'" (click)="step='step1'">Step1</li>
</ol>
That's the normal structure for ngClass is:
[ngClass]="{'classname' : condition}"
So in your case, just use it like this...
<ol class="breadcrumb">
<li [ngClass]="{'active': step==='step1'}" (click)="step='step1'">Step1</li>
<li [ngClass]="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li [ngClass]="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>
with the following examples you can use 'IF ELSE'
<p class="{{condition ? 'checkedClass' : 'uncheckedClass'}}">
<p [ngClass]="condition ? 'checkedClass' : 'uncheckedClass'">
<p [ngClass]="[condition ? 'checkedClass' : 'uncheckedClass']">
You can use ngClass to apply the class name both conditionally and not in Angular
For Example
[ngClass]="'someClass'">
Conditional
[ngClass]="{'someClass': property1.isValid}">
Multiple Condition
[ngClass]="{'someClass': property1.isValid && property2.isValid}">
Method expression
[ngClass]="getSomeClass()"
This method will inside of your component
getSomeClass(){
const isValid=this.property1 && this.property2;
return {someClass1:isValid , someClass2:isValid};
}
Angular provides multiple ways to add classes conditionally:
First way
active is your class name
[class.active]="step === 'step1'"
Second way
active is your class name
[ngClass]="{'active': step=='step1'}"
Third way
by using ternary operator class1 and class2 is your class name
[ngClass]="(step=='step1')?'class1':'class2'"
You should use something ([ngClass] instead of *ngClass) like that:
<ol class="breadcrumb">
<li [ngClass]="{active: step==='step1'}" (click)="step='step1; '">Step1</li>
(...)
In Angular 7.X
The CSS classes are updated as follows, depending on the type of the expression evaluation:
string - the CSS classes listed in the string (space delimited) are added
Array - the CSS classes declared as Array elements are added
Object - keys are CSS classes that get added when the expression given in the value evaluates to a truthy value, otherwise they are removed.
<some-element [ngClass]="'first second'">...</some-element>
<some-element [ngClass]="['first', 'second']">...</some-element>
<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
<some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
<some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
Additionally, you can add with method function:
In HTML
<div [ngClass]="setClasses()">...</div>
In component.ts
// Set Dynamic Classes
setClasses() {
let classes = {
constantClass: true,
'conditional-class': this.item.id === 1
}
return classes;
}
to extend MostafaMashayekhi his answer for option two>
you can also chain multiple options with a ','
[ngClass]="{'my-class': step=='step1', 'my-class2':step=='step2' }"
Also *ngIf can be used in some of these situations usually combined with a *ngFor
class="mats p" *ngIf="mat=='painted'"
You can use [ngClass] or [class.classname], both will work the same.
[class.my-class]="step==='step1'"
OR
[ngClass]="{'my-class': step=='step1'}"
Both will work the same!
While I was creating a reactive form, I had to assign 2 types of class on the button. This is how I did it:
<button type="submit" class="btn" [ngClass]="(formGroup.valid)?'btn-info':''"
[disabled]="!formGroup.valid">Sign in</button>
When the form is valid, button has btn and btn-class (from bootstrap), otherwise just btn class.
We can make class dynamic by using following syntax. In Angular 2 plus, you can do this in various ways:
[ngClass]="{'active': arrayData.length && arrayData[0]?.booleanProperty}"
[ngClass]="{'active': step}"
[ngClass]="step== 'step1'?'active':''"
[ngClass]="step? 'active' : ''"
Let, YourCondition is your condition or a boolean property, then do like this
[class.yourClass]="YourCondition"
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 object, then for each key-value pair of the object with a truthy value the corresponding key is used as a class name.
If the expression evaluates to an array, each element of the array should either be a string as in type 1 or an object as in type 2. This means that you can mix strings and objects together in an array to give you more control over what CSS classes appear. See the code below for an example of this.
[class.class_one] = "step === 'step1'"
[ngClass]="{'class_one': step === 'step1'}"
For multiple options:
[ngClass]="{'class_one': step === 'step1', 'class_two' : step === 'step2' }"
[ngClass]="{1 : 'class_one', 2 : 'class_two', 3 : 'class_three'}[step]"
[ngClass]="step == 'step1' ? 'class_one' : 'class_two'"
ngClass syntax:
[ngClass]="{'classname' : conditionFlag}"
You can use like this:
<ol class="breadcrumb">
<li [ngClass]="{'active': step==='step1'}" (click)="step='step1'">Step1</li>
<li [ngClass]="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li [ngClass]="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>
This is what worked for me:
[ngClass]="{'active': dashboardComponent.selected_menu == 'profile'}"
For elseif statement (less comparison) use like that: (For example you compare three statement)
<div [ngClass]="step === 'step1' ? 'class1' : (step === 'step2' ? 'class2' : 'class3')"> {{step}} </div>
Not relevant with [ngClass] directive but I was also getting the same error as
Cannot read property 'remove' of undefined at...
and I thought to be the error in my [ngClass] condition but it turned out the property I was trying to access in the condition of [ngClass] was not initialized.
Like I had this in my typescript file
element: {type: string};
and In my [ngClass] I was using
[ngClass]="{'active', element.type === 'active'}"
and I was getting the error
Cannot read property 'type' of undefined at...
and the solution was to fix my property to
element: {type: string} = {type: 'active'};
Hope it helps somebody who is trying to match a condition of a property in [ngClass]
<div class="collapse in " [ngClass]="(active_tab=='assignservice' || active_tab=='manage')?'show':''" id="collapseExampleOrganization" aria-expanded="true" style="">
<ul> <li class="nav-item" [ngClass]="{'active': active_tab=='manage'}">
<a routerLink="/main/organization/manage" (click)="activemenu('manage')"> <i class="la la-building-o"></i>
<p>Manage</p></a></li>
<li class="nav-item" [ngClass]="{'active': active_tab=='assignservice'}"><a routerLink="/main/organization/assignservice" (click)="activemenu('assignservice')"><i class="la la-user"></i><p>Add organization</p></a></li>
</ul></div>
Code is good example of ngClass if else condition.
[ngClass]="(active_tab=='assignservice' || active_tab=='manage')?'show':''"
[ngClass]="{'active': active_tab=='assignservice'}"
Try Like this..
Define your class with ''
<ol class="breadcrumb">
<li *ngClass="{'active': step==='step1'}" (click)="step='step1; '">Step1</li>
<li *ngClass="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li *ngClass="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>
The example is a bit big, but triggering a class instead of typing inline is my first preferred approach.
this way you can add as many possibilities as you want to your element.
There may be a way for those who want to bind more than one [ngClass] to a single element.
<span class="inline-flex items-center font-medium" [ngClass]="addClass">{{ badge.text }}</span>
import { ChangeDetectionStrategy, Component, Input } from '#angular/core';
type Badge = {
size?: 'basic' | 'large';
shape?: 'basic' | 'rounded';
color?: 'gray' | 'red' | 'yellow' | 'green' | 'blue' | 'indigo' | 'purple' | 'pink';
dot?: boolean;
removeButton?: false;
text?: string;
}
#Component({
selector: 'bio-badge',
templateUrl: './badge.component.html',
styleUrls: ['./badge.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BioBadgeComponent {
#Input() badge!: Badge;
get addClass() {
return {
'px-2.5 py-0.5 text-sx': this.badge.size === 'basic',
'px-3 py-0.5 text-sm': this.badge.size === 'large',
'rounded-full': this.badge.shape === 'basic',
'rounded': this.badge.shape === 'rounded',
'bg-gray-100 text-gray-800': this.badge.color === 'gray',
'bg-red-100 text-red-800': this.badge.color === 'red',
'bg-yellow-100 text-yellow-800': this.badge.color === 'yellow',
'bg-green-100 text-green-800': this.badge.color === 'green',
'bg-blue-100 text-blue-800': this.badge.color === 'blue',
'bg-indigo-100 text-indigo-800': this.badge.color === 'indigo',
'bg-purple-100 text-purple-800': this.badge.color === 'purple',
'bg-pink-100 text-pink-800': this.badge.color === 'pink',
}
}
}
If user want to display the class on basis of && and ||
then below one is work for me
[ngClass]="{'clasname_1': condition_1 && condition_2, 'classname_2': condition_1 && condition2, 'classname_3': condition}"
Example:
[ngClass]="{'approval-panel-mat-drawer-side-left': similar_toil_mode==='side' && showsTheSimilarToilsWithCloseIcon, 'approval-panel-mat-drawer-side-right': similar_toil_mode==='side' && !showsTheSimilarToilsWithCloseIcon, 'approval-panel-mat-drawer-over': similar_toil_mode==='over'}"

Is there a way to pass a variable with curly brace template tags through a method?

I have an ng-repeat where I run an ng-if with a method that checks to see if certain parameters match between two sets of data. If there's a match, it updates $scope with the current match, which I can use to output my template:
<div ng-repeat="repeatedItem in repeatedItems">
<a href="" ng-if="!matchData(repeatedItem.item1, repeatedItem.item2)">
<img ng-src="{{ matchedItem.falseImageUrl }}" />
</a>
<a href="" ng-if="matchData(repeatedItem.item1, repeatedItem.item2)" ng-click="open(matchedItem.id)">
<img ng-src="{{ matchedItem.imageUrl }}" />
<time>{{ matchedItem.date }}</time>
<div class="button-wrapper">
<button class="btn">{{ matchedItem.text }}</button>
</div>
</a>
</div>
The thinking is that everything out of repeatedItems needed to show, unless an item in repeatedItem matched another data set, in which case you'd show the matchedItem instead.
Everything works fine, except for the ng-click="open(matchedItem.id) bit. Because matchedItem.id isn't wrapped in template tags, ng-click is always calling the latest iteration of matchedItem, so for all but the last repeated element, it's opening the wrong link.
The most obvious solution in my Angular-inexperienced mind would have been to do something like ng-click="open( {{ matchedItem.id }} ), but that throws an error. My next idea was something like ng-click="open( {0} ):{{ matchedItem.id }} - a printf-type of solution, but I haven't found any built-in Angular solution for that. I also thought about stashing {{ matchedItem.id }} in an attribute somewhere (data-id="{{ matchedItem.id }}"?), but I'm not sure how to call that attribute in the method.
It's possible (probable?) that I'm just not "thinking in Angular" yet, and I'm going about this the wrong way entirely. Or perhaps there's a directive that I'm just not aware of?
Here's the method:
$scope.matchData = function(item1, item2) {
var isItAMatch = false;
for (i=0; i<$scope.repeatedItems.length; i++) {
itemAToMatch = $scope.repeatedItems[i].itemA;
itemBToMatch = $scope.repeatedItems[i].itemB;
if (itemAToMatch == item1 && itemBToMatch == item2) {
isItAMatch = true;
$scope.matchedItem = $scope.repeatedItems[i];
break;
}
}
return isItAMatch;
}

Categories

Resources