26. Cookbook Recipes

Parent Child Component Interaction:

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

@Component({
  selector: 'app-article',
  template: `
      <p>{{ currentDate | date }}</p>
      <h1>{{ title }}</h1>
      <app-attribution [author]="author"></app-attribution>
  `,
  styles: []
})
export class ArticleComponent {
  currentDate: Date;
  title: string;
  author: string;

  constructor() {
    this.currentDate = new Date();
    this.title = `The Will to Win is nothing without the Will to Prepare`;
    this.author = 'Nils- Holger';
  }

}

@Component({
  selector: 'app-attribution',
  template: `
  <h3>Written by: {{ author }}</h3>
  `,
  styles: [``]
})
export class AttributionComponent {
@Input() author: string;

}

Binding to Native Element Attribute:

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

@Component({
  selector: 'app-polymer-princess',
  template: `
          <img [src]="imgURL">
  `,
  styles: [`
        img {
          border-radius: 100%;
          width: 600px;
        }
  `]
})
export class PolymerPrincessComponent {
  imgURL = './assets/carmen1.jpg';

}

Registering handlers on native browser events:

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

@Component({
  selector: 'app-article',
  template: `
      <h1>{{ title }}</h1>
      <p>Shares: {{ shareCount }}</p>
      <button (click)="share($event)">Share</button>
  `,
  styles: []
})
export class ArticleComponent {
  title = 'I have met my hero, he is me.';
  shareCount = 0;

  share(e: Event): void {
    console.log(e);
    ++this.shareCount;
  }

}

Generating and capturing custom events using EventEmitter:

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


@Component({
  selector: 'app-text-editor',
  template: `
    <textarea (keyup)="emitWordCount($event)"></textarea>
  `
})
export class TextEditorComponent {
    @Output() countUpdate = new EventEmitter<number>();

    emitWordCount(e): void {
      this.countUpdate.emit(e.target.value.match(/\S+/g || []).length);
    }

}

@Component({
  selector: 'app-article',
  template: `
      <h1>{{ title }}</h1>
      <p>Word count: {{ wordCount }}</p>
      <app-text-editor (countUpdate)="updateWordCount($event)"></app-text-editor>
  `,
  styles: []
})
export class ArticleComponent {
  title = 'I have met my hero, he is me.';
  wordCount = 0;

  updateWordCount(e: number): void {
    this.wordCount = e;
  }

}

Directive behavior DOM elements:

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

@Directive({
  selector: '[appMouseOverToReveal]'
})
export class ClickToRevealDirective {
  @HostListener('mouseover', ['$event.target'])
  reveal(target) {
    target.style['white-space'] = 'normal';
  }
}

@Component({
  selector: 'app-article',
  template: `
      <h1 appMouseOverToReveal>{{ title }}</h1>
  `,
  styles: [`
      h1 {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
        max-width: 300px;
      }
  `]
})
export class ArticleComponent {
  title = 'The will to win is nothing without the will to prepare.';

}

Projecting nested content using ng-content:

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

@Component({
  selector: 'app-ad-section',
  template: `
      <a href="#">{{ adText }}</a>
      <ng-content select="p"></ng-content>
  `
})
export class AdSectionComponent {
    adText = 'Angular 5 Love Affair';
}

@Component({
  selector: 'app-article',
  template: `
      <h1>{{ title }}</h1>
      <app-ad-section>
        <p>It is at the borders of pain that the men are separated from the boys.</p>
        <p>Somebody may beat me but they will have to bleed to do it.</p>
      </app-ad-section>
  `,
  styles: [``]
})
export class ArticleComponent {
  title = 'The will to win is nothing without the will to prepare.';

}

ngFor && ngIf:

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


@Component({
  selector: 'app-article-list',
  template: `
        <div *ngFor="let article of articles; let i = index;">
              <h1 *ngIf="article.active">
                    {{ i + 1 }}: {{ article.title }}
              </h1>
        </div>
  `
})
export class ArticleListComponent {
    articles: Array<Object> = [
      { title: 'Angular 5 to save the world', active: true },
      { title: 'Angular CLI 1.6', active: false },
      { title: 'Angular Material 5 CSS Design Framework', active: true }
    ];

}

Desugared:

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

@Component({
  selector: 'app-article-list',
  template: `
        <ng-template ngFor let-article [ngForOf]="articles" let-i="index">
              <div>
                  <ng-template [ngIf]="article.active">
                      <h1>
                        {{ i + 1 }}: {{ article.title }}
                      </h1>
                  </ng-template>
              </div>
        </ng-template>
  `
})
export class ArticleListComponent {
    articles: Array<Object> = [
      { title: 'Angular 5 to save the world', active: true },
      { title: 'Angular CLI 1.6', active: true },
      { title: 'Angular Material 5 CSS Design Framework', active: false }
    ];

}

Template Reference Variable:

@Component({
  selector: 'app-article',
  template: `
      <input #title (keyup)="0">
      <h1>{{ title.value }}</h1>
  `,
  styles: [``]
})
export class ArticleComponent {

}

@Component({
  selector: 'app-article',
  template: `
      <input #title (keyup)="setTitle(title.value)">
      <h1>{{ myTitle }}</h1>
  `,
  styles: [``]
})
export class ArticleComponent {
  myTitle = '';

  setTitle(val: string): void {
    this.myTitle = val;
  }

}

Lifecycle Hooks:

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

@Component({
  selector: 'app-article-list',
  template: `
      <input type="text" (keyup.enter)="add($event)">
          <app-article *ngFor="let title of titles; let i = index;"
                    [articleTitle]="title">
              <button (click)="remove(i)">X</button>
          <app-article>
  `
})
export class ArticleListComponent {
  titles: Array<string> = [];

  add(e): void {
    this.titles.push(e.target.value);
    e.target.value = '';
  }

  remove(index: number) {
    this.titles.splice(index, 1);
  }

}


@Component({
  selector: 'app-article',
  template: `
      <div>
            {{ articleTitle }}<ng-content></ng-content>
      </div>
  `,
  styles: [``]
})
export class ArticleComponent implements OnInit, OnDestroy {
  @Input() articleTitle: string;

  ngOnInit() {
    console.log('created', this.articleTitle);
  }

  ngOnDestroy() {
    console.log('destroyed', this.articleTitle);
  }

}

Referencing Parent Component in Child Component:

@Component({
  selector: 'app-article',
  template: `
      <app-feedback [val]="likes"></app-feedback>
  `,
  styles: [``]
})
export class ArticleComponent {
  likes = 0;

  incrementLikes(): void {
    this.likes++;
  }

}

@Component({
  selector: 'app-feedback',
  template: `
      <h1>Number of likes: {{ val }}</h1>
      <button (click)="likeArticle()">Like this article</button>
  `
})
export class FeedbackComponent {
  @Input() val: number;
  private articleComponent: ArticleComponent;

  constructor(articleComponent: ArticleComponent) {
    this.articleComponent = articleComponent;
  }

  likeArticle(): void {
    this.articleComponent.incrementLikes();
  }

}

ViewChild && forwardRef:

import { Component, Input, Inject,
         forwardRef, ViewChild, AfterViewInit } from '@angular/core';

@Component({
  selector: 'app-feedback',
  template: `
      <h1>Number of likes: {{ val }}</h1>
      <button (click)="likeArticle()"
               [disabled]="!likeEnabled">
               Like this article!
      </button>
  `
})
export class FeedbackComponent {
  @Input() val: number;
  private likeEnabled = false;
  private articleComponent: ArticleComponent;

  constructor(@Inject(forwardRef(() => ArticleComponent))
                      articleComponent: ArticleComponent) {
    this.articleComponent = articleComponent;
  }

  likeArticle(): void {
    this.articleComponent.incrementLikes();
  }

  setLikeEnabled(newEnabledStatus: boolean): void {
    this.likeEnabled = newEnabledStatus;
  }

}

@Component({
  selector: 'app-article',
  template: `
      <input type="checkbox" (click)="changeLikesEnabled($event)">
      <app-feedback [val]="likes"></app-feedback>
  `,
  styles: [``]
})
export class ArticleComponent implements AfterViewInit {
  @ViewChild(FeedbackComponent) feedbackComponent: FeedbackComponent;
  likes = 0;

  constructor() {
    console.log(this.feedbackComponent);
  }

  ngAfterViewInit() {
    console.log(this.feedbackComponent);
  }

  incrementLikes(): void {
    this.likes++;
  }

  changeLikesEnabled(e): void {
    this.feedbackComponent.setLikeEnabled(e.target.checked);
  }

}

ContentChild && forwardRef:

import { Component, ContentChild,
         Inject, forwardRef, AfterContentInit } from '@angular/core';

@Component({
  selector: 'app-feedback',
  template: `
      <h1>Number of likes: {{ val }}</h1>
      <button (click)="likeArticle()"
               [disabled]="!likeEnabled">
               Like this article!
      </button>
  `
})
export class FeedbackComponent {
  private val: number;
  private likeEnabled = false;
  private articleComponent: ArticleComponent;

  constructor(@Inject(forwardRef(() => ArticleComponent))
                      articleComponent: ArticleComponent) {
    this.articleComponent = articleComponent;
  }

  updateLikes() {
    this.val = this.articleComponent.likes;
  }

  likeArticle(): void {
    this.articleComponent.incrementLikes();
    this.updateLikes();
  }

  setLikeEnabled(newEnabledStatus: boolean): void {
    this.likeEnabled = newEnabledStatus;
  }

}

@Component({
  selector: 'app-article',
  template: `
      <input type="checkbox" (click)="changeLikesEnabled($event)">
      <ng-content></ng-content>
  `,
  styles: [``]
})
export class ArticleComponent implements AfterContentInit {
  @ContentChild(FeedbackComponent) feedbackComponent: FeedbackComponent;
  likes = 0;

  constructor() {
    console.log(this.feedbackComponent);
  }

  ngAfterContentInit() {
    console.log(this.feedbackComponent);
  }

  incrementLikes(): void {
    this.likes++;
  }

  changeLikesEnabled(e): void {
    this.feedbackComponent.setLikeEnabled(e.target.checked);
  }

}

ngModel:

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


@Component({
  selector: 'app-article-editor',
  template: `
      <input [(ngModel)]="title">
      <input [(ngModel)]="title">
      <h2>{{ title }}</h2>
  `
})
export class ArticleEditorComponent {
    title = '';

}

@Component({
  selector: 'app-article-editor',
  template: `
      <input [ngModel]="title" (ngModelChange)="title=$event">
      <input [ngModel]="title" (ngModelChange)="title=$event">
      <h2>{{ title }}</h2>
  `
})
export class ArticleEditorComponent {
    title = '';

}

@Component({
  selector: 'app-article-editor',
  template: `
      <input [value]="title" (input)="title=$event.target.value">
      <input [value]="title" (input)="title=$event.target.value">
      <h2>{{ title }}</h2>
  `
})
export class ArticleEditorComponent {
    title = '';

}

FormControl basic field validation:

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


@Component({
  selector: 'app-article-editor',
  template: `
      <p>Article title (required)</p>
      <input [formControl]="titleControl" required>
      <button (click)="submitTitle()">Save</button>
      <h1>{{ title }}</h1>
  `
})
export class ArticleEditorComponent {
    title = '';
    titleControl: FormControl = new FormControl();

    submitTitle(): void {
      if (this.titleControl.valid) {
        this.title = this.titleControl.value;
      } else {
        console.log('Title required');
      }
    }

}

import { Component } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';


@Component({
  selector: 'app-article-editor',
  template: `
      <p>Article title (required)</p>
      <input [formControl]="titleControl">
      <button (click)="submitTitle()">Save</button>
      <h1>{{ title }}</h1>
  `
})
export class ArticleEditorComponent {
    title = '';
    titleControl: FormControl = new FormControl(null, Validators.required);

    submitTitle(): void {
      if (this.titleControl.valid) {
        this.title = this.titleControl.value;
      } else {
        console.log('Title required');
      }
    }

}

FormGroup:

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';


@Component({
  selector: 'app-article-editor',
  template: `
          <p>Title: <input [formControl]="titleControl"></p>
          <p>Text: <input [formControl]="textControl"></p>
          <p><button (click)="saveArticle()">Save</button></p>
          <hr>
          <p>Preview:</p>
          <div style="border: 1px solid #999; margin: 50px;">
                  <h1>{{ article.title }}</h1>
                  <p>{{ article.text }}</p>
          </div>
  `
})
export class ArticleEditorComponent {
    article: {title: string, text: string } = {title: '', text: ''};

