Skip to content

Instantly share code, notes, and snippets.

@kayode-adechinan
Last active January 17, 2020 17:18
Show Gist options
  • Save kayode-adechinan/1690a988656e1f3e7746ed17f9ad4536 to your computer and use it in GitHub Desktop.
Save kayode-adechinan/1690a988656e1f3e7746ed17f9ad4536 to your computer and use it in GitHub Desktop.
Angular Cheat Sheet

if

<div *ngIf="condition; else elseBlock">Content to render when condition is true.</div>
<ng-template #elseBlock>Content to render when condition is false.</ng-template>

ngFor

<ul>
    <li *ngFor="let element of array"></li>
</ul>

ngSwitch

<ng-container [ngSwitch]="switch_expression">
    <p *ngSwitchCase="match_expression_1"> 1 </p>
    <p *ngSwitchCase="match_expression_2"> 2 </p>
    <p *ngSwitchDefault> default </p>
</ng-container>

routing

<a routerLink="/path">

<a [routerLink]="[ '/path', routeParam ]">

<a [routerLink]="[ '/path', 
      { matrixParam: 'value' } ]">

<a [routerLink]="[ '/path' ]" 
     [queryParams]="{ page: 1 }">

<a [routerLink]="[ '/path' ]" 
     fragment="anchor">

routing

constructor(private router: Router){
        this.router.navigate(['/catalog/search', this.searchTerm], {queryParams: {page: newPage}});
}

routing

constructor(private route: ActivatedRoute){
  let id = route.snapshot.paramMap.get('id');
}

routing

const routes: Routes = [
  {path: '', pathMatch: 'full', redirectTo: 'products' },
  {path:'products', component:ListProductComponent},
  {path:'**', redirectTo: 'products' }
];

two way data biding

import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
          <p>
          Foreground: <input [(ngModel)]="fg" />
          </p>
          <p>
          Background: <input [(ngModel)]="bg" />
          </p>
        `,
  styles: []
})
export class AppComponent {
  fg = "#ffffff";
  bg = "#000000";
}

Components communication

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

@Component({
  selector: 'app-parent',
  template: `
    Message: {{message}}
    <app-child (messageEvent)="receiveMessage($event)"></app-child>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  constructor() { }

  message:string;

  receiveMessage($event) {
    this.message = $event
  }
}

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

