Εμφάνιση αναρτήσεων με ετικέτα Angular 8. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα Angular 8. Εμφάνιση όλων των αναρτήσεων

Τρίτη 26 Μαΐου 2020

Angular Copy Text to Clipboard








Copy text to Clipboard from multiple UI elements


Handle case of <span/> inner text

Generic method to handle all elements








1. Component - HTML



<div fxLayout="row" fxLayoutAlign="space-between center" style="padding: 50px;">
    <div><span>NAME: </span> <span id="nameEl{{name}}">{{name}}</span></div>
    <div (click)="copyToClipboard('nameEl'+name)"><mat-icon fontSet="material-icons-outlined">bookmarks</mat-icon></div>
</div>
<div fxLayout="row" fxLayoutAlign="space-between center" style="padding: 50px;">
    <div><span>SURNAME: </span> <span id="surnameEl{{surname}}">{{surname}}</span></div>
    <div (click)="copyToClipboard('surnameEl'+surname)"><mat-icon fontSet="material-icons-outlined">bookmarks</mat-icon></div>
</div>



2. Component - TS


import { Component, OnInit } from "@angular/core";
@Component({ selector: "jhi-test-comp", templateUrl: "./test-comp.component.html", styleUrls: ["./test-comp.component.scss"] })
export class TestCompComponent implements OnInit {
    name = "Myname";
    surname = "MySurname";
    constructor() {}
    ngOnInit(): void {}
    // Handles case of span element  
    // Copy span text to temporary input  
    // Execute command to copy to clipboard  
   copyToClipboard(id: string): void {   
      const copyEl = document.getElementById(id);    
      const inputHelper = document.createElement('input');
      if (inputHelper !== null && copyEl !== null && copyEl.textContent !== null) {
         // Remove white spaces      
         inputHelper.value = copyEl.textContent.replace(/\s/g, '');
         document.body.appendChild(inputHelper);
         inputHelper.select();
         document.execCommand('Copy');
         inputHelper.remove();  
      }
}


Τρίτη 19 Μαΐου 2020

Skeleton gray views with animation, while loading backend data








Show skeleton views to the user, until all data are fetched


1- Loading...

2 - Loading completed





1. Component - HTML


<div>
    <br/><br/>
    <p>All cars:</p>
    <br/>
    <div *ngFor="let car of cars">
        <div fxLayout="row" fxLayoutAlign="space-between center" style="width:70%; padding-left: 20px">
            <div [ngClass]="{'skeleton': this.loading}">{{car.name}}</div>
            <div [ngClass]="{'skeleton': this.loading}">{{car.brand}}</div>
            <div [ngClass]="{'skeleton': this.loading}">{{car.type}}</div>
        </div>
        <br/>
    </div>
</div>



2. Component - TS


import {Component, OnInit} from '@angular/core';

interface ICar {
  name: string,
  brand: string,
  type: string
}
@Component({
  selector: 'jhi-test-comp',
  templateUrl: './test-comp.component.html',
  styleUrls: ['./test-comp.component.scss']
})
export class TestCompComponent implements OnInit {
  loading = true;
  SKELETON_CARS = [{name: 'xxx', brand: 'xxx', type: 'xxxx'}, {name: 'xxxx', brand: 'xxxxxx', type: 'xxxxxx'}, {name: 'xxxxxxx', brand: 'xxxxxxxx', type: 'xxxxxxxxx'},
    {name: 'xxxxxxxx', brand: 'xxxxxxx', type: 'xxxxxxxxx'}, {name: 'xxxxxxxx', brand: 'xxxxxxx', type: 'xxxxxxxx'}, {name: 'xxxxxxxx', brand: 'xxxxxxxx', type: 'xxxxxxx'}];
  cars: ICar[] = this.SKELETON_CARS;
  constructor() {
  }
  ngOnInit(): void {
    setTimeout(() => {
      this.cars = [{name: 'i10', brand: 'Hyundai', type: 'gasoline'}, {name: 'swift', brand: 'Suzuki', type: 'gasoline'}, {name: 'Y', brand: 'Tesla', type: 'electric'},
        {name: 'Almera', brand: 'Nissan', type: 'gasoline'},{name: 'Primera', brand: 'Nissan', type: 'gasoline'},{name: 'Civic', brand: 'Honda', type: 'gasoline'},{name: 'Romeo', brand: 'Alfa', type: 'gasoline'},
        {name: 'Golf', brand: 'VW', type: 'gasoline'},{name: 'Impreza', brand: 'Subaru', type: 'gasoline'}];
      this.loading = false;
    }, 3600);
  }
}





3. Component - CSS


.mat-button {
  border-radius: 30px;
  font-size: 2.08vh;
  background-color: #3339ea;
  color: white;
  width: 10%;
}
.skeleton {
  animation: skeleton-loading 1.7s infinite linear;
  background-color: #dee0e2 !important;
  color: rgba(0, 0, 0, 0) !important;
  background-image: -webkit-linear-gradient(to right, #dee0e2 0%, #ebeced 20%, #dee0e2 40%, #dee0e2 100%);
  background-image: linear-gradient(to right, #dee0e2 0%, #ebeced 20%, #dee0e2 40%, #dee0e2 100%);
  background-repeat: no-repeat;
}


Σάββατο 16 Μαΐου 2020

Conditional HTML display with ngTemplate








Show/hide HTML with ngTemplate


If no elements are retrieved, show "No Data"message.

To simulate this, click on Toggle to update a 'noData' flag

Clicking on Toggle simulates no-data case:





1. Component - HTML


<button mat-button (click)="toggleDisplay()"> Toggle </button>
<div *ngIf="this.noData; then noDataMessage else mainDisplay"></div>
<ng-template #mainDisplay>
    <br/><br/>
    <p>All cars:</p>
    <br/>
    <div *ngFor="let car of cars">
        {{car.name}} - {{car.brand}} - {{car.type}}
    </div>
</ng-template>
<ng-template #noDataMessage>
    <br/><br/>
    <div>
    No data available
    </div>
</ng-template>




2. Component - TS


import {Component, OnInit} from '@angular/core';

interface ICar {
  name: string,
  brand: string,
  type: string
}
@Component({
  selector: 'jhi-test-comp',
  templateUrl: './test-comp.component.html',
  styleUrls: ['./test-comp.component.scss']
})
export class TestCompComponent implements OnInit {
  noData = false;
  cars: ICar[] = [{name: 'i10', brand: 'Hyundai', type: 'gasoline'}, {name: 'swift', brand: 'Suzuki', type: 'gasoline'}, {name: 'Y', brand: 'Tesla', type: 'electric'}];
  constructor() {
  }
  ngOnInit(): void {
  }
  toggleDisplay(): void {
    this.noData = !this.noData;
  }
}



3. Component - CSS

.mat-button {
  border-radius: 30px;
  font-size: 2.08vh;
  background-color: #3339ea;
  color: white;
  width: 10%;
}


Πέμπτη 14 Μαΐου 2020

Display Child components inside a Parent component








Display Child components inside a Parent component








1. Parent Component - HTML

<div fxLayout="column" fxLayoutAlign="space-between stretch">

    <div *ngIf="allPersonItems.length">
        <div class="person-entry-container">
            <div class="person-card" *ngFor="let person of allPersonItems">
                <jhi-person-entry [name]="person.name"
                                    [surname]="person.surname"
                                    [address]="person.address">
                </jhi-person-entry>
            </div>
        </div>

    </div>


</div>



2. Parent Component - TS


import {Component, OnInit} from '@angular/core';


export interface IPersonItem {
  name: string;
  surname: string;
  address?: string;
}

@Component({
  selector: 'jhi-contracts-overview',
  templateUrl: './contracts-overview.component.html',
  styleUrls: ['./contracts-overview.component.scss']
})
export class PersonsOverviewComponent implements OnInit {

  allPersonItems: IPersonItem[] = [
    {name: 'Peter', surname: 'Schmidt', address: 'Frankfurt'},
    {name: 'Amanda', surname: 'Anderson', address: 'New York'},
    {name: 'George', surname: 'Black', address: 'Dublin'}
  ];

  constructor() {}

  ngOnInit(): void {}
}


3. Parent Component - CSS

@mixin card-view {
  border-radius: 1vw;
  background-color: #ffffff;
}

.person-card {
  @include card-view;
  margin: 1.23vh 1vw;
  display: block;
}

.person-entry-container {
  height: 70vh;
  overflow: scroll;
  padding-left: 1.3vh;
  padding-right: 1.3vh;
}


4. Child Component - HTML

<div class="person-entry" fxLayout="row" fxLayoutAlign="start center">
    <div fxFlex="8"><mat-icon>phone_android</mat-icon></div>
    <div fxFlex="57">
        <div class="gray-style">{{name}}</div>
        <div class="bold margin-top">{{surname}}</div>
        <div class="gray-style margin-top">{{address}}</div>
    </div>
</div>



5. Child Component - TS

import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'jhi-person-entry',
  templateUrl: './person-entry.component.html',
  styleUrls: ['./person-entry.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class PersonEntryComponent implements OnInit {
  @Input()
  name = '';
  @Input()
  surname = '';
  @Input()
  address = '';

  constructor() {}

  ngOnInit(): void {}
}


6. Child Component - CSS

.person-entry {
  width: 100%;
  padding: 1.56vh;

  .margin-top {
    margin-top: 0.5vh;
  }

  .gray-style {
    color: gray;
  }

  .mat-icon {
    font-size: 6vw;
  }

  div {
    font-size: 2.08vh;
    line-height: 2.6vh;
  }

  .bold {
    font-weight: bold;
  }
}

Τετάρτη 6 Μαΐου 2020

Dynamic CSS with Angular ViewChild, Renderer2








Control element CSS programmatically with ViewChild and Renderer2


1. Input is visible initially

2. Input is hidden with ViewChild




1. Component - HTML


<div>
    Container...
    <div #elementRef>
        <input matInput type="text" value="some data.."/>
        <button mat-button>&nbsp;</button>
    </div>

</div>





2. Component - TS


import {AfterViewInit, Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';

@Component({
  selector: 'jhi-test-comp',
  templateUrl: './test-comp.component.html',
  styleUrls: ['./test-comp.component.scss']
})
export class TestCompComponent implements OnInit,AfterViewInit {

  @ViewChild('elementRef', { static: false })
  elementRef?: ElementRef;

  constructor(private renderer: Renderer2) { }

  ngOnInit(): void {}

  ngAfterViewInit(): void {
    if (this.elementRef) {
      this.renderer.setStyle(this.elementRef.nativeElement, 'display', 'none');
    }
  }
}


Κυριακή 3 Μαΐου 2020

Angular Bootstrap Navigation/Tab pills








Tab navigation with Bootstrap, ngSwitch and Material




Prerequisites:
bootstrap v4.3.1
@angular/material v8.2.3




1. Component - HTML


<div class="tab-page">
    <div fxLayout="row" fxLayoutAlign="center start">
        <div>
            <ul class="nav nav-pills">
                <li><a [class.nav-link]="true" [class.active]="viewMode=='tab1'" (click)="viewMode='tab1'">Tab-1</a></li>
                <li><a [class.nav-link]="true" [class.active]="viewMode=='tab2'" (click)="viewMode='tab2'">Tab-2</a></li>
                <li><a [class.nav-link]="true" [class.active]="viewMode=='tab3'" (click)="viewMode='tab3'">Tab-3</a></li>
            </ul>

            <mat-divider></mat-divider>

            <div [ngSwitch]="viewMode">
                <div *ngSwitchCase="'tab1'"> Tab 1 content...</div>
                <div *ngSwitchCase="'tab2'"> Tab 2 content...</div>
                <div *ngSwitchCase="'tab3'"> Tab 3 content...</div>
            </div>
        </div>
    </div>


</div>


2. Component - TS


import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'tabs-page',
  templateUrl: './tabs-page.component.html',
  styleUrls: ['./tabs-page.component.scss']
})
export class TabsPageComponent implements OnInit {

  viewMode: string;

  constructor() { }

  ngOnInit() {
  }

}



3. Component - CSS


.nav-pills {
  font-size: 30px;
}

.tab-page{
  height: 500px;
}

Τετάρτη 22 Απριλίου 2020

Angular - Redirect to Component with pop-up Material Dialog






Show Component with MatDialog service as popup



Prerequisites:
@angular/material v8.2.3







1. Redirection Service - TS


...
  constructor(public dialog: MatDialog, private location: PlatformLocation, public translate: TranslateService) {
    this.location.onPopState(() => {
      const length = this.dialog.openDialogs.length;
      if (!this.ignorePop) {
        if (length > 0) {
          this.dialog.openDialogs[length - 1].componentInstance.closePopup();
        }
      } else {
        this.ignorePop = false;
      }
    });
  }

// Method to open component 
  open(title: string, componentType: string, options?: { [key: string]: any }): void {
    let translate = this.translate.instant(title);
    if (translate.includes('[' + title + ']')) {
      translate = title;
    }
    const dialogRef = this.dialog.open(RedirectionDialogComponent, {
      data: { title: translate, component: getComponent(componentType)},
      maxWidth: '90%',
      width: '90%',
      height: 98.5 - this.dialog.openDialogs.length * 1.5 + '%',
      hasBackdrop: false,   
     closeOnNavigation: false,
    });


  }
.......



2. Redirection Dialog Component - TS


export interface DialogData {

  title: string;

  component: Type<any>;

  parameters: { [key: string]: any };
}

@Component({
  selector: 'redirection-dialog',
  templateUrl: './redirection-dialog.component.html',
  styleUrls: ['./redirection-dialog.component.scss']
})
export class RedirectionDialogComponent implements OnInit {

  @ViewChild('contentComponent', { static: true, read: ViewContainerRef }) contentComponent!: ViewContainerRef;

  constructor(
    public dialogRef: MatDialogRef<RedirectionDialogComponent >,
    @Inject(MAT_DIALOG_DATA) public data: DialogData,
    public dialog: MatDialog,
    private componentFactoryResolver: ComponentFactoryResolver,
    private el: ElementRef
  ) {}

  ngOnInit(): void {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.data.component);
    this.contentComponent.clear();
    const compRef = this.contentComponent.createComponent(componentFactory);
  }
}


3. Redirection Dialog Component - HTML

<div>
    <h1> {{data.title}} </h1>
<mat-dialog-content>
    <div #contentComponent></div>
</mat-dialog-content>
</div>

3.1 Example of use:

In your code:
...
this.redirectionService.open('Your component title', 'YourComponent');
...

Πέμπτη 16 Απριλίου 2020

Angular Material accordion/expansion deletable panels






Panels/Cards in Accordion :



Cards layout (also deletable)
Open first panel:


Open second panel: 




Prerequisites:
@angular/material v8.2.3




1. Component - HTML


