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

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

Related

while deleting the particular items from iteration of items in click event first item is deleting instead of clciked one

In my angular application I have some iteration items and saving the items based on adding the items.
.component.html
<ng-container *ngFor="let categoryDetail of selectedCategoryDetails">
<div class="__header">
<div>
<b>{{ categoryDetail.category }}</b>
</div>
</div>
<div
class="clinical-note__category__details"
*ngIf="categoryDetail.showDetails">
<ul>
<li class="habit-list"
*ngFor="let habits of categoryDetail.habitDetails" >
<div class="target-details">
<b>{{ clinicalNoteLabels.target }}: </b
><span class="habit-list__value">{{ habits.target }}</span>
</div>
</li>
</ul>
<div class="habit-footer">
<span class="m-l-10"
[popoverOnHover]="false"
type="button"
[popover]="customHabitPopovers"><i class="fa fa-trash-o" ></i> Delete</span>
</div>
<div class="clinical-note__popoverdelete">
<popover-content #customHabitPopovers [closeOnClickOutside]="true">
<h5>Do you want to delete this habit?</h5>
<button
class="btn-primary clinical-note__save" (click)="deletedata(index);customHabitPopovers.hide()">yes </button>
</popover-content></div>
</div>
</ng-container>
.component.ts
public saveHealthyHabits() {
let isCategoryExist = false;
let categoryDetails = {
category: this.clinicalNoteForm.controls.category.value,
habitDetails: this.healthyHabits.value,
showDetails: true,
};
if (this.customHabitList.length) {
categoryDetails.habitDetails = categoryDetails.habitDetails.concat(
this.customHabitList
);
this.customHabitList = [];
}
if (this.selectedCategoryDetails) {
this.selectedCategoryDetails.forEach((selectedCategory) => {
if (selectedCategory.category === categoryDetails.category) {
isCategoryExist = true;
selectedCategory.habitDetails = selectedCategory.habitDetails.concat(
categoryDetails.habitDetails
);
}
});
}
if (!this.selectedCategoryDetails || !isCategoryExist) {
this.selectedCategoryDetails.push(categoryDetails);
}
this.clinicalNoteForm.patchValue({
category: null,
});
this.healthyHabits.clear();
}
public deletedata(index:number){
if (this.selectedCategoryDetails) {
this.selectedCategoryDetails.forEach((selectedCategory) => {
this.selectedCategoryDetails.splice(index, 1);
}}
From the above code I have saved the data based on adding the items as above and my requirement is when we click on the delete(it will show the popup having the button yes implemented in anbove code).
when we click on the yes button from list of items, I have to remove the particular item
When I tried removing ,It is only deleting the first item instead of clicked one
Can anyone help me on the same
The logic for deletion is incorrect. The splice mutates the original array, and you are applying the loop for deletion, which keeps on iterating over the array and deleting the array elements based on index, instead of deleting single matched index element.
Example -
const categories = [
1,
2,
3,
4
];
function removal(i) {
categories.forEach((category, index) => {
categories.splice(i, 1);
});
console.log('----Categories-->', categories);
}
removal(0);
Categories Array
First Iteration [index = 0]
[1,2,3,4]
Loop Starts Iterating from 1
Second Iteration [index = 1]
[2,3,4]
Loop Starts Iterating from 3
Third Iteration [index = 2]
[3,4]
Stop
Instead you can use filter function.
public deletedata(index:number){
this.selectedCategoryDetails = this.selectedCategoryDetails.filter((_, i) => i! == index);
}
Note - I would recommend to delete the categories based on some identifier like id instead of index because the array elements position can get changed.
Instead of passing index to deleteData method, you can pass the category object.
public deletedata(category){
this.selectedCategoryDetails = this.selectedCategoryDetails.filter((c) => c.id! == category.id);
}

Angular arrays and ngFor

I have a html component with list
<div *ngFor="let item of myArr">
<ul>
<li>
{{ item }}
<span>
<mat-icon (click)="expression($event)">add</mat-icon>
</span>
<ng-container
><h2 *ngFor="let item of textArr">
{{ item }}
</h2></ng-container
>
</li>
</ul>
</div>
and component ts with arrays
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
})
export class ChildComponent implements OnInit {
myArr = ['1', '2', '3', '4'];
abc = ['a', 'b', 'c', 'd'];
textArr = [];
constructor() {}
ngOnInit(): void {}
expression(ev: Event) {
console.log(ev);
this.textArr.push(this.abc);
}
}
how can I make, that when I click + what is on front of 1, then 'a' form abc will be displayed only under 1, if I click + what is on front of 2, then b from abc should be display under 2, etc...
Use the index in for loop.
I have an example
<h2>Form list</h2>
<div *ngFor="let form of formList; let i = index">
<ul>
<li>
<h4>{{ form.formLable }}</h4>
<div>
<button (click)="loadForm(i)">View {{ form.formLable }}</button>
</div>
<div *ngIf="form.isLoaded">
Do what ever strategy to load a form in
If many many many forms look at loading components in dynamically
</div>
</li>
</ul>
</div>
Here is a working example.
https://stackblitz.com/edit/angular-8clu3r?file=src%2Fapp%2Fform-list%2Fform-list.component.html
I think you're asking how to put the corresponding value from one array below an item in the list, e.g. pressing add next to 1 would show a, 3 would show c.
I think your best bet is to pass the index of the loop into your function 'expression'. You can get the index of your loop by adding index as i to your loop, so it would look something like this.
<div *ngFor="let item of myArr; index as i">
<ul>
<li>
{{ item }}
<span>
<mat-icon (click)="expression($event, i)">add</mat-icon>
</span>
...
Now you need a structure to store the values that have been added, I would look at using a 2D array (an array of arrays) to store your added values and display them using your for loops and indexes.
Hope this helps 😊

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