    titleControl = new FormControl(null, Validators.required);
    textControl = new FormControl(null, Validators.required);

    articleFormGroup = new FormGroup({
      title: this.titleControl,
      text: this.textControl
    });

    saveArticle(): void {
        if (this.articleFormGroup.valid) {
          this.article = this.articleFormGroup.value;
        } else {
          console.log('Missing field(s)!');
        }
    }

}

FormArray:

import { Component } from '@angular/core';
import { FormControl, FormArray, Validators } from '@angular/forms';


@Component({
  selector: 'app-article-editor',
  template: `
              <p>Tags:</p>
              <ul>
                  <li *ngFor="let t of tagControls; let i = index;">
                        <input [formControl]="t">
                        <button (click)="removeTag(i)">X</button>
                  </li>
              </ul>
              <p><button (click)="addTag()">+</button></p>
              <p><button (click)="saveArticle()">Save</button></p>
  `
})
export class ArticleEditorComponent {
       tagControls: Array<FormControl> = [];
       tagFormArray: FormArray = new FormArray(this.tagControls);

       addTag(): void {
            this.tagFormArray.push(new FormControl(null, Validators.required));
       }

       removeTag(idx: number): void {
            this.tagFormArray.removeAt(idx);
       }

       saveArticle(): void {
          if (this.tagFormArray.valid) {
            console.log('Valid');
          } else {
            console.log('Missing field(s)');
          }
       }

}

NgForm:

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

@Component({
  selector: 'app-article-editor',
  template: `
              <form #f="ngForm" (ngSubmit)="saveArticle(f)">
                <div ngModelGroup="article">
                  <p><input ngModel name="title" placeholder="Article title"></p>
                  <p><textarea ngModel name="text" placeholder="Article text"></textarea></p>
                </div>
                  <p><button type="submit">Save</button></p>
              </form>
  `
})
export class ArticleEditorComponent {
              saveArticle(f: NgForm): void {
                  console.log(f);
              }

}

FormBuilder:

import { Component, Inject } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-article-editor',
  template: `
            <form [formGroup]="articleGroup" (ngSubmit)="saveArticle()">
            <div formGroupName="article">
                <p><input [formControl]="titleControl" placeholder="Article title"></p>
                <p><textarea [formControl]="textControl" placeholder="Article text"></textarea></p>
            </div>
            <p><button type="submit">Save</button></p>
            </form>
  `
})
export class ArticleEditorComponent {
            titleControl = new FormControl(null, Validators.required);
            textControl = new FormControl(null, Validators.required);
            articleGroup: FormGroup;

              constructor(@Inject(FormBuilder) formBuilder: FormBuilder) {
                this.articleGroup = formBuilder.group({
                  article: formBuilder.group({
                  title: this.titleControl,
                  text: this.textControl
                  })
                });
               }

              saveArticle(): void {
                console.log(this.articleGroup);
               }

}

CustomValidator:

import { Component, Directive } from '@angular/core';
import { FormControl, Validator, NG_VALIDATORS } from '@angular/forms';

@Directive({
  selector: '[appMaxWordCount]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: MaxWordCountValidatorDirective,
    multi: true
  }]
})
export class MaxWordCountValidatorDirective implements Validator {
  validate(c: FormControl): {[key: string]: any} {
    const wordCt: number = ((c.value || '').match(/\S+/g) || []).length;
    return wordCt <= 10 ? null : {maxwords: {limit: 10, actual: wordCt}};
  }
}

@Component({
  selector: 'app-article-editor',
  template: `
           <h2>The Will to Win is nothing witout the Will to Prepare</h2>
           <textarea [formControl]="bodyControl" required
                    appMaxWordCount placeholder="Article text"></textarea>
           <p><button (click)="saveArticle()">Save</button></p>
  `
})
export class ArticleEditorComponent {
              articleBody = '';
              bodyControl: FormControl = new FormControl();

              saveArticle(): void {
                  if (this.bodyControl.valid) {
                    alert('Valid!');
                  } else {
                    alert('Invalid!');
                  }
              }

}

