collapsible lists with *ngFor - html

As it looks in the photo, that I want is, when I click show more, I want to expand this row, and show me all sensors in there
I'm using materialize, and my code html is:
<table class="bordered table-bordered" [mfData]="homeboxsp | dataFilter : filterQuery" #mf="mfDataTable" [mfRowsOnPage]="rowsOnPage"
[(mfSortBy)]="sortBy" [(mfSortOrder)]="sortOrder">
<thead style="color:black; background:rgb(207, 235, 245);border:1px solid rgb(190, 190, 190);">
<tr>
<th>
<mfDefaultSorter by="client">Client</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="description">Homebox</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="sensors">Sensors</mfDefaultSorter>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of homeboxsp">
<td>{{item.client}}</td>
<td>{{item.description}}</td>
<td>
<ul>
<ul *ngFor="let sensor of item.sensors">
<li>{{sensor.sensor_serial}}</li>
</ul>
</ul>
</td>
</tr>
</tbody>
</table>

you can do this in simple way using ngIf
<ul *ngFor="let sensor of item.sensors;let i = index;">
<li *ngIf="i < value">{{sensor.sensor_serial}}</li>
<button (click)="showmore()">showmore</button>
</ul>
ts
value:number = 2;
showmore(){
let value = this.value;
this.value= value+1;
}
you can modify logic as you want

The SlicePipe is perfect for this kind of situations. It does pretty much the same as the accepted answer with fewer *ngIf needed.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let val of values | slice:0:visible">{{val}}</div>
<div *ngIf="visible==3" (click)="visible=values.length">show more</div>
<div *ngIf="visible==values.length" (click)="visible=3">show less</div>
</div>
`,
})
export class App {
visible= 3;
values = [1, 2,3,4, 5,6,7,8,9]
constructor() {
}
}
You can see a working example here.
The same principle applies if you want to have multiple lists that expand. You just need to control the visibility for each list separately.
Here's an example of that.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let group of groups">
<div>{{group.name}}</div>
<div *ngFor="let val of group.values | slice:0:(group.expanded? group.values.length :3)">{{val}}</div>
<div *ngIf="!group.expanded" (click)="group.expanded=true">show more</div>
<div *ngIf="group.expanded" (click)="group.expanded=false">show less</div>
</div>
</div>
`,
})
export class App {
groups = [{name: 'a', values: [1, 2,3,4, 5,6,7,8,9]},{name: 'b', values: [1, 2,3,4, 5,6,7,8,9]}]
constructor() {
}
}
And the working plnkr here.

You can build a simple component that can show just a certain number of sensors, or adjust it to show just certain height:
<td><values-display-cmp [values]="row"></values-display-cmp></td>
Then the component:
#Component({
selector: 'values-display-cmp',
template: `
<ul>
<li *ngFor="let val of shownValues">{{val}}</li>
</ul>
More
`
})
export class ValuesDisplayComponent {
#Input() set values(values) {
this._values = values;
this.shownValues = values.slice(0, 2);
};
open = false;
shownValues = [];
_values = [];
more() {
this.open = true;
this.shownValues = this._values;
}
}
Here's a full example.

Separate your array in 2 parts, and after clicking show more concat them. Seems like not a big deal.

Related

Angular Material Table - how to place *ngIf on whole column?

I have an Angular Material Table and would like to place an ngIf on the whole column so the whole column, header & data cells only show if the condition is true. I can place it on the data cell with little issue but I can't seem to work how to place it on the ng-container or on the mat-header-cell.
I have tried using ngFor and then using an ngIf, I have tried wrapping the header in a div or a table header & row but no luck.
result is the object, which has a property of websiteUrl
<table mat-table matSort [dataSource]="dataSource">
<ng-container matColumnDef="websiteUrl">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Website Link</th>
<td mat-cell *matCellDef="let result">
<div *ngIf="showWebsiteLink===1">
<div *ngIf="websiteLinkVisible(result)">
<a (click)="copyLink(result.websiteUrl)">
Copy Link
</a>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
I made a very simple example for you. To remove the entire col you would need to use *ngIf to remove it from the template when someCondition is false. And also you would need to remove the same col name from displayedColumns array. The latter is more important. In the example I have removed col 'weight'.
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
displayedColumns = ['position', 'name', 'weight', 'symbol'];
dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);
someCondition: boolean = false;
constructor() {
if (!this.someCondition) {
this.displayedColumns.splice(2,1);
}
}
}
https://stackblitz.com/edit/angular-material-table-data-source-kdttsz?file=app%2Fapp.component.ts

