import {
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {MatDialog, MatDialogRef, MatSort, MatTable, MatTableDataSource} from '@angular/material';
import {SelectionModel} from '@angular/cdk/collections';
import {MatContactDialogComponent} from './dialog/mat-contact-dialog.component';
import {Contact, IContactDialogData} from '../../interfaces';
import {Filter, Methods} from '../../enums';
/**
* @title Table with selection
*/
@Component({
selector: 'mat-contacts',
styleUrls: ['mat-contacts.component.scss'],
templateUrl: 'mat-contacts.component.html',
encapsulation: ViewEncapsulation.Emulated,
})
export class MatContactsComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild(MatTable) table: MatTable<any>;
@ViewChild(MatSort) sort: MatSort;
@Input()
contacts: Contact[];
@Input()
title = 'Contacts';
@Input()
isLoading: boolean;
@Input()
readonly: boolean;
@Input()
enableMenu: boolean;
@Output()
onContactAdded: EventEmitter<Contact> = new EventEmitter<Contact>();
@Output()
onContactRemoved: EventEmitter<Contact> = new EventEmitter<Contact>();
@Output()
onAddingNewContactCanceled: EventEmitter<void> = new EventEmitter<void>();
methods = Methods;
filter: Filter;
contactsDataSource: MatTableDataSource<Contact>;
contactsDisplayedColumns = ['name', 'email', 'phoneNumber'];
selection = new SelectionModel<Contact>(true, []);
dialogRef: MatDialogRef<MatContactDialogComponent> | null;
dialogAfterCloseSubscription: any;
constructor(public dialog: MatDialog) {
}
ngOnInit(): void {
console.log('ConfigurationHelper ngOnInit');
this.contactsDataSource = new MatTableDataSource<Contact>(this.contacts);
// this.contactsDataSource.sort = this.sort;
if (!this.readonly) {
this.contactsDisplayedColumns.splice(0, 0, 'select');
this.contactsDisplayedColumns.push('more');
console.log('data : ', this.contactsDataSource.data);
}
}
ngOnChanges(changes: SimpleChanges): void {
// console.log('on changes: ', changes);
if (!changes.contacts.isFirstChange()) {
this.table.renderRows();
}
}
ngOnDestroy(): void {
if (this.dialogAfterCloseSubscription) {
this.dialogAfterCloseSubscription.unsubscribe();
}
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.contactsDataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.contactsDataSource.data.forEach(row => this.selection.select(row));
}
select(row: any) {
if (!this.readonly) {
this.selection.toggle(row);
}
}
openAddDialogContainer(method?: Methods, contact?: Contact) {
const dialogData: IContactDialogData = {
method: method,
contact: contact
};
this.dialogRef = this.dialog.open(MatContactDialogComponent, {
panelClass: 'new-contact-dialog',
data: dialogData
});
this.dialogAfterCloseSubscription = this.dialogRef
.afterClosed()
.subscribe((result: IContactDialogData) => {
if (result) {
const methodFromResult: Methods = result.method;
const contactFromResult: Contact = result.contact;
switch (methodFromResult) {
case Methods.POST:
// console.log('on post');
// console.log('contact added -> ', result);
this.add(contactFromResult);
break;
case Methods.DELETE:
// console.log('on delete');
this.remove(contactFromResult);
break;
}
} else {
this.onAddingNewContactCanceled.emit();
}
});
}
add(contact: Contact) {
this.contactsDataSource.data.splice(0, 0, contact);
this.onContactAdded.emit(contact);
this.table.renderRows();
}
remove(contact: Contact) {
console.log('contact -> ', contact);
console.log('data.length before: ', this.contactsDataSource.data.length);
const index = this.contactsDataSource.data.indexOf(contact);
console.log('contactToRemove = ', index);
if (index > -1) {
this.contactsDataSource.data.splice(index, 1);
console.log('data.length after: ', this.contactsDataSource.data.length);
this.selection.clear();
this.table.renderRows();
this.onContactRemoved.emit(contact);
}
}
removeSelected() {
const selectedContacts = this.selection.selected;
selectedContacts.forEach((contact) => {
this.remove(contact);
});
}
applyFilter(filterValue: string) {
filterValue = filterValue.trim(); // Remove whitespace
filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
this.contactsDataSource.filter = filterValue;
}
}
<div fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="center">
<mat-contact-menu *ngIf="enableMenu" fxFlex="none" fxHide.lt-md></mat-contact-menu>
<div fxFlex>
<mat-progress-bar *ngIf="isLoading" color="accent" mode="query"></mat-progress-bar>
<mat-toolbar color="primary">
<div *ngIf="selection.selected.length > 0; then contactSelected else none"></div>
<ng-template #contactSelected>
{{selection.selected.length}}
<button mat-icon-button matTooltip="remove" (click)="removeSelected()">
<mat-icon>delete</mat-icon>
</button>
</ng-template>
<ng-template #none>
{{title}}
</ng-template>
<span class="fill-remaining"></span>
<button *ngIf="!readonly"
fxHide.xs
mat-fab
color="accent"
class="fab-add"
matTooltip="add new contact"
(click)="openAddDialogContainer(methods.POST)">
<mat-icon>add</mat-icon>
</button>
<button *ngIf="!readonly" fxHide
fxShow.xs
mat-icon-button
matTooltip="add new contact"
(click)="openAddDialogContainer(methods.POST)">
<mat-icon>add</mat-icon>
</button>
</mat-toolbar>
<div class="example-container mat-elevation-z8" style="margin-bottom: 50px">
<mat-table #table [dataSource]="contactsDataSource" matSort>
<!-- Checkbox Column -->
<ng-container *ngIf="!readonly" matColumnDef="select">
<mat-header-cell *matHeaderCellDef class="cell-shrink-checkbox">
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</mat-header-cell>
<mat-cell *matCellDef="let row" class="cell-shrink-checkbox">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</mat-cell>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Name</mat-header-cell>
<mat-cell *matCellDef="let element">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
<div *ngIf="element.photoURL; then image else avatar"></div>
<ng-template #image>
<img class="ngx-avatar"
mat-card-avatar
[src]="element.photoURL">
</ng-template>
<ng-template #avatar>
<avatar [email]="element.email"
[name]="element.name"
[displayType]="'circle'">
</avatar>
</ng-template>
<span>{{element.name}}</span>
</div>
</mat-cell>
</ng-container>
<!-- Email Column -->
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef> Email</mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.email}}</mat-cell>
</ng-container>
<!-- PhoneNumber Column -->
<ng-container matColumnDef="phoneNumber">
<mat-header-cell *matHeaderCellDef> Phone Number</mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.phoneNumber}}</mat-cell>
</ng-container>
<!--More Options Column-->
<ng-container *ngIf="!readonly" matColumnDef="more">
<mat-header-cell *matHeaderCellDef class="cell-shrink-more">
</mat-header-cell>
<mat-cell *matCellDef="let element"
class="cell-shrink-more"
(click)="$event.stopPropagation()">
<button mat-icon-button
[matMenuTriggerFor]="posXMenu"
class="mat-24"
aria-label="Open x-positioned menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu xPosition="before" #posXMenu="matMenu">
<button mat-menu-item [disabled]="false" (click)="remove(element)">
<mat-icon>delete</mat-icon>
remove
</button>
</mat-menu>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="contactsDisplayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: contactsDisplayedColumns; matRipple"
(click)="select(row)">
</mat-row>
</mat-table>
</div>
</div>
</div>
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
min-width: 300px;
}
.mat-header-cell.mat-sort-header-sorted {
color: black;
}
mat-sidenav {
//width: 200px;
//height: 500px;
}
mat-sidenav-content {
//margin-left: 250px !important;
}
.mat-table {
overflow: auto;
max-height: 500px;
}
.mat-row {
cursor: pointer;
}
.mat-column-select {
overflow: visible;
}
::ng-deep img {
max-width: 100%;
object-fit: cover;
}
::ng-deep div .avatar {
width: 40px;
line-height: 40px !important;
height: 40px;
font-size: 18px !important;
max-width: 40px;
max-height: 40px;
}
.ngx-avatar {
width: 40px;
min-width: 40px;
height: 40px;
line-height: 40px;
margin: 0 8px 0 0;
border-radius: 50%;
font-size: 17px;
font-weight: 500;
text-align: center;
color: #fff;
}
.cell-shrink-checkbox {
flex-grow: 0.1;
flex-shrink: 1;
flex-basis: 48px;
}
.cell-shrink-more {
flex-grow: 0.1;
}
.fill-remaining {
flex: 1 1 auto;
}
.fab-add {
margin-top: 55px;
margin-right: 20px;
}
::ng-deep .new-contact-dialog {
.mat-dialog-container {
padding: 0 0 24px 0;
min-width: 350px;
}
}