Angular ngFor without an iterable. Need to Build the Iterable - javascript

Having an *ngFor in Angular 2+ need to build the iterable (rows) through the creation of an array of n elements:
<table>
<tr *ngFor="let row of rows">
</tr>
</table>
It's there a way of iterate over a number??.
Thanks in advance.

You can generate an array of row indexes with a method of the component class:
public generateRowIndexes(count: number): Array<number> {
let indexes = [];
for (let i = 0; i < count; i++) {
this.indexes.push(i);
}
return indexes;
}
and call it in the template:
<table>
<tr *ngFor="let rowIndex of generateRowIndexes(5)">
...
</tr>
</table>
The code can be tested in this stackblitz.

I suggest you build a custom structure directive just for looping as many times as you want, it is a for-loop wrapped as directive.
The steps to doing this are:
Step-1:
In your module create a loop.directive.ts file.
Step-2: In this directive file paste this code:
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[appLoop]'
})
export class LoopDirective {
constructor(
private viewContianerRef: ViewContainerRef,
private templateRef: TemplateRef<any>,
) {}
#Input('appLoop') set render(loop: number){
this.viewContianerRef.clear();
for(let i=0; i<loop; i++){
this.viewContianerRef.createEmbeddedView(this.templateRef, {});
}
}
}
Step-3: Now in your template file use *appLoop="5" if you want to loop 5 times or use a variable for example numberOfLines : *appLoop="numberOfLines".
In the code you put in the question it will look like this, where rows is of type number:
<table>
<tr *appLoop="rows">
</tr>
</table>

I would use this syntax to set the index value into an attribute of the HTML element:
<table>
<tr *ngFor="let item of items; let i = index" [attr.data-index]="i"> // data-index attribute will be assigned index
</tr>
</table>

Related

Angular Creating Dynamic Component Inside Desired Component

I am trying to create a component inside html table's each td child dynamicly.
I created a table component like following code (inside app.component.html)
<div class="container">
<table class="table">
<thead>
<tr>
<th scope="col" *ngFor="let column of columns">{{column}}</th>
</tr>
</thead>
<tbody #preGrid id="focusItem">
</tbody>
</table>
</div>
app.component.ts file seems as follow.
ngOnInit() {
let tableItem = document.getElementById("focusItem");
let lastTR;
for (let i = 0; i < 12; i++) {
if (i % this.columns.length == 0) {
let tr = document.createElement("tr");
tableItem.appendChild(tr);
}
lastTR = this.getLastNode(tableItem);
let td = document.createElement("td");
lastTR.appendChild(td);
const factory = this.resolver.resolveComponentFactory(
CustomInputComponent
);
this.container.createComponent(factory);
}
}
I am trying to create angular components by dynamicly using ComponentFactoryResolver.To draw this component, I used ViewContainerRef.But seems, it is not true way to do this.Coz I cannot create my CustomInputComponent inside td component via this way.
What I am trying to do is, embeding CustomInputComponent inside each td elements.You can see what I wrote till now here.
You need to create an array of object which will have the props as well as the type of component you want to render, then in the html portion you can have an ngFor loop to iterate over that array of object. Inside the ngFor loop have multiple ngSwitchCase to render the correct control for the current iteration.
Please see this link : https://codeburst.io/angular-dynamic-forms-ng-switch-approach-4f267c01d2c6

making dynamic condition in typescript

I have a dynamic array that represents columns in a table, and I have an array of rows in the table. I get the arrays as input in my angular 4 components and I don't know anything in the columns before I get them (should work on multiple types of variables).
Each column has a variable that says if it is mandatory or not and according to this I build a condition string that I want to run before I insert a new row to the array of rows or change an existing one.
I'm creating the condition in the ngInit, and after the creation it looks something like this:
"(r.id == null) || (r.name == '')" and so on (there could be && too)
I'm trying to bind the condition to the disabled attribute of a button, but I can't seem to convert it to a real.
How can I convert it to a condition so I could execute it and return true/ false accordingly?
I've already tried doing [disabled]="[conditionVar]", and also tried using.
${condition},
but those didn't work
my code looks something like this:
myComponent.ts:
export class Column {
title: string;
fieldName:string;
mandatory: boolean;
}
#Component({
selector: 'myComponent',
templateUrl: './myComponent.component.html',
styleUrls: ['./myComponent.component.css']
})
export class MyComponentimplements OnInit {
#Input() columns:Column[];
#Input() rows;
conditionString = "";
constructor() { }
ngOnInit() {
for (let c of columns.filter(d=>d.mandatory)) {
this.conditionString += "(r." + c['fieldName'] + " == null) || "; // 'r' is the name of the variable that i want to run the condition on
}
this.conditionString = this.conditionString.substr(0, this.conditionString.length - 4); // to remove the last ||
}
}
myComponent.html:
<table>
...
<tbody>
<tr *ngFor="let r of rows">
<td *ngFor="let c of columns">
<input [(ngModel)]="r.[c.fieldName]">
</td>
<td *ngFor="let c of columns">
<button [disabled]="here I want to bind my condition">save this row</button>
</td>
</tr>
</tbody>
</table>
(the user should edit the rows and if he want he can save the changes)
every thing works but the binding of the condition

unable to assign a value in ngFor loop: cannot assign to readonly property of object