@Component({
  selector: 'app-child',
  template: `
      <button (click)="sendMessage()">Send Message</button>
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  message: string = "Hola Mundo!"

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  sendMessage() {
    this.messageEvent.emit(this.message)
  }
}

validators

// inside a validators.ts
import { AbstractControl, FormControl } from '@angular/forms';
import { UserService } from '../../user/user.service';
import { timer } from 'rxjs';
import { switchMap ,map } from 'rxjs/operators'; 


export const ValidateUrl = (control: AbstractControl) => {
  if (!control.value.startsWith('https') || !control.value.includes('.io')) {
    return { validUrl: true };
  }
  return null;
}

export const emailExist = 
  (userService: UserService, time: number = 500) => {
    return (input: FormControl) => {
      return timer(time).pipe(
        switchMap(() => userService.checkIfEmalAlreadyExists(input.value)),
        map(res => {
          console.log(res);
          return !res.status ? null : {emailExist: true};
        })
      );
    };
  };

form

// import in app.module.ts
// import { ReactiveFormsModule, FormsModule } from '@angular/forms';
// imports:[
        // ....
//         ReactiveFormsModule,
//         FormsModule,
//    ]

// inside a form.component.ts
@Component({
  selector: 'app-child',
  template: `
  
  <form [formGroup]="createUserForm" (ngSubmit)="onSubmit(createUserForm)">

    <input type="email" formControlName="email" class="form-control" />
    <div style="color:red" *ngIf=" f.email.errors && (f.email.dirty || f.email.touched) && f.email.errors.required">
        Required
    </div>
    
    <div style="color:red" *ngIf=" f.email.errors && (f.email.dirty || f.email.touched) && f.email.errors.pattern">
        Invalid email
    </div>

    <div style="color:red" *ngIf="f.email.errors && (f.email.dirty || f.email.touched) && f.email.errors.emailExist">
        Email already exist
    </div>

    <button type="submit" class="btn btn-primary" [disabled]="createUserForm.invalid">
        Register
    </button>

  </form>
      
  `,
  styleUrls: []
})
export class AddComponent implements OnInit {

  createUserForm: FormGroup;
  submitted = false;
  
  constructor(private userService: UserService, private fb:FormBuilder) { }

  ngOnInit() {
    this.createUserForm = this.fb.group({
      websiteUrl: ['', [Validators.required, ValidateUrl]],
      email:['', [Validators.required,  Validators.pattern("^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$")], 
      [emailExist(this.userService)]],
      password:['', [Validators.required]]
    })
  }
  
  // convenience getter for easy access to form fields
  get f() { return this.createUserForm.controls; }
  
  
 onSubmit(form: FormGroup) {
    if(form.valid){
      console.log(form);
    }
  }
}

dynamically add row

//product.ts
export class Product {
    name: string
    selling_points: SellingPoint[]
}

export class SellingPoint {
    selling_point: string
}
@Component({
  selector: 'app-product-form',
  templateUrl: './product-form.component.html',
  styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent implements OnInit {

  constructor(private fb: FormBuilder) { }

  productForm: FormGroup;

  ngOnInit() {

    /* Initiate the form structure */
    this.productForm = this.fb.group({
      title: [],
      selling_points: this.fb.array([this.fb.group({point:''})])
    })
  }

  get sellingPoints() {
    return this.productForm.get('selling_points') as FormArray;
  }

  /////// This is new /////////////////

  addSellingPoint() {
    this.sellingPoints.push(this.fb.group({point:''}));
  }

  deleteSellingPoint(index) {
    this.sellingPoints.removeAt(index);
  }

  //////////// End ////////////////////
}
<h1>Edit Product</h1>

<form [formGroup]="productForm">

  <label>
    Title: <input formControlName="title" />
  </label>
  <h2>Selling Points</h2>

  <div formArrayName="selling_points">
    <div *ngFor="let item of sellingPoints.controls; let pointIndex=index" [formGroupName]="pointIndex">
    <label>
      Selling Point: <input formControlName="point" />
    </label>
    <button type="button" (click)="deleteSellingPoint(pointIndex)">Delete Selling Point</button>
    </div>
    <button type="button" (click)="addSellingPoint()">Add Selling Point</button>
  </div>

</form>

{{ this.productForm.value | json }}

http requests

// import in app.module.ts
// import { HttpClientModule } from '@angular/common/http';
// imports:[
        // ....
//         HttpClientModule
//    ]

// Make request inside a search.service.ts

// import { HttpClient } from '@angular/common/http';


//add
add(software:Software){
    return this.http.post<Software>(`${this.BASE_URL}/softwares/`, software);
}

//search
search(params:any){
    return this.http.get<any>(`${this.BASE_URL}/softwares/`, {params:params})
}

viewchild & element ref

@Component({
  selector: 'hello',
  template: `<h1>Hello !</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
}



@Component({
  selector: 'my-app',
  template: `
  <hello name=""></hello>
  <p #pRef>
    Start editing to see some magic happen :)
  </p>
  <hello  name="Angular 6"  ></hello>
  <hello  name="Angular 7"  ></hello>
   <hello  name="Angular 8"  ></hello>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
  name = 'Angular';
  @ViewChild(HelloComponent, {static: false}) hello: HelloComponent;
  @ViewChild('pRef', {static: false}) pRef: ElementRef;
  @ViewChildren(HelloComponent) hellos: QueryList<any>;


  ngAfterViewInit() {
    console.log('Hello ', this.hello.name); 
    console.log(this.pRef.nativeElement.innerHTML); 
    this.pRef.nativeElement.innerHTML = "DOM updated successfully!!!"; 
    this.hellos.forEach(hello => console.log(hello));
  }
}

pipe

// add to  app-module.ts
//declarations:[
    //...
//    EllipsisPipe,
//    PriceFormatPipe
    //...

//]

//common/ellipsis.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'ellipsis'
})
export class EllipsisPipe implements PipeTransform {

  transform(value: any, ...args: any[]): any {

    if (args === undefined) {
      return value;
    }

    if (value.length > args) {
      return value.substring(0, args) + '...';
    } else {
      return value;
    }

  }

}

// common/price-format.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'priceFormat'
})
export class PriceFormatPipe implements PipeTransform {

  transform(value: any, ...args: any[]): any {
    return new Intl.NumberFormat("fr-FR", { style: "currency", currency: "XOF" }).format(value);
  }

}

state

// counter.service.ts
interface Count {
  value: number;
}

@Injectable()
export class CounterService {

  constructor() { }

  private initialCount: Count = {value: 0};
  private countTracker = new BehaviorSubject<Count>(this.initialCount);

  /** Allows subscription to the behavior subject as an observable */
  getCount(): Observable<Count> {
    return this.countTracker.asObservable();
  }

  /**
   * Allows updating the current value of the behavior subject
   * @param val a number representing the current value
   * @param delta a number representing the positive or negative change in current value
   */
  setCount(val: number, delta: number): void {
    this.countTracker.next({value: (val + delta)});
  }

  /** Resets the count to the initial value */
  resetCount(): void {
    this.countTracker.next(this.initialCount);
  }
}

//counter.component.ts
export class CounterComponent implements OnInit, OnDestroy {

  constructor(private counter: CounterService) { }

  currentCount: number;
  subscription;

  ngOnInit(): void {
    this.subscription = this.counter.getCount().subscribe(
      res => {
        this.currentCount = res.value;
      },
      err => {
        console.error(`An error occurred: ${err.message}`);
      }
    );
  }

  increment(): void {
    this.counter.setCount(this.currentCount, 1);
  }

  decrement(): void {
    this.counter.setCount(this.currentCount, -1);
  }

  reset(): void {
    this.counter.resetCount();
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

state

@Injectable()
export class SettingsState {

  private updating$ = new BehaviorSubject<boolean>(false);
  private cashflowCategories$ = new BehaviorSubject<CashflowCategory[]>(null);

  isUpdating$() {
    return this.updating$.asObservable();
  }

  setUpdating(isUpdating: boolean) {
    this.updating$.next(isUpdating);
  }

  getCashflowCategories$() {
    return this.cashflowCategories$.asObservable();
  }

  setCashflowCategories(categories: CashflowCategory[]) {
    this.cashflowCategories$.next(categories);
  }

  addCashflowCategory(category: CashflowCategory) {
    const currentValue = this.cashflowCategories$.getValue();
    this.cashflowCategories$.next([...currentValue, category]);
  }

  updateCashflowCategory(updatedCategory: CashflowCategory) {
    const categories = this.cashflowCategories$.getValue();
    const indexOfUpdated = categories.findIndex(category => category.id === updatedCategory.id);
    categories[indexOfUpdated] = updatedCategory;
    this.cashflowCategories$.next([...categories]);
  }

  updateCashflowCategoryId(categoryToReplace: CashflowCategory, addedCategoryWithId: CashflowCategory) {
    const categories = this.cashflowCategories$.getValue();
    const updatedCategoryIndex = categories.findIndex(category => category === categoryToReplace);
    categories[updatedCategoryIndex] = addedCategoryWithId;
    this.cashflowCategories$.next([...categories]);
  }

  removeCashflowCategory(categoryRemove: CashflowCategory) {
    const currentValue = this.cashflowCategories$.getValue();
    this.cashflowCategories$.next(currentValue.filter(category => category !== categoryRemove));
  }
}

authentication

// auth.service.ts
@Injectable({
  providedIn: 'root'
})
export class AuthService {

  BASE_URL = "http://localhost:8080/api/auth";

  constructor(private http:HttpClient, private router:Router) {}

  signUp(user:User){
    return this.http.post(`${this.BASE_URL}/signup`, user);
  }

  signIn(user:User){
    return this.http.post<any>(`${this.BASE_URL}/signin`, user);
  }

  signOut(){
    localStorage.removeItem('token');
    localStorage.removeItem('user');
    this.router.navigate(['/'])
  }

  isAuthenticated(){
    let token = localStorage.getItem('token');
    return !(token === null);
  }

  currentUser(){
    return JSON.parse(localStorage.getItem('user'));
  }

  currentUserId(){
    let user  = JSON.parse(localStorage.getItem('user'));
    return parseInt(user.id);
  }
}

// can-activate.guard.ts
import { Injectable } from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router} from '@angular/router';
import { Observable } from 'rxjs';
import {AuthService} from "../auth/auth.service";
@Injectable({
  providedIn: 'root'
})
export class CanActivateGuard implements CanActivate {
  constructor(private auth:AuthService, private router: Router){

  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (!this.auth.isAuthenticated()) {
      this.router.navigate(['sign-in']);
      return false;
    }
    return true;
  }


}

// token interceptor
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {

  constructor() { }

  intercept(req: HttpRequest<any>, next: HttpHandler) {
  
    if (localStorage.getItem('token')) {
      req = req.clone({
        setHeaders: {
          Authorization: localStorage.getItem('token')
        }
      })
    }

    return next.handle(req);

  }
}

// app.module.ts
providers: [CanActivateGuard,

    { provide: HTTP_INTERCEPTORS, useClass:
      TokenInterceptorService, multi: true }

  ],
  
// app-routing
const routes: Routes = [
 {path:'dashboard', component:DashboardComponent, canActivate:[CanActivateGuard]},
]

jquery

import * as $ from 'jquery'  
export class AppComponent implements OnInit { 
    ngOnInit(){ 
    $(document).ready(function(){ 
                $("button").click(function(){ 
                    alert(‘GeeksForGeeks’); 
                }); 
            }); 
    } 
 } 
 
 # bootstrap
 ```json
  "styles": [
              "src/styles.css",
              "node_modules/bootstrap/dist/css/bootstrap.min.css"
            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.min.js",
              "node_modules/bootstrap/dist/js/bootstrap.min.js"
            ]

Validating image dimensions

AddFilesToFormData(event: any, fileName: string) {
  if (event.target.files && event.target.files.length) {
    for (const file of event.target.files) {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        const img = new Image();
        img.src = reader.result as string;
        img.onload = () => {
          const height = img.naturalHeight;
          const width = img.naturalWidth;
          console.log('Width and Height', width, height);
        };
      };
    }
  }
}

Image preview

Image upload & preview

// file.service.ts
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class FileService {

  BASE_URL = 'http://localhost:8080/api/v1';

  constructor(private http: HttpClient) { }

  upload(data):Observable<any>{
    return this.http.post<any>(`${this.BASE_URL}/files`, data);
  }

  multiUpload(data):Observable<any>{
    return this.http.post<any>(`${this.BASE_URL}/files/multi-upload`, data);
  }


}

// add.component.ts

url:any;
constructor(private fileService:FileService) { }

onFileChange(event) {
    if (event.target.files.length > 0) {
      this.fileName = event.target.files[0];

      let reader = new FileReader();

      reader.readAsDataURL(event.target.files[0]); // read file as data url

      reader.onload = (event) => { // called once readAsDataURL is completed
        this.url = reader.result;
      }
    }
}

onSubmit(form:FormGroup){

    const formData = new FormData();
    formData.append('file', this.fileName);
    this.fileService.upload(formData).subscribe(
      data=> {

        form.value['image'] = data['url'];
        form.value['userId'] = this.auth.currentUserId();

        this.portfolioService.save(form.value)
          .subscribe(data => {
            console.log(data);
            this.router.navigate(['/dashboard'])
          }, err => console.log(err))
      },
      error => console.log(error)
    )

  }

// add.component.html
// <img *ngIf="url" [src]="url" height="200"> <br/>

Tdd

// pipe
describe('TroncaturePipe', () => {
  it('create an instance', () => {
    const pipe = new TroncaturePipe(); // * pipe instantiation
    expect(pipe).toBeTruthy();
  });

  it('truncate a string if its too long (>20)', () => {
    // * arrange
    const pipe = new TroncaturePipe();
    // * act
    const ret = pipe.transform('123456789123456789456666123');
    // * asser
    expect(ret.length).toBe(20);
  });
});

//
// shared/post.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';

export interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

@Injectable({
  providedIn: 'root'
})

export class PostService {
  REST_API: string = 'https://jsonplaceholder.typicode.com/posts';

  constructor(private http: HttpClient) { }

  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>(`${this.REST_API}`)
  }
}

//
import { TestBed, async, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { PostService } from './post.service';

describe('PostService', () => {
  let postService: PostService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
      ],
      providers: [
        PostService
      ],
    });

    postService = TestBed.get(PostService);
    httpMock = TestBed.get(HttpTestingController);
  });

  it(`should fetch posts as an Observable`, async(inject([HttpTestingController, PostService],
    (httpClient: HttpTestingController, postService: PostService) => {

      const postItem = [
        {
          "userId": 1,
          "id": 1,
          "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
          "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
        },
        {
          "userId": 1,
          "id": 2,
          "title": "qui est esse",
          "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
        },
        {
          "userId": 1,
          "id": 3,
          "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
          "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
        }
      ];


      postService.getPosts()
        .subscribe((posts: any) => {
          expect(posts.length).toBe(3);
        });

      let req = httpMock.expectOne('https://jsonplaceholder.typicode.com/posts');
      expect(req.request.method).toBe("GET");

      req.flush(postItem);
      httpMock.verify();

    })));
});
``
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment