How to add row component in table in Angular 7? - javascript

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>

Related

Angular 6 data not getting binded to UI OnInit

I have a problem that my dynamic data is not getting binded to the UI and also Data tables. I have tried using various ways but its not working. I am using smart admin latest theme for my development of website
when I hit the api i get response.
var tabledata:any[]=[]
Get(){
this.getservice().subscribe(
res=>{
if(res && res.data && res.data.length>0){
this.tabledata=res.data;
console.log(this.tabledata);
}
}
)
}
In html
<tr *ngFor="let data of tabledata">
<td>{{data.name}}</td>
<td>{{data.age}}</td>
</tr>
Could you please try to excute your Get method in OnInit?
Why do your Get method starts with a capital letter?
Why do you declare your table as a table of any? Using typed objects i always better.
Can you try this code below?
vartabledata: Person[];
class MyComponent implements OnInit {
ngOnInit() { this.get()
}
get(){
this.getservice().subscribe(
res=> {this.tabledata = res.data;
console.log(this.tabledata);}
)}
}
In front i would suggest you to add *ngIf so you display table only when your data are loaded.
<div *ngIf="tabledata">
<tr *ngFor="let data of tabledata">
<td>{{data.name}}</td>
<td>{{data.age}}</td>
</tr>
</div>
If there are no data even with this, try to do
<div *ngIf="tabledata">
<tr *ngFor="let data of tabledata">
{{data | json}}
</tr>
</div>
I think that you receive a data object that doesn't contains name and age
attributes

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

Angular ngFor without an iterable. Need to Build the Iterable

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>

React <div> cannot appear as a child of <tr> Warning

I am getting the following warnings in a React component:
The related code is the following:
import React, { PropTypes } from 'react';
import { Checkbox } from 'react-bootstrap';
const MyComponent = (params) => {
function onSelect(event) {
params.onSelect(event, params.data.id);
}
return (
<tr key={params.data.id}>
{params.isSelectable ? (
<Checkbox onChange={onSelect}>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</Checkbox>
) : (
<div>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</div>
)}
</tr>
);
};
If I remove the div tags, I get the following error:
Adjacent JSX elements must be wrapped in an enclosing tag
I am new to React, so i am not quite clear on what is going on here. What's the problem and how can I fix it?
Update: my React version is 15.3.2.
If you need to return several elements and can't have a wrapper (such as in this case), you have a new option as of React 16.2. Fragments:
<React.Fragment>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</React.Fragment>
Or, you might be able to simplify it as:
<>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</>
The fragments won't resolve to any HTML element, so the <td>s will be direct children of your <tr>.
There are two problems with your code
Only td and th are allowed inside tr
In React version < 15, you have to wrap tags in one element, when you try to render them. In React 16, you can now do the following :
[
<td key={1}>{params.data.firstName} </td>,
<td key={2}>{params.data.lastName} </td>
]
instead of wrapping the tds inside a div
I also suggest you extract your logic outside of the 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