         <div *ngIf="banks.length" class="bank-accounts-overview">
                <mat-accordion>
                    <div *ngFor="let bank of banks; let i = index;">
                        <div id="id{{i}}">
                            <mat-expansion-panel hideToggle="true" #xyz>
                                <mat-expansion-panel-header [collapsedHeight]="'12vh'" [expandedHeight]="'12vh'">
                                    <mat-panel-title>
                                        <div class="bank-header-entry">
                                            <div class="bank-entry" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="2.31vw">
                                                <span>{{bank.bankName}}</span>
                                                <span class="balance">{{bank.totalBalance}} €</span>
                                            </div>
                                        </div>
                                    </mat-panel-title>
                                    <mat-panel-description>
                                        <div style="position:absolute; right:0; margin-right: 10px;">
                                        <button (click)=deleteCard(i) type="button" mat-icon-button matSuffix>
                                            <mat-icon>delete_forever</mat-icon>
                                        </button>
                                        </div>
                                    </mat-panel-description>
                                </mat-expansion-panel-header>
                                <div *ngFor="let account of bank.accounts">
                                    <mat-divider></mat-divider>
                                    <div class="jhi-bank-account-entry" matRipple>
                                        <div class="bank-account-entry" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="2.70vw">
                                            <mat-icon>arrow_forward</mat-icon>
                                            <span>{{account.accountName}}</span>
                                            <span class="balance">{{account.balance}} € </span>
                                        </div>
                                    </div>
                                </div>

