How to groupBy rows in Mat-Table - html

I have mat-Table with filters and i want to group the table by one of the columns.
I want to make it a collapsible row that just shows how much you have of "that" kind and you can press it to see every single row inside that group.
I will share my HTML(that's a snippet not the whole HTML :
<mat-checkbox class="CheckBoxClass" value="clientType" [(ngModel)]="isChecked " disabled (change)="updateFilter('LQOCH_SHM_LOEZI_QTSR', clientType)" >{{clientType}}</mat-checkbox>
<br><br>
<mat-checkbox class="CheckBoxClass" value="contSize" [(ngModel)]="isChecked" disabled (change)="updateFilter('AORKH_MCOLH', contSize)" >{{contSize}}</mat-checkbox>
<br><br>
<mat-checkbox class="CheckBoxClass" value="storageType" [(ngModel)]="isChecked" disabled (change)="updateFilter('TAOR_QTSR_EBRI', storageType)" >{{storageType}}</mat-checkbox>
</div>
<!-- Container Table -->
<div>
<mat-table [dataSource]="dataSource" [hidden]="!show" >
<!-- Location -->
<ng-container matColumnDef="AITOR">
<mat-header-cell *matHeaderCellDef> Location
</mat-header-cell>
<mat-cell *matCellDef="let container"> {{container.AITOR}} </mat-cell>
</ng-container>
<!-- Type -->
<ng-container matColumnDef="SOG_MCOLH">
<mat-header-cell *matHeaderCellDef > Container Type</mat-header-cell>
<mat-cell *matCellDef="let container"> {{container.SOG_MCOLH}}</mat-cell>
This is my component NgOnInit :
ngOnInit() {
this.marinService.getAllContainers().subscribe((result) => {
//Data
this.dataSource = new MatTableDataSource(result);
//Paginator
this.dataSource.paginator = this.paginator;
this.dataSource.filterPredicate = ((data: Container, filter: string): boolean => {
const filterValues = JSON.parse(filter);
let conditions = true;
for (let filterKey in filterValues) {
if (filterKey === 'SOG_MCOLH' || filterKey === 'LQOCH_SHM_LOEZI_QTSR' || filterKey === 'AORKH_MCOLH' || filterKey === 'TAOR_QTSR_EBRI') {
conditions = conditions && data[filterKey].trim().toLowerCase().indexOf(filterValues[filterKey]) !== -1;
}
else if (filterValues[filterKey].length) {
conditions = conditions && filterValues[filterKey].includes(data[filterKey].trim().toLowerCase());
}
}
return conditions;
});
}
)}
Basically what I want to achieve is a row that collapse and shows how many rows it has and you can press on it to open the rows.

This doesn't come out of the box with angular Material. There are some solutions to do it yourself.
Here is another SO post that gives some directions: Angular Material mat-table Row Grouping
The second StackBlitz link provided there gives a good example of a table using grouped row.
You can also have a look at other libraries that include row grouping by default, if that's something your project allows.

Related

How to have 2 different results in a bootstrap dropdown separated by a heading?

I am trying to show different results in a single autocomplete result differentiating with the heading. I have 2 roles Subject and Teacher, when searching with some letter I want to show both the results of Teacher and Subject, but want to differentiate as below
Expected Output
expected output
Teacher heading will differentiate the 2 result sets
The output I am getting
Subject
Math
...
Maddy
....
The link to the code is here
Any help is much appreciated.
Thanks in advance!!
Slightly updated logic:
Typescript:
export class AppComponent {
typeahead: FormControl = new FormControl();
countries = countries;
suggestions = {
countries: [],
teachers: [],
};
suggest() {
this.suggestions = {
countries: this.countries
.filter(c => c.role === 'Subject')
.filter(c => c.name.startsWith(this.typeahead.value))
.slice(0, 5),
teachers: this.countries
.filter(c => c.role === 'Teacher')
.filter(c => c.name.startsWith(this.typeahead.value))
.slice(0, 5),
};
}
}
HTML:
<input type="text" [formControl]="typeahead" placeholder="Type ahead !" (input)="suggest()">
<div class="suggestions">
<ng-container *ngFor="let s of suggestions.countries; let i = index">
<h3 *ngIf="i === 0">{{ s.role }}</h3>
<p>{{ s.role }} - {{ s.name }}</p>
</ng-container>
<ng-container *ngFor="let s of suggestions.teachers; let i = index">
<h3 *ngIf="i === 0">{{ s.role }}</h3>
<p>{{ s.role }} - {{ s.name }}</p>
</ng-container>
</div>
Your example doesn't work because in html only the first element is being checked (when deciding when to put h3 tag) and it was with the role Teacher. Splitting into 2 fields will result in you being able to separately iterate each of them and apply logic (in this case, check if i === 0).

Get data from nested json in Angular

Im trying to iterate over an array but page is displaying [ object Object] instead. How can I get data.id of my example?
Service:
return this._http.get<CustomersList[]>(this.apiUrl,{ headers: reqHeader });
My json looks like this:
[ {"current_page":1,"data":[{"id":25},{"id":26}] }]
And here is my component:
customerslist$: CustomersList[];
return this.customerdataService.getCustomersList().subscribe(data => this.customerslist$ = data);
Would be great if anybody can help me?
The data property is an array contained in an array. You either need two *ngFor directives
<ng-container *ngFor="let customer of customerslist$">
<ng-container *ngFor="let item of customer.data">
{{ item.id }}
</ng-container>
/ng-container>
Or if you're sure the customerslist$ array will always contain only one element, you could access the first element directly.
<ng-container *ngFor="let item of customerslist$[0].data">
{{ item.id }}
</ng-container>
Also if you're aren't using the this.customerList$ in the controller, you could skip the subscription in the controller and use the async pipe in the template. It takes care of any potential memory leak issues due to open subscriptions.
Contoller
customerslist$: Observable<CustomersList[]>; // <-- typeof `Observable`
ngOnInit() {
this.customerslist$ = this.customerdataService.getCustomersList(); // <-- don't subscribe yet
}
<ng-container *ngIf="(customerslist$ | async) as customerslist">
<ng-container *ngFor="let customer of customerslist">
<ng-container *ngFor="let item of customer.data">
{{ item.id }}
</ng-container>
</ng-container>
</ng-container>
By convention, variable names suffixed with a dollar sign (customerlist$) are used to denote observables.
If you are using Observable $ sign at the end of variable denotes observable,
in that case you do not need to subscribe only return value, in template you can subscribe using async pipe which automatically unsubscribe as well to prevent memory leak and a recommended approach.
Here we assume we are only taking first element of array that why directly accessing it giving first index, otherwise we need to iterate that array in template as well.
customerslist$: Observable<CustomersList[]>;
this.customerslist$ = this.customerdataService.getCustomersList()
.pipe(map(res) => res[0].data));
In template use async pipe to subscribe
<ng-container *ngFor="let data of customerslist$ | async">
<span> {{ data.id }} </span>
</ng-container>
WITHOUT OBSERVABLE
customerslist: CustomersList[];
this.customerdataService.getCustomersList()
.subscribe((res) => this.customerslist = res[0].data));
In template
<ng-container *ngFor="let data of customerslist">
<span> {{ data.id }} </span>
</ng-container>

HTML / TypeScript display multiple checkbox values in angular 2

The problem is that i want to be able to select multiple checkboxes, and on the click of the Testing button, display the selected boxes in the console. The problem here is that im only using one checkbox that displays in several rows. I´m using angular 2 with material design components and angular fire 2.
//TypeScript
import { Component, Input, OnInit } from '#angular/core';
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
import { DataSource } from '#angular/cdk';
#Component({
selector: 'waybill-billing',
templateUrl: './waybillbilling.component.html',
styleUrls: ['./waybillbilling.component.css']
})
export class WaybillBillingComponent implements OnInit {
checked = false;
#Input('id') waybillId: string;
displayedColumns = ['status', 'type', 'articleId', 'description', 'amount'];
dataSource: FirebaseDataSource;
constructor(private db: AngularFireDatabase){}
ngOnInit(): void {
this.dataSource = new FirebaseDataSource(this.db, this.waybillId);
}
}
export class FirebaseDataSource extends DataSource<any> {
items: FirebaseListObservable<any[]>;
constructor(private db: AngularFireDatabase, private waybillId: string) {
super();
}
connect(): FirebaseListObservable<any[]> {
this.items = this.db.list('xxx/pieces/' + this.waybillId);
console.log(this.items);
return this.items;
}
disconnect(){
}
}
//HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="script.js"></script>
<div class="example-container mat-elevation-z8">
<md-table #table [dataSource]="dataSource" class="mat-typography">
<div> ng-controller="AppCtrl">
<ng-container cdkColumnDef="status">
<md-header-cell class="body-2" *cdkHeaderCellDef> Status </md-header-cell>
<md-cell class="body-1" *cdkCellDef="let row">
<md-checkbox></md-checkbox>
</md-cell>
</ng-container>
</div>
<!-- Type Column -->
<ng-container cdkColumnDef="type">
<md-header-cell class="body-2" *cdkHeaderCellDef> Type </md-header-cell>
<md-cell class="body-1" *cdkCellDef="let row">
<md-chip-list><md-chip selected="true" color="blue">{{row.type}}</md-chip></md-chip-list>
</md-cell>
</ng-container>
<!-- Article Column -->
<ng-container cdkColumnDef="articleId">
<md-header-cell class="body-2" *cdkHeaderCellDef> Art.nr </md-header-cell>
<md-cell class="body-1" *cdkCellDef="let row"> {{row.articleId}} </md-cell>
</ng-container>
<!-- Description Column -->
<ng-container cdkColumnDef="description">
<md-header-cell class="body-2" *cdkHeaderCellDef> Beskrivning </md-header-cell>
<md-cell class="body-1" *cdkCellDef="let row"> {{row.description}} </md-cell>
</ng-container>
<!-- Amount Column -->
<ng-container cdkColumnDef="amount">
<md-header-cell class="body-2" *cdkHeaderCellDef> kg/st </md-header-cell>
<md-cell class="body-1" *cdkCellDef="let row"> {{row.amount}} kg </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
</md-table>
<button (click)="test()" md-button>Testing</button>
</div>
strong text
You need to keep track of which checkboxes is checked:
<ng-container cdkColumnDef="select">
<md-header-cell *cdkHeaderCellDef> Status </md-header-cell>
<md-cell *cdkCellDef="let row">
<md-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row.$key) : null"
[checked]="selection.isSelected(row.$key)">
</md-checkbox>
</md-cell>
</ng-container>
In your code:
import {SelectionModel} from '#angular/material';
selection = new SelectionModel<string>(true, []);
Here is a plunker where you probably can find some good stuff for your needs...
md-table with checkboxes