How to add a new column to the table, based on checkbox selection in angular 7

I have a table containing some rows.The table data comes from loop.Here I need to add new columns based on selection of checkbox.Suppose I checked product,a new table heading will create as product along with blank rows just below the table heading,again If I uncheck created columns will be removed.New columns should create before add button.Here is the code below
https://stackblitz.com/edit/angular-table-focus
app.component.html
<div class="container">
<h2>Basic Table</h2>
<p>The .table class adds basic styling (light padding and only horizontal dividers) to a table:</p>
<div><button (click)="enable()">Edit</button> <input type="checkbox" value="product">Product <input type="checkbox" value="market">market</div>
<table class="table border">
<thead>
<tr>
<th>Items</th>
<th>Business</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<td> <input #focus [disabled]='toggleButton' type="text" value="{{row.name}}"> </td>
<td> <input [disabled]='toggleButton' type="text" value="{{row.items}}"> </td>
<td> <button (click)="addRow(i)">Add</button></td>
</tr>
</tbody>
</table>
</div>
app.component.ts
import { Component,ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedRow : Number;
#ViewChild('focus') input: ElementRef;
public toggleButton: boolean = true;
ngOnInit() {
}
groups=[
{
"name": "pencils",
"items": "red pencil"
},
{
"name": "rubbers",
"items": "big rubber"
},
{
"name": "rubbers1",
"items": "big rubber1"
},
];
addRow(index): void {
var currentElement = this.groups[index];
this.groups.splice(index, 0, currentElement);
}
enable(){
this.toggleButton = false
setTimeout(()=>{ // this will make the execution after the above boolean has changed
this.input.nativeElement.focus();
this.selectedRow = 0;
},0);
}
setClickedRow(index) {
this.selectedRow = index;
}
}
Check out my StackBlitz for live demo: https://stackblitz.com/edit/angular-table-focus-77xrnn
To add or remove columns dynamically, we have to first fetch the values from the component instead of hard coding them in the template.
columns = ["Items", "Business"];
Component:
And whenever a column checkbox is toggled, we listen to the change event and add or delete the column accordingly.
onColumnStatusChange(column, isChecked) {
if (isChecked) {
// Add column to the table.
this.columns.push(column);
this.groups.forEach(value => {
value[column] = "";
});
} else {
// Delete column from the table.
this.columns.splice(this.columns.indexOf(column), 1);
this.groups.forEach(value => {
delete value[column];
});
}
}
Template:
In the template, columns and also table row items must be fetched dynamically.
<input type="checkbox" value="product" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">Product
<input type="checkbox" value="market" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">market
...
...
<tr>
<ng-container *ngFor="let column of columns; let i = index">
<th>{{ column }}</th>
</ng-container>
</tr>
...
...
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<ng-container *ngFor="let item of objectKeys(row); let j = index">
<td><input #focus [disabled]='toggleButton' type="text" value="{{row[item]}}"></td>
</ng-container>
<td><button (click)="addRow(i)">Add</button></td>
</tr>

How to add new row in the table on click a button in angular 7

