Angular 2 Generate Class Name Based on ngFor Index - javascript

I'm running into a problem with creating a dynamic class name based on the Angular 2 ngFor loop index. I had to use the following syntax because Angular 2 does not like ngFor and ngIf on the same element.
With this syntax, how can I create a dynamic class name with the value of index at {{index}}. I know this isn't proper A2 code, but I put it in my code example to show you where I would like the value to appear.
<div class="product-detail__variants">
<template ngFor #variant [ngForOf]="variants" #index="index">
<div *ngIf="currentVariant == index">
<div class="product-detail-carousel-{{index}}">
</div>
</div>
</template>
</div>
The value "variants" is an empty array of a set length. "variant" thus has no value.
"currentVariant" is a number that by default equals 0.
EDIT: This code above is correct. I had another extraneous error that I thought was connected to this code.

I don't really understand your problem ;-)
There are two ways to set classes for a specific element:
The way you do with curly brackets:
#Component({
selector: 'my-app',
template: `
<div class="product-detail__variants">
<template ngFor #variant [ngForOf]="variants" #index="index">
<div *ngIf="currentVariant == index">
<div class="product-detail-carousel-{{index}}">
Test
</div>
</div>
</template>
</div>
`,
styles: [
'.product-detail-carousel-2 { color: red }'
]
})
Test is displayed only for the third element (index 2) and in red.
As suggested by #Langley using the ngClass directive
import {NgClass} from 'angular2/common';
#Component({
selector: 'my-app',
template: `
<div class="product-detail__variants">
<template ngFor #variant [ngForOf]="variants" #index="index">
<div *ngIf="currentVariant == index">
<div class="product-detail-carousel-{{index}}">
Test
</div>
</div>
</template>
</div>
`,
styles: [
'.product-detail-carousel-2 { color: red }'
],
directives: [ NgClass ]
})
The different is that you need to specify the NgClass directive within the providers attribute of your component. Again Test is displayed only for the third element (index 2) and in red.
Your following sentences: "The value variants is an empty array of a set length. variant thus has no value. currentVariant is a number that by default equals 0.". How do you expect something to be displayed if your array is empty. An ngFor is an iteration...
Hope it helps you,
Thierry

In case anyone is looking to add a specific class based on the index, you can do something like this. I personally prefer the syntax here too. Let's say you wanted to add last-para to the last paragraph from an array:
<p
*ngFor="let p of paragraphs; let i = index"
[class.last-para]="i >= paragraphs.length - 1">
{{p}}</p>

Related

Not able to pass string to formGroup

1.I want to loop through an array
*ngFor="let item of myformNameArray"
Think myformNameArray.length have 3 items
If I console item it will be like
myFormName1
myFormName2
myFormName3
I have already made these form group in my typescript component.
Example
<form [formGroup]="myFormName3">
It will work perfectly!!
But i want to loop :means
<div *ngFor="let item of myformNameArray">
<form [formGroup]="{{item}}">
</form>
</div>
So when I do,
[formGroup]="{{item}}"
It throws me an error can't assign to object or interpolation
Or
[formGroup]="see(item)"
Where ,
see(item) :string {
return String(item);
}
ERROR TypeError: can't assign to property "validator" on "see(item)": not an object
[formGroup] requires a FormGroup Object not string
you need to make array of formgroups instead of string names
TS:
myformArray = [
this.myFormOne,
this.myFormTwo,
this.myFormThree
]
HTML:
<div *ngFor="let item of myformArray">
<form [formGroup]="item">
</form>
</div>
you can also use formArray instead of normal array
TS:
myFormArray = new FormArray([]);
this.myFormArray.push(myFormOne);
this.myFormArray.push(myFormTwo);
this.myFormArray.push(myFormThree);
HTML:
<div *ngFor="let form of myFormArray.controls;">
<form [formGroup]="form">
<input formControlName="controlOne" />
<input formControlName="ControlTwo" />
</form>
</div>
And also you should not use both square brackets and curly brackets (interpolation) for property binding, use either square brackets or interpolation.
internally angular converts square brackets to interpolation
either do this : [formGroup]="item"
or this : formGroup="{{item}}"
not both

clicking should reveal the rest of the text