                            </mat-expansion-panel>
                        </div>
                        <br/>
                    </div>
                </mat-accordion>

            </div>




2. Component - TS


@Component({
  selector: 'accounts-overview',
  templateUrl: './accounts-overview.component.html',
  styleUrls: ['./accounts-overview.component.scss']
})
export class AccountsOverviewComponent implements OnInit {
  banks: any[] = [];

  constructor(private renderer: Renderer2) {

    this.banks = [
      {
        bankName: 'Bank 1',
        icon: 'account_box',
        totalBalance: 214000,
        hide: false,
        accounts: [{accountId:'1',bankName:'Bank 1',icon:'account_box',accountName:'Account 1',balance:20000},
          {accountId:'1',bankName:'Bank 1',icon:'account_box',bankLoginId:'idxxx',accountName:'Account 1',balance:432000,iban:'XXXA4RRT5',bic:'Bic',hidden:false}]
      },
      {
        bankName: 'Bank 2',
        icon: 'account_box',
        totalBalance: 1645000,
        hide: false,
        accounts: [{accountId:'1',bankName:'Bank 1',icon:'account_box',accountName:'Account 1',balance:17000},
          {accountId:'1',bankName:'Bank 1',icon:'account_box',bankLoginId:'idxxx',accountName:'Account 1',balance:4.000,iban:'XXXA4RRT5',bic:'Bic',hidden:false}]
      },
      {
        bankName: 'Bank 3',
        icon: 'account_box',
        totalBalance: 145.000,
        hide: false,
        accounts: [{accountId:'1',bankName:'Bank 1',icon:'account_box',accountName:'Account 1',balance:150.000},
          {accountId:'1',bankName:'Bank 1',icon:'account_box',bankLoginId:'idxxx',accountName:'Account 1',balance:12.000,iban:'XXXA4RRT5',bic:'Bic',hidden:false}]
      }
    ];
  }