temp.html code
<div>
<div>{{status}}</div>
</div>
<table>
<tr *ngFor="let d of list">
<td>{{d.desc}}</td>
</tr>
</table>
d.desc has value :submitted,pending,partial
I want if d.desc is pending, status should change to pending.I don't want loop or filter in ts file
ts file has variable
public status:string="ok";
public list:any=[{"desc":"submitted"},{"desc":"pending"},{"desc":"partial"}]
I am trying but got cannot assign to readonly property of object
You can't achieve directly! But still you can use function and call that function from html by passing the status.
I have created a stackblitz for that! Take a look at it!
https://stackblitz.com/edit/angular-a7bxve
In html...
<table>
<tr *ngFor="let d of list">
<td>{{showStatus(d.desc)}}</td>
</tr>
</table>
And in ts file...
showStatus(status) {
this.status = status;
}

How to add row component in table in Angular 7?

I use last version of angluar. (7.2.0)
I have personal tr component like:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-table-row',
templateUrl: './table-row.component.html',
styleUrls: ['./table-row.component.scss']
})
export class TableRowComponent implements OnInit {
#Input() character: any;
#Input() columns: string[];
constructor() { }
ngOnInit() {
}
}
I want to use this component inside table like:
<table class="mat-elevation-z8">
<tr>
<th *ngFor="let c of columns">{{c}}</th>
</tr>
<tr app-table-row class="component-style table-row-component" *ngFor="let ch of characters | async"
[character]="ch"
[columns]="columns">
</tr>
</table>
get errors like:
Can't bind to 'character' since it isn't a known property of 'tr'. ("table-row class="component-style table-row-component" *ngFor="let ch of characters | async"
[ERROR ->][character]="ch"
[columns]="columns">
</tr>
"): ng:///AppModule/TableComponent.html#7:7
How to add my component correctly inside table?
Define the component selector as an attribute selector with square brackets:
#Component({
selector: 'tr[app-table-row]',
...
})
so that it can be set as an attribute of the tr element:
<tr app-table-row ...>
The approach of selecting an attribute 'tr[app-table-row]' is fine when you want to generate only one row at a time, but it could become cumbersome for many rows, for instance if you need to use multiple row span
In this case, just use a regular selector 'app-table-rows' and set the component's host display to contents.
:host {
display: contents;
}
demo
As #ConnorsFan mentioned, you need to define your component as an attribute by adding the square brackets to your component's selector, but there is no need to add the 'tr' in front of it, so the following should be fine:
#Component({
selector: '[app-table-row]',
...
})
Then use it as follows:
<tr app-table-row
*ngFor="let item of items"
[myProp]="item"
(myEvent)="executeEvent($event)"
...>
</tr>

How to access element by id in angular 2

I have table generated with dynamic ids like this one
<table>
<tbody>
<tr *ngFor="let row of createRange(seats.theatreDimension.rowNum)">
<td id={{row}}_{{seat}} *ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>
</tr>
</tbody>
</table>
I want to access table td element from angular 2 .ts file. Here are functions:
ngOnInit() {
this.getSeats();
}
getSeats() {
this.cinemaService.getAllSeats(this.repertoire.id).subscribe(
data => {
this.seats = data.json();
this.setReservedSeats();
}
)
}
setReservedSeats() {
this.seats.reservedSeats.forEach(seat => {
let cssSeatId = seat.rowNumReserved + "_" + seat.seatNumInRowResereved;
document.getElementById(cssSeatId).className += "reserved";
}
)
}
and after that I want dynamically to set class of every td, but I am getting this console error in my browser:
ERROR TypeError: Cannot read property 'className' of null
Just to note once again that I generate td ids dynamically. They are in form rowNum_cellNum.
I am getting this object from api.
{
"theatreDimension": {
"rowNum": 17,
"seatNumInRow": 20
},
"reservedSeats": [
{
"rowNumReserved": 9,
"seatNumInRowResereved": 19
},
{
"rowNumReserved": 2,
"seatNumInRowResereved": 4
},
{
"rowNumReserved": 15,
"seatNumInRowResereved": 15
}
]
}
I am using theatreDimension to make table. Then I try to make reserved seats from reservedSeats array with red background (reserved)
How I can access td elements from angular 2 and solve this issue?
Instead of accessing the DOM directly, you should try using the ngClass directive to set the class:
<td [ngClass]="{'reserved': isReserved(row, seat)}" id={{row}}_{{seat}} *ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>
You can then implement the isReserved(row, seat) function, and if it returns true, it will apply the reserved class.
isReserved(rowNum: number, seatNum: number) {
return this.seats.reservedSeats.some((r) => r.rowNumReserved === rowNum && r.seatNumInRowResereved === seatNum);
}
To directly answer your question, in order to get the element by ID, you need to do so after the page has been renedered. Use the function ngAfterViewInit(). You will need to remove the call to setReservedSeats() from getSeats().
ngOnInit() {
this.getSeats();
}
ngAfterViewInit(){
this.setReservedSeats();
}
However, I would suggest going a different route. You can set the style of the element based on whether or not the seat has been reserved. Assuming you have some sort of "reserved" flag on the seat object you can do something like this:
<td id={{row}}_{{seat}}
[ng-class]="{'reserved' : seat.researved}"
*ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>

Categories