I am new to angular and I have a p tag with a list of paragraphs and data(nested array of objects) for p tag will be coming from the backend. I need to truncate the text after some character limit and show....show more. when a user clicks on the p tag it should show reveal the rest of the text. I have figure out the way to truncate the text and display ...show more.clicking should reveal that specific paragraph text but in my case, all other paragraphs texts which are truncated are also showing full texts and since the data is nested array of objects it is tricky for me and i am not able to figure out the solution. I am providing the stackblitz link below. any help will be appreciated.
stackblitz link
data = [
{
comments:[
{text:'this is comment',id:'1'},
{text:'this is comment',id:'2'}
]
},
{
comments:[
{text:'this is comment',id:'3'},
{text:'this is comment',id:'4'}
]
},
{
comments:[
{text:'this is comment',id:'5'},
{text:'this is comment',id:'6'}
]
}
]
showrest:boolean = false
<div *ngFor="let c of data">
<div *ngFor="let comment of c['comments']">
<p (click)="showrest=true">{{showrest?comment.text:(comment.text | slice:0:10)+'...Click to Read More'}}</p>
</div>
</div>
Here is how you could do, see this repro on stackblitz. You need a context for each comment so just create a component for it. Here is the code :
ts:
import { Component, Input } from "#angular/core";
#Component({
selector: "app-comment",
templateUrl: "./comment.component.html",
styleUrls: ["./comment.component.css"]
})
export class CommentComponent {
#Input() comment;
showrest = false;
}
html:
<p (click)="showrest=!showrest">{{showrest?comment.text:(comment.text | slice:0:10)+'...Click to Read More'}}</p>
and you call it as follow :
app.html
<div *ngFor="let c of data">
<div *ngFor="let comment of c['comments']">
<app-comment [comment]="comment"></app-comment>
</div>
</div>
Your problem was that each of your comment were bound to the same variable (Of the same component), so when yu click on a paragraph it update your variable and therefore all the binded element (here your paragraphs).
Another solution could be to add a property to your comment model, something like :
{text:'this is comment',id:'1', isFullyVisible: false},
This way your could handle the display of your comment with this variable. The best solution depends on what you intend to do with your comment. If it's just a display and nothing else then just adding a property to your model is good enough.

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 😊

How to Dynamically Add Elements in Vue.JS?

I am having a little trouble figuring out how to dynamically add elements in Vue. I tried using the V-for as a for loop, however, the output is just the second item in the array. My goal is to create a new box for each of the array items.
The first block is my script code containing my two arrays. The second block is the template or HTML. I tried to loop through each of the numbs array and create a new box in each instance, however, I am having trouble creating new boxes for each element in the array.
<script>
export default{
data(){
return{
numbs: ['Application 1', 'Application 2'],
applications: [
{ ver: '0.99911', status: 'Ready for Sale', deploymentD: '09/10/2020', deploymentC: 'XXXX'},
{ ver: '0.99911', status: 'Ready for Sale', deploymentD: '09/10/2020', deploymentC: 'XXXX'}
]
}
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div class="body">
<div class="box"
v-for="(demo, index) in numbs"
:key="index">
<div class="heading">
{{demo}}
</div>
<button class="info"><router-link to = "/viewapp">View</router-link></button>
<button class="edit"><router-link to = "/editapp">Edit</router-link></button>
<img class ="place" src="../images/imageinsert.jpg" />
<div class ="summary"
v-for="(apps, index) of applications"
:key="index">
Application Version {{apps.ver}}
{{apps.status}}, {{apps.deploymentD}}, {{apps.deploymentC}}
</div>
</div>
</div>
</template>
Here is an image of how the code currently runs. It prints out the last element of both arrays
Any help is greatly appreciated!!
To loop through an array and create an element for each item, you use the v-for directive. You use it like so:
<div class="summary" v-for="(apps, index) in applications" :key="index">
Application Version {{apps.ver}}
{{apps.status}}, {{apps.deploymentD}}, {{apps.deploymentC}}
</div>
You have v-for=(apps, index) for applications" (it should be in not for)
Also, you are using the index variable in both the inner and outer for loops. That's bound to cause an issue.

accessing *ngFor last in its component in angular 6

In angular 6 I want to access *ngFor last value as I want to operation if last value is set
eg
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span [last]="last"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
I want to do is [(myvar)]=last in place of let last=last
I want to bind the last variable so, I can access it is set or not in its component.
you can create a custom directive:
import { Directive, Output, EventEmitter, Input } from '#angular/core';
#Directive({
selector: '[onCreate]'
})
export class OnCreate {
#Output() onCreate: EventEmitter<any> = new EventEmitter<any>();
constructor() {}
ngOnInit() {
this.onCreate.emit('dummy');
}
}
and then you can use it in your *ngFor to call the method in your component:
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span (onCreate)="onCreate(last)"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
then in your component:
myvar: boolean = false;
onCreate(last) {
this.myvar = last;
}
checkout this DEMO.
Angular provides certain local variables when using *ngFor, one is for example last, which will (not as you expect currently) be a boolean value, being true if it is the last item. This is meant for adding specific stylings for the example to the last element of a list.
If you want that boolean you already use it correctly, but obviously the element using it should be a component. So instead of
<span [last]="last"></span>
it should be something like
<my-component [last]="last"></my-component>
where in my-component you define
#Input last: boolean;
and thus have access to it.
for example
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let index=i;">
</li>
now you excess last elemnt like
<anyTag *ngIf="i === chatlist.length-1"></anyTag>

Categories

Resources