  ngOnInit(): void {
  }

  deleteCard(id: number): void {
    const parent: HTMLElement | null = document.getElementById('id'+id);
    if (parent !== null) {
      const child = parent.children[0];
      this.renderer.removeChild(parent, child);
    }
  }
}



3. Component - CSS


.bank-header-entry {
  width: 80vw;
}

.bank-accounts-overview {
  padding: 1.5vh;
}

.jhi-bank-account-entry {
  padding-top: 3.9vh;
  padding-bottom: 3.9vh;
  padding-left: 30px;
}

button.mat-raised-button {
  height: 6.25vh;
  font-size: 2.08vh;
  line-height: 2.6vh;
  border-radius: 0;
}

h1 {
  font-size: 2.92vh;
  line-height: 3.64vh;
  text-align: center;
  width: 100%;
}

@media (hover: hover) {
  .jhi-bank-entry:hover,
  .jhi-bank-account-entry:hover {
    background-color: #d2d2d2;
  }
}


.bank-entry {
  width: 100%;
}

.logo {
  width: 5.21vh;
  height: 5.21vh;
}

span {
  font-size: 2.08vh;
  line-height: 2.6vh;
  font-weight: bold;
}

.balance {
  margin-left: auto;
}

.bank-account-entry {
  width: 90%;
}