File

src/module/components/contacts/mat-contacts.component.ts

Implements

OnInit OnDestroy OnChanges

Example

Metadata

encapsulation ViewEncapsulation.Emulated
selector mat-contacts
styleUrls mat-contacts.component.scss
templateUrl mat-contacts.component.html

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(dialog: MatDialog)
Parameters :
Name Type Optional
dialog MatDialog No

Inputs

contacts
Type : Contact[]
enableMenu
Type : boolean
isLoading
Type : boolean
readonly
Type : boolean
title
Default value : 'Contacts'

Outputs

onAddingNewContactCanceled
Type : EventEmitter<void>
onContactAdded
Type : EventEmitter<Contact>
onContactRemoved
Type : EventEmitter<Contact>

Methods

add
add(contact: Contact)
Parameters :
Name Type Optional
contact Contact No
Returns : void
applyFilter
applyFilter(filterValue: string)
Parameters :
Name Type Optional
filterValue string No
Returns : void
isAllSelected
isAllSelected()

Whether the number of selected elements matches the total number of rows.

Returns : boolean
masterToggle
masterToggle()

Selects all rows if they are not all selected; otherwise clear selection.

Returns : void
ngOnChanges
ngOnChanges(changes: SimpleChanges)
Parameters :
Name Type Optional
changes SimpleChanges No
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
openAddDialogContainer
openAddDialogContainer(method?: Methods, contact?: Contact)
Parameters :
Name Type Optional
method Methods Yes
contact Contact Yes
Returns : void
remove
remove(contact: Contact)
Parameters :
Name Type Optional
contact Contact No
Returns : void
removeSelected
removeSelected()
Returns : void
select
select(row: any)
Parameters :
Name Type Optional
row any No
Returns : void

Properties

contactsDataSource
Type : MatTableDataSource<Contact>
contactsDisplayedColumns
Type : []
Default value : ['name', 'email', 'phoneNumber']
Public dialog
Type : MatDialog
dialogAfterCloseSubscription
Type : any
dialogRef
Type : MatDialogRef<MatContactDialogComponent> | null
filter
Type : Filter
methods
Default value : Methods
selection
Default value : new SelectionModel<Contact>(true, [])
sort
Type : MatSort
Decorators :
@ViewChild(MatSort)
table
Type : MatTable<any>
Decorators :
@ViewChild(MatTable)
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>

mat-contacts.component.scss

.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;
  }
}

Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""