Add style to specific element angular 6

I have the following block of code in the front-end:
<li *ngFor = "let cat of this.dataCategory.iconTitleSet" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
in the component:
getTypeFromCategory(tipo: string) {
this.typeItem = tipo.toLowerCase();
if (this.arrayTipo.includes(this.typeItem)) {
const i = this.arrayTipo.indexOf( this.typeItem );
this.arrayTipo.splice( i, 1 );
} else {
this.arrayTipo.push(this.typeItem);
}
}
in synthesis what up to now does is add a value that I get from FRONTEND in an array in case it is not, and if it eliminates it from the array, but when I add it I also want to put a specific style, for example a yellow background, but this last I do not know how to do it, I do not know how to say to angular that I put a specific style in element "li" specific generated by an "ngfor" loop when I click on the element.
this is the image in the frontend
You can do it in some ways:
1. to set a diffrennt class with [ngClass]="" to each li and style in css
for example:
<li *ngFor = "let cat of this.dataCategory.iconTitleSet;let i=index;" [ngClass]="'title_'+i" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
in css:
.title_1{}
2. you can set it with [ngStyle] or style.
<li *ngFor = "let cat of this.dataCategory.iconTitleSet;let i=index;" [style.color]="cat.color" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
You can do the following.
HTML
<li *ngFor = "let cat of this.dataCategory.iconTitleSet; let i = index" (click)="getTypeFromCategory(cat.title, index)" class="list-group-item puntero" [class.changeColor]="i == selectedValue">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
Component
selectedValue: any;
getTypeFromCategory(tipo: string, index) {
this.selectedValue = index;
this.typeItem = tipo.toLowerCase();
if (this.arrayTipo.includes(this.typeItem)) {
const i = this.arrayTipo.indexOf( this.typeItem );
this.arrayTipo.splice( i, 1 );
} else {
this.arrayTipo.push(this.typeItem);
}
}
class changeColor will be applied to every list item selected.

How to display following object in angular5 html file?

In the above screenshot console I have an object with 2 values users and tickers. Again each one is array of values.
Now how to display these values in angular5 html template as specified in above screenshot?
I am trying to use ngFor but it is showing errors.
Suppose this is your data:
public data = {
tickers:[
{id:"1",name:"Ticker Name 1", company:"Company 1"},
{id:"2",name:"Ticker Name 2", company:"comapny 2"}
],
users:[
{id:"1",first_name:"User1 ", last_name:"u1last", email:"user1#test.com"},
{id:"2",first_name:"User2", last_name:"u2last", email:"user2#test.com"},
{id:"3",first_name:"User3", last_name:"u3last", email:"user3#test.com"},
{id:"4",first_name:"User4", last_name:"u4last", email:"user4#test.com"}
]
};
public dataKeys; // This will hold the main Object Keys.
The constructor will look something like this:
constructor() {
this.dataKeys = Object.keys(this.data);
}
Here is the simple HTML that you need to write:
<div *ngFor="let key of dataKeys">
<h3>{{ key }}</h3>
<ul>
<li *ngFor="let d of data[key]">{{d.name || d.first_name}}</li>
</ul>
</div>
Here is the complete working plunker for your case:
Click here to view the Working Solution
you can use like that,
<ul>
<P>users</p>
<li *ngFor="let item of object.users; let i = index">
{{i}}. {{item.frist_name}}
</li>
<P>Tickets</p>
<li *ngFor="let item of object.tickers; let i = index">
{{i}}. {{item.name}}
</li>
</ul>
According to the documentation it should be this:
Users:
<ol>
<li *ngFor="let user of users">{{user.first_name}}</li>
</ol>
Tickets:
<ol>
<li *ngFor="let ticket of tickets">{{ticket.name}}</li>
</ol>
You can use json pipe for debug purpose like this:
{{object |json}}
If you want exactly as in picture, look at this solution. With this case, you don't need to manually write first level property names of object in template for using *ngFor

Categories

Resources