I have a table contains several rows and an add button with each row.When I click add button a new row should be created just below that row similar to that row.Here is the code below
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
ngOnInit() {
}
groups=[
{
"name": "pencils",
"items": "red pencil"
},
{
"name": "rubbers",
"items": "big rubber"
},
];
//console.log(this.groups);
}
app.component.html
<div class="container">
<h2>Basic Table</h2>
<p>The .table class adds basic styling (light padding and only horizontal dividers) to a table:</p>
<table class="table border">
<tbody>
<tr *ngFor="let row of groups">
<td> {{row.name}} </td>
<td> {{row.items}} </td>
<td> <button>Add</button> </td>
</tr>
</tbody>
</table>
</div>
Just add it to the array and the view will update.
<td> <button (click)="addItem()">Add</button> </td>
public addItem(): void {
this.groups.push({name: 'foo', items: 'bar});
}
When I click add button a new row should be created just below that
row similar to that row.
Pass the current index where the button is clicked and insert an item at that index.
HTML:
<tr *ngFor="let row of groups; let i = index">
<td> {{row.name}} </td>
<td> {{row.items}} </td>
<td> <button (click)="addItem(i)">Add</button> </td>
</tr>
Component:
We are using JavaScript's splice() method to insert an item at that specific index.
addItem(index) {
var currentElement = this.groups[index];
this.groups.splice(index, 0, currentElement);
}
Live demo on StackBlitz: https://stackblitz.com/edit/angular-1pd6yx
a. Add click handler to the button
<button (click)="addRow(row)">Add</button>
b. Add method to the component
addRow(row: {name: string; items: string;}): void {
this.groups.push(row);
},

merge divs with same value

I am trying to merge the divs that have the same number in order to see the week and its number.
This is how my html looks like
<div class="wrapper clearfix">
<div class="left">
<label class="kw">WeekNo.</label>
</div>
<div class="right week-number" *ngFor="let day of days">
<label class="number-label"><span>{{day.weekNumber}} </span></label>
</div>
</div>
do I have to use *ngIf or just css?
I have read this How to merge two divs, but I don't know where ti include the if statement
this is the class I am using
export class Day {
number: number;
weekDay: number;
name: string;
weekNumber: number;
constructor(number: number, weekDay: number, name: string, weekNumber: number
) {
this.number = number;
this.weekDay = weekDay;
this.name = name;
this.weekNumber = weekNumber;
}
}
and this is how it should look afet merging
You can do it the following way by creating a groupBy pipe and including Bootstrap 4 in the project. Here is a plain working demo (you will have to do the styling) demo:
groupby.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
transform(collection: any[], property: string): any[] {
// prevents the application from breaking if the array of objects doesn't exist yet
if(!collection) {
return null;
}
const groupedCollection = collection.reduce((previous, current)=> {
if(!previous[current[property]]) {
previous[current[property]] = [current];
} else {
previous[current[property]].push(current);
}
return previous;
}, {});
// this will return an array of objects, each object containing a group of objects
return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
}
}
html code
<div class="table table-borderless ">
<div class="col-md-12 text-center border"><span>{{currentMonthName}}</span> <span>{{currentYear}}</span></div>
<div class="d-flex">
<div class=" text-center m-2 border" *ngFor="let day of days | groupBy:'weekNumber'">
{{day.key}}
<table class="table">
<tbody>
<tr>
<th scope="row" *ngFor="let x of day.value ">{{x.number}}</th>
</tr>
<tr>
<th scope="row" *ngFor="let y of day.value ">{{y.name}}</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>

Angular: Reset index value in the same template

I have a template of 3 tables having same JSON as parent like this.
<tbody>
<ng-container *ngFor="let row of reportingData.RecommendationData; let i=index">
<tr *ngIf="row.swRecommendations">
<td>{{i+1}}</td>
<td> {{row.swRecommendations.deviceID}}</td>
</tr>
</ng-container>
</tbody>
Another table body
<tbody>
<ng-container *ngFor="let row of reportingData.RecommendationData; let j=index">
<tr *ngIf="row.licenseRecommendations">
<td>{{j+1}}</td>
<td> {{row.licenseRecommendations.deviceID}}</td>
</tr>
</ng-container>
</tbody>
All these tables are in the same template. I'm assigning index values to different variables(i & j) but increment is happening i.e. if first table is having 5 rows, second table is starting with 6 not 1. How to fix this?
I tested you'r code and indexes are starting from 0 .
Please review my code.
Component.html
<h1>First Table</h1>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<ng-container>
<tr *ngFor="let a of array;let i = index">
<th>{{i + 1}}</th>
<th>{{a.name}}</th>
</tr>
</ng-container>
</table>
<h1>Second Table</h1>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<ng-container>
<tr *ngFor="let a of array;let j = index">
<th>{{j + 1}}</th>
<th>{{a.name}}</th>
</tr>
</ng-container>
</table>
Component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styles: ['./app.component.scss']
})
export class AppComponent {
constructor() {}
public array = [
{ id: 1, name: 'aaa'},
{ id: 2, name: 'bbb'},
{ id: 3, name: 'ccc'},
{ id: 4, name: 'ddd'},
{ id: 5, name: 'eee'},
{ id: 6, name: 'fff'},
]
}
H recently faced the same issue, since we are indexing it and increment it will be like that, the solution of this problem is like this Filter your data in the ts
this.a= this.reportingData.filter(x => x.swRecommendations);
this.b= this.reportingData.filter(x => x.licenseRecommendations);
<tr *ngFor="let row of a"> <tr *ngFor="let row of b">,
and then remove the if condition and iterate the data in HTML like this let row of reportingData , editing needed based on your consition in ts