Converting Observable to a Promise:

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


@Component({
  selector: 'app-article',
  template: `
           <h2>The Will to Win is nothing witout the Will to Prepare</h2>
            <div> {{ results | json }}</div>
  `
})
export class ArticleEditorComponent {
        results;
        constructor(private http: HttpClient) {
          http.get(`https://jsonplaceholder.typicode.com/posts`)
              .toPromise().then(data => {
                this.results = data;
              });
         }

}

Basic Observable:

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


@Component({
  selector: 'app-article',
  template: `
           <h2>The Will to Win is nothing witout the Will to Prepare</h2>
            <h1>{{ user }}</h1>
            <p>{{ title }}</p>
  `
})
export class ArticleEditorComponent {
        user = '';
        title = '';
        constructor(private http: HttpClient) {
          http.get(`https://jsonplaceholder.typicode.com/posts/1`)
              .subscribe(data => {
                this.user = data['userId'];
                this.title = data['title'];
              },
              error => console.log(error));
         }

Publish Subscribe Model:

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

@Component({
    selector: 'app-click-observer',
    template: `
          <button (click)="clickEmitter.next($event)">Emit event!</button>
          <p *ngFor="let click of clicks; let i = index;">
              {{ i }}: {{ click }}
          </p>
    `
})
export class ClickObserverComponent {
      clickEmitter: Subject<Event> = new Subject();
      clicks: Array<Event> = [];

      constructor() {
        this.clickEmitter.subscribe(clickEvent => this.clicks.push(clickEvent));
      }

}


import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Component({
    selector: 'app-click-observer',
    template: `
          <button (click)="publish($event)">Emit event!</button>
          <p *ngFor="let click of clicks; let i = index;">
              {{ i }}: {{ click }}
          </p>
    `
})
export class ClickObserverComponent {
      clickEmitter: Observable<Event>;
      private clickSubject_: Subject<Event> = new Subject();
      clicks: Array<Event> = [];

      constructor() {
        this.clickEmitter = this.clickSubject_.asObservable();
        this.clickEmitter.subscribe(clickEvent => this.clicks.push(clickEvent));
      }

      publish(e: Event): void {
        this.clickSubject_.next(e);
      }

}


import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';

@Component({
    selector: 'app-click-observer',
    template: `
          <button #btn>Emit event!</button>
          <p *ngFor="let click of clicks; let i = index;">
              {{ i }}: {{ click }}
          </p>
    `
})
export class ClickObserverComponent implements AfterViewInit {
      @ViewChild('btn') btn;
      clickEmitter: Observable<Event>;
      clicks: Array<Event> = [];

    ngAfterViewInit() {
          this.clickEmitter = Observable.fromEvent(this.btn.nativeElement, 'click');
          this.clickEmitter.subscribe(clickEvent => this.clicks.push(clickEvent));
    }

}

BehaviorSubject:

import { Component, Injectable, OnDestroy } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

@Injectable()
export class AuthService {
    private authManager_: BehaviorSubject<AuthState> = new BehaviorSubject(AuthState.LoggedOut);
    private authState_: AuthState;
    authChange: Observable<AuthState>;

    constructor() {
      this.authChange = this.authManager_.asObservable();
    }

    login(): void {
      this.setAuthState_(AuthState.LoggedIn);
    }

    logout(): void {
      this.setAuthState_(AuthState.LoggedOut);
    }

    emitAuthState(): void {
      this.authManager_.next(this.authState_);
    }

    private setAuthState_(newAuthState: AuthState): void {
      this.authState_ = newAuthState;
      this.emitAuthState();
    }

}

export const enum AuthState {
  LoggedIn,
  LoggedOut
}


@Component({
  selector: 'app-login',
  template: `
        <button *ngIf="!loggedIn" (click)="login()">
            Login
        </button>
        <button *ngIf="loggedIn" (click)="logout()">
            Logout
        </button>
  `
})
export class LoginComponent implements OnDestroy {
    loggedIn = false;
    private authChangeSubscription_: Subscription;

    constructor(private authService_: AuthService) {
      this.authChangeSubscription_ = authService_.authChange.subscribe(newAuthState => {
                this.loggedIn = (newAuthState === AuthState.LoggedIn);
        });
    }

