Τρίτη 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;
  }
}

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

Angular Filter array of elements with Pipe








Display filtered array elements by search term with Pipe







1. Component - HTML


.....
 <input [(ngModel)]="searchParam" matInput [type]="'text'"> 

<div *ngFor="let item of allItems | itemFilter : searchParam">
          <your-item-entry [property1]="item.property1" 
                                    [property2]="item.property2"
                                    [property3]="item.property3"
                                    [property4]="item.property4">
           </your-item-entry>

 </div>




2. Component - TS


...
 allItems: IYourItem[] = []
// fetch your items here

searchParam = '';
...



3. Pipe


import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'ItemFilter'
})
export class ItemFilterPipe implements PipeTransform {
  transform(allItems: IYourItem[], searchText: string): any[] {
    if (!allItems) return [];
    if (!searchText) return allItems;

// Returns items containing search value (e.g. "codeinpackets") in either of their fields1-4
    searchText = searchText.toLowerCase();
    return allItems.filter(i => {
      return (i.field1 != null && i.field1.toLowerCase().includes(searchText) ||
        (i.field2 != null && i.field2.toLowerCase().includes(searchText)) ||
        (i.field3 != null && i.field3.toLowerCase().includes(searchText)) ||
        (i.field4 != null && i.field4.toLowerCase().includes(searchText))
      );
    });
  }
}

Τετάρτη 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;
}

Angular Material Form with MatIput, MatSelect, MatRadioGroup, MatRadioButton and Angular Flex Layout








Angular Material Form with MatIput, MatSelect, MatRadioGroup, MatRadioButton and Angular Flex Layout



Prerequisites:
bootstrap v4.3.1
@angular/material v8.2.3
@angular/flex-layout": "^8.0.0-beta.27





1. Component - HTML


<div class="create-exercise-page" fxLayout="column" fxLayoutAlign="start center">
    <br/> <br/>
    <form role="form" (ngSubmit)="submitForm()" [formGroup]="createActivityForm" autocomplete="off">
        <div fxLayout="column" fxLayoutAlign="space-around center">
            <div style="text-align: center">
                <mat-radio-group ngDefaultControl style="width: 600px;text-align: center"
                                 formControlName="selectedActivityType">
                    <mat-radio-button *ngFor="let a of activityTypes" [value]="a.id">
                        {{a.label}}
                    </mat-radio-button>
                </mat-radio-group>
            </div>

            <!--    ACTIVITY NAME -->
            <br/>
            <div>
                <mat-form-field>
                    <input formControlName="actNameInput" name="actNameInput" [type]="'text'" matInput
                           placeholder="Enter name...">
                </mat-form-field>
            </div>
            <br/>
            <br/>
            <!--    SUBTYPE CHOICE-->
            <div style="height: 180px; width: 800px;" fxLayout="row" fxLayoutAlign="center start">
                <div>
                    <br/>
                    <div style="text-align: center">
                        <mat-form-field>
                            <mat-select name="selectedActSubType" placeholder="Choose subType..."
                                        formControlName="selectedActSubType" matSelect>
                                <mat-option *ngFor="let subType of subTypes"
                                            [value]="subType">{{subType}}</mat-option>
                            </mat-select>
                        </mat-form-field>
                    </div>
                </div>
            </div>

            <!--    SUBMIT BUTTON-->
            <br/> <br/>
            <br/>
            <div style="text-align: center">
                <button style="width:200px;height:40px;" type="submit" class="btn btn-primary">
                    <span>Submit</span>
                </button>
            </div>
            <br/>
            <br/>
        </div>
    </form>

</div>



2. Component - TS


import {Component, OnInit} from '@angular/core';
import {FormBuilder} from '@angular/forms';
import {MatFormFieldControl} from '@angular/material/form-field';

interface IActivityType {
  id: number;
  label: string;
}

@Component({
  selector: 'jhi-create-activity',
  templateUrl: './create-activity.component.html',
  styleUrls: ['./create-activity.component.scss'],
  providers: [
    {provide: MatFormFieldControl, useExisting: CreateActivityComponent}
  ]
})
export class CreateActivityComponent implements OnInit {

  createActivityForm = this.fb.group({
    selectedActivityType: [''],
    actNameInput: [''],
    selectedActSubType: [''],
    newSubtype: ['']
  });

  subTypes: String[] = ['BASKET', 'SOCCER', 'TENNIS'];
  activityTypes: IActivityType[] = [{id: 1, label: 'HIKING'}, {id: 2, label: 'SPORTS'}, {id: 3, label: 'SWIMMING'}];

  constructor(private fb: FormBuilder) {}

  ngOnInit() {}

  submitForm(): void {
    const selectedActivityTypeId = this.createActivityForm.get(['selectedActivityType'])!.value;
    const actNameInput = this.createActivityForm.get(['actNameInput'])!.value;
    const selectedActSubType = this.createActivityForm.get(['selectedActSubType'])!.value;
    const newSubtype = this.createActivityForm.get(['newSubtype'])!.value;

    this.yourService.createNewActivity(selectedActivityTypeId, actNameInput, selectedActSubType, newSubtype).subscribe(
      () => (console.log('New Activity Created..')),
      () => (console.log('Error in creating new activity..'))
    );
  }
}



3. Component - CSS



.create-exercise-page {

  height: 1100px;

  width: 500px;

}


.mat-radio-button ~ .mat-radio-button {
  margin-left: 50px;
}

p {
  font-family: Lato;
}