Angular 4 Material 2 - How to change tabbing from being vertical to horizontal with MD-Gridlist?

I'm currently attempting to switch the tabbing inside of an MD-Gridlist to tab hoizontally rather than vertically. I have tried using tab indexes and had no luck. I want to be able to tab through this dynamically growing or shrinking grid list horizontally.
<ng-container *ngFor="let field of fieldsTable; let i = index">
<!--condition in the grid-list tag checks if the key filed can be shown -->
<md-grid-list class="static-column" cols="1" rowHeight="25px" *ngIf="field.$type == GlobalVariables.typeClasses.Static && (field.Name !== '' || showKey)">
<md-grid-tile class="field-name-tile theme-primary" *ngIf="!field.IsKey;">
{{field.Name}}
</md-grid-tile>
<md-grid-tile class="field-name-tile theme-primary" *ngIf="field.IsKey && showKey" mdTooltip="Key field used to update data for this row via the api, values must be unique.">
<md-icon>vpn_key</md-icon>
</md-grid-tile>
<md-grid-tile class="static-field-tile theme-accent-alternating " *ngFor="let content of field.ContentData; let i = index">
<md-input-container class="content-data-input">
<input class="field-input" mdInput (keyup)="content.Value=$event.target.value" value="{{content.Value}}">
</md-input-container>
</md-grid-tile>
</md-grid-list>
</ng-container>
Any help would be greatly appreciated because I've hit a roadblock.
Thank you in advance!
I managed to remedy this by:
Adding this to the input container html which live in the grid tiles of the grid list
tabindex={{setTabIndex(i,j)}}
and this in the TypeScript
setTabIndex(outerIndex: number, innerIndex: number): number {
return (outerIndex + 1) + (innerIndex * this.staticTableLength);
}

*ngIf else if in template