    login(): void {
      this.authService_.login();
    }

    logout(): void {
      this.authService_.logout();
    }

    ngOnDestroy() {
      this.authChangeSubscription_.unsubscribe();
    }

}

Generalized Publish-Subscribe Service:

import { Component, Input, Injectable, AfterViewInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';

@Injectable()
export class PubSubService {
  private publishSubscribeSubject_: Subject<any> = new Subject();
  emitter_: Observable<any>;

  constructor() {
    this.emitter_ = this.publishSubscribeSubject_.asObservable();
  }

  publish(channel: string, event: any): void {
    this.publishSubscribeSubject_.next({
        channel: channel,
        event: event
    });
   }

  subscribe(channel: string, handler: ((value: any) => void)): Subscription {
    return this.emitter_
    .filter(emission => emission.channel === channel)
    .map(emission => emission.event)
    .subscribe(handler);
   }

}

export const enum AuthState {
  LoggedIn,
  LoggedOut
}


@Component({
  selector: 'app-node',
  template: `
          <p>Heard {{ count }} of {{ subscribeChannel }}</p>
          <button (click)="send()">Send {{ publishChannel }}</button>
  `
})
export class NodeComponent implements AfterViewInit, OnDestroy {
        @Input() publishChannel: string;
        @Input() subscribeChannel: string;
        count = 0;
        private pubSubServiceSubscription_: Subscription;

        constructor(private pubSubService_: PubSubService) { }

        send() {
            this.pubSubService_.publish(this.publishChannel, {});
        }

        ngAfterViewInit() {
         this.pubSubServiceSubscription_ =
         this.pubSubService_.subscribe(this.subscribeChannel, event => ++this.count);
        }

        ngOnDestroy() {
          this.pubSubServiceSubscription_.unsubscribe();
        }

}

QueryLists:

import { Component, Input, ViewChildren, AfterViewInit,
         QueryList, ChangeDetectorRef } from '@angular/core';


@Component({
  selector: 'app-inner',
  template: `
           <p>{{ val }}</p>

  `
})
export class InnerComponent {
      @Input() val: number;

}

@Component({
  selector: 'app-outer',
  template: `
          <button (click)="add()">More</button>
          <button (click)="remove()">Less</button>
          <button (click)="shuffle()">Shuffle</button>
          <app-inner
                *ngFor="let i of list" val="{{ i }}">
          </app-inner>
        <p>Value of last: {{ lastVal }}</p>
  `
})
export class OuterComponent implements AfterViewInit {
        @ViewChildren(InnerComponent) innerComponents: QueryList<InnerComponent>;
        list: Array<number> = [];
        lastVal = 0;

        constructor(private changeDetectorRef_: ChangeDetectorRef) { }

        add(): void {
          this.list.push(this.list.length);
        }

        remove(): void {
          this.list.pop();
        }

        shuffle(): void {
          this.list = this.list.sort(() => (4 * Math.random() > 2) ? 1 : -1);
        }

        ngAfterViewInit() {
          this.innerComponents.changes.subscribe(innerComponents => {
          this.lastVal = (innerComponents.last || {}).val;
          this.changeDetectorRef_.detectChanges();
        });
      }

}

Autocomplete Observable:

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

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/switchMap';


@Injectable()
export class APIService {
  constructor(private http: HttpClient) { }
  search(query: string): Observable<string> {
    return this.http.get('./assets/response.json')
                .map(r => r['prefix'] + query)
                .concatMap(x => Observable.of(x).delay(Math.random() * 1000));
  }
}

@Component({
  selector: 'app-search',
  template: `
              <input [formControl]="queryField">
              <p *ngFor="let result of results">{{ result }}</p>
  `
})
export class SearchComponent {
        results: Array<string> = [];
        queryField: FormControl = new FormControl();
        constructor(private apiService_: APIService) {
          this.queryField.valueChanges
                          .debounceTime(200)
                          .distinctUntilChanged()
                          .switchMap(query => this.apiService_.search(query))
                          .subscribe(result => this.results.push(result));
        }
        search(query: string): void {
          this.apiService_.search(query).subscribe(result => this.results.push(result));
        }
}

results matching ""

    No results matching ""