director.ts

import { Component, OnInit, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
import { DirectorService, GENRES_DI } from './director.service';
import { Director, Movie, Genre } from './director';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-director-form',
  templateUrl: './director-form.component.html',
  styles: [
    ` .form-group, .form-array { margin: 1em;}
     label { display: inline-block; width: 200px}
     .action-bar { 
        display: flex; 
        flex-direction: flex-row; 
        width: 360px; 
        flex-wrap: wrap; 
        justify-content: space-evenly
      }
      input + span {display: block; margin: 0; font-size: smaller; color: red}
      .ng-invalid:not(form) {border-left: 5px solid red}`
    ], 
  providers: [{provide: GENRES_DI, useValue: Object.keys(Genre).filter(k => isNaN(Number(k)))}]
})
export class DirectorFormComponent implements OnInit {

  directorForm: FormGroup;
  length: number = 0;

  constructor(
    private fb: FormBuilder,
    private service: DirectorService, 
    @Inject(GENRES_DI) private genres: string[]
  ) {
    this.directorForm = this.createForm();
  }

  ngOnInit() {
    this.setDataLength();
    this.load();
  }

  setDataLength() {
    this.service.getDirectors().pipe(map(data => data.length)).subscribe(size => this.length = size);
  }

  /**
   * Randomly loads a director from data source.
   */
  load() {
    this.service.getDirector(this.getRandomId(this.length))
      .subscribe(
        (director) => this.directorForm.patchValue(this.getValueFrom(director)),
        (err) => console.log(err)
      );
  }

  /**
   * Gets a random id.
   * @param the max length of the data table.
   */
  getRandomId(length: number): number {
    let rd: number = Math.random() * 10 % length;
    return Math.ceil(rd);
  }

  /**
   * Creates the initial formGroup.
   * @returns a formGroup instance
   */
  createForm() {
    return this.fb.group({
      firstname: ['', Validators.required],
      lastname: ['', Validators.required],
      movies: this.fb.array([
      ])
    });
  }

   /**
   * Fills director's values.
   */
  getValueFrom(author: Director) {
    let value = {firstname: '',lastname: ''};
    this.resetMovies();// reset to avoid duplicates after loading
    for (let key in author) {
      if (this.directorForm.get(key)) {
        if (Array.isArray(author[key])) {
          author[key].forEach(m => this.setMovie(m))
        } else {
          value[key] = author[key];
        }
      }
    }
    return value;
  }

  // GETTERS
  get firstname() {
    return this.directorForm.get('firstname');
  }

  get lastname() {
    return this.directorForm.get('lastname');
  }

  /**
   * Gets the movies' formArray.
   * @returns movies formControl as FormArray
   */
  get movies() {
    return this.directorForm.get('movies') as FormArray;
  }

  /**
   * Adds a new group {[title]: string, [genre]: string} to the movies' formArray of the director.
   * @param some movie instance
   */
  setMovie(movie: Movie) {
    this.movies.push(this.fb.group({ title: movie.title, genre: movie.genre }));
  }

  addMovie() {
    this.movies.push(this.fb.group({ title: '', genre: '' }));
  }

  removeMovie(index: number) {
    this.movies.removeAt(index);
  }

  /**
   * Clears the formArray modelling this director's movies.
   */
  resetMovies() {
    while (this.movies.length > 0) {
      this.movies.removeAt(0);
    }
  }

  onSubmit(formValue: any) {
    console.log(formValue);
  }

}