How would I have multiple cases in an *ngIf statement? I'm used to Vue or Angular 1 with having an if, else if, and else, but it seems like Angular 4 only has a true (if) and false (else) condition.
According to the documentation, I can only do:
<ng-container *ngIf="foo === 1; then first else second"></ng-container>
<ng-template #first>First</ng-template>
<ng-template #second>Second</ng-template>
<ng-template #third>Third</ng-template>
But I want to have multiple conditions (something like):
<ng-container *ngIf="foo === 1; then first; foo === 2; then second else third"></ng-container>
<ng-template #first>First</ng-template>
<ng-template #second>Second</ng-template>
<ng-template #third>Third</ng-template>
But I'm ending up having to use ngSwitch, which feels like a hack:
<ng-container [ngSwitch]="true">
<div *ngSwitchCase="foo === 1">First</div>
<div *ngSwitchCase="bar === 2">Second</div>
<div *ngSwitchDefault>Third</div>
</ng-container>
Alternately, it seems like a lot of the syntaxes I've got used to from Angular 1 and Vue aren't supported in Angular 4, so what would be the recommended way to structure my code with conditions like this?
Another alternative is to nest conditions
<ng-container *ngIf="foo === 1;else second"></ng-container>
<ng-template #second>
<ng-container *ngIf="foo === 2;else third"></ng-container>
</ng-template>
<ng-template #third></ng-template>
You can just use:
<ng-template [ngIf]="index == 1">First</ng-template>
<ng-template [ngIf]="index == 2">Second</ng-template>
<ng-template [ngIf]="index == 3">Third</ng-template>
unless the ng-container part is important to your design I suppose.
Here's a Plunker
This seems to be the cleanest way to do
if (foo === 1) {
} else if (bar === 99) {
} else if (foo === 2) {
} else {
}
in the template:
<ng-container *ngIf="foo === 1; else elseif1">foo === 1</ng-container>
<ng-template #elseif1>
<ng-container *ngIf="bar === 99; else elseif2">bar === 99</ng-container>
</ng-template>
<ng-template #elseif2>
<ng-container *ngIf="foo === 2; else else1">foo === 2</ng-container>
</ng-template>
<ng-template #else1>else</ng-template>
Notice that it works like a proper else if statement should when the conditions involve different variables (only 1 case is true at a time). Some of the other answers don't work right in such a case.
aside: gosh angular, that's some really ugly else if template code...
You can use multiple way based on sitaution:
If you Variable is limited to specific Number or String, best way is using ngSwitch or ngIf:
<!-- foo = 3 -->
<div [ngSwitch]="foo">
<div *ngSwitchCase="1">First Number</div>
<div *ngSwitchCase="2">Second Number</div>
<div *ngSwitchCase="3">Third Number</div>
<div *ngSwitchDefault>Other Number</div>
</div>
<!-- foo = 3 -->
<ng-template [ngIf]="foo === 1">First Number</ng-template>
<ng-template [ngIf]="foo === 2">Second Number</ng-template>
<ng-template [ngIf]="foo === 3">Third Number</ng-template>
<!-- foo = 'David' -->
<div [ngSwitch]="foo">
<div *ngSwitchCase="'Daniel'">Daniel String</div>
<div *ngSwitchCase="'David'">David String</div>
<div *ngSwitchCase="'Alex'">Alex String</div>
<div *ngSwitchDefault>Other String</div>
</div>
<!-- foo = 'David' -->
<ng-template [ngIf]="foo === 'Alex'">Alex String</ng-template>
<ng-template [ngIf]="foo === 'David'">David String</ng-template>
<ng-template [ngIf]="foo === 'Daniel'">Daniel String</ng-template>
Above not suitable for if elseif else codes and dynamic codes, you can use below code:
<!-- foo = 5 -->
<ng-container *ngIf="foo >= 1 && foo <= 3; then t13"></ng-container>
<ng-container *ngIf="foo >= 4 && foo <= 6; then t46"></ng-container>
<ng-container *ngIf="foo >= 7; then t7"></ng-container>
<!-- If Statement -->
<ng-template #t13>
Template for foo between 1 and 3
</ng-template>
<!-- If Else Statement -->
<ng-template #t46>
Template for foo between 4 and 6
</ng-template>
<!-- Else Statement -->
<ng-template #t7>
Template for foo greater than 7
</ng-template>
Note: You can choose any format, but notice every code has own problems
Or maybe just use conditional chains with ternary operator. if … else if … else if … else chain.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator#Conditional_chains
<ng-container [ngTemplateOutlet]="isFirst ? first : isSecond ? second : third"></ng-container>
<ng-template #first></ng-template>
<ng-template #second></ng-template>
<ng-template #third></ng-template>
I like this aproach better.
To avoid nesting and ngSwitch, there is also this possibility, which leverages the way logical operators work in Javascript:
<ng-container *ngIf="foo === 1; then first; else (foo === 2 && second) || (foo === 3 && third)"></ng-container>
<ng-template #first>First</ng-template>
<ng-template #second>Second</ng-template>
<ng-template #third>Third</ng-template>
you don't need to use *ngIf if you use ng-container
<ng-container [ngTemplateOutlet]="myTemplate === 'first' ? first : myTemplate ===
'second' ? second : third"></ng-container>
<ng-template #first>first</ng-template>
<ng-template #second>second</ng-template>
<ng-template #third>third</ng-template>
<ion-row *ngIf="cat === 1;else second"></ion-row>
<ng-template #second>
<ion-row *ngIf="cat === 2;else third"></ion-row>
</ng-template>
<ng-template #third>
</ng-template>
Angular is already using ng-template under the hood in many of the
structural directives that we use all the time: ngIf, ngFor and
ngSwitch.
> What is ng-template in Angular
https://www.angularjswiki.com/angular/what-is-ng-template-in-angular/
I came a cross this type of situation *ngIf elseIf else and I solved using ng-template, Hope the following snippet may depicts briefly,
I have a form control named "NIC" and need to show one error message at a time when the form control invalid.
form: FormGroup = new FormGroup({
NIC: new FormControl('', [Validators.required, Validators.minLength(10), Validators.maxLength(10), Validators.pattern("^[0-9]*$")])
});
Template
<mat-form-field appearance="standard">
<mat-label>NIC Number</mat-label>
<input matInput placeholder="Enter NIC no" formControlName="NIC">
<mat-error *ngIf="form.controls['NIC'].errors?.required; else minvalue">This field is mandatory.
</mat-error>
<ng-template #minvalue>
<mat-error *ngIf="form.controls['NIC'].errors?.minlength; else maxvalue">Minimum 10 charactors
needed.
</mat-error>
</ng-template>
<ng-template #maxvalue>
<mat-error *ngIf="form.controls['NIC'].errors?.maxLength; else numericonly">Maximum 10
charactors allowed.
</mat-error>
</ng-template>
<ng-template #numericonly>
<mat-error *ngIf="form.controls['NIC'].errors?.pattern">
Numeric characters only.
</mat-error>
</ng-template>
</mat-form-field>
If you have a straightforward case in which some variable can take several values (for example, foo is of 1 | 2 | 3 type) just use the plain ngSwitch:
<ng-container [ngSwitch]="foo">
<div *ngSwitchCase="1">First</div>
<div *ngSwitchCase="2">Second</div>
<div *ngSwitchCase="3">Third</div>
</ng-container>
However, if you need a more sophisticated if-else logic it may be better and clearer to describe it inside the component's code and then turn its result into one of the set of states:
getLandformType(): 'continent' | 'island' | 'peninsula' {
if(this.pieceOfLand.bigEnough){
return 'continent';
}
else if(this.pieceOfLand.surroundedByWater){
return 'island';
}
return 'peninsula';
}
which, again, can be handled through ngSwitch:
<ng-container [ngSwitch]="getLandformType()">
<div *ngSwitchCase="'continent'">Continent</div>
<div *ngSwitchCase="'island'">Island</div>
<div *ngSwitchCase="'peninsula'">Peninsula</div>
</ng-container>
This approach also ensures that only one option can win at once, in contrast to that with three different *ngIf (without elses) blocks.
You can also use this old trick for converting complex if/then/else blocks into a slightly cleaner switch statement:
<div [ngSwitch]="true">
<button (click)="foo=(++foo%3)+1">Switch!</button>
<div *ngSwitchCase="foo === 1">one</div>
<div *ngSwitchCase="foo === 2">two</div>
<div *ngSwitchCase="foo === 3">three</div>
</div>