[Angular] Component architecture and Reactive Forms

It it recommeded that when deals with form component, we can create a container component to hold state, and then create a stateless component to enpower the form.

For example:

In the example has two components, one is container component 'meal.component.ts', another is statless component 'meal-form.component.ts'.

 

For the container component, it talks to service:

import {Component} from '@angular/core';
import {Meal} from '../../../shared/services/meals/meals.service';
@Component({
  selector: 'meal',
  styleUrls: ['meal.component.scss'],
  template: `
    <div class="meal">
      <div class="meal__title">
        <h1>
          <img src="/img/food.svg" alt="Food">
          <span>Create meal</span>
        </h1>
      </div>
      <div>
        <meal-form
          (create)="addMeal($event)"
        ></meal-form>
      </div>
    </div>
  `
})
export class MealComponent {
  constructor() {

  }

  addMeal(meal: Meal) {
    console.log("meal", JSON.stringify(meal, null, 2))
  }
}

So 'addMeal' function will dispatch action to talk to the service.

 

For statless component:

import {ChangeDetectionStrategy, Component, EventEmitter, Output} from '@angular/core';
import {FormBuilder, FormArray, FormGroup, FormControl, Validators} from '@angular/forms';
import {Meal} from '../../../shared/services/meals/meals.service';
@Component({
  selector: 'meal-form',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['meal-form.component.scss'],
  template: `
    <div class="meal-form"> 
      <form [formGroup]="form">
        <div class="meal-form__name">
          <label>
            <h3>Meal name</h3>
            <input type="text" 
                   formControlName="name"
                   placeholder="e.g. English Breakfast">
            <div class="error" *ngIf="required">
              Workout name is required
            </div>
          </label>
        </div>
        
        <div class="meal-form__food">
          <div class="meal-form__subtitle">
            <h3>Food</h3>
            <button 
              type="button" 
              (click)="addIngredient()"
              class="meal-form__add">
              <img src="/img/add-white.svg" alt="Add food">
              Add food
            </button>
          </div>
          <div formArrayName="ingredients">
            <label *ngFor="let c of ingredients.controls; index as i;">
              <input type="text" [formControlName]="i" placeholder="e.g Eggs">
              <span
                class="meal-form__remove"
                (click)="removeIngredient(i)"
              ></span>
            </label>
          </div>
        </div>
        
        <div class="meal-form__submit">
          <div>
            <button type="button" class="button" (click)="createMeal()">
              Create Meal
            </button>
            <a 
              [routerLink]="['../']"
              class="button button--cancel">
              Cancel
            </a>
          </div>
        </div>
      </form>
    </div>
  `
})
export class MealFormComponent {

  @Output()
  create = new EventEmitter<Meal>();

  form = this.fb.group({
    name: ['', Validators.required],
    ingredients: this.fb.array([''])
  });

  get ingredients () {
    // Type check for ingredients, mark as FormArray
    // Therefore when we use 'ingredients',
    // We can get auto complete
    return this.form.get('ingredients') as FormArray;
  }

  get required() {
    return (
      this.form.get('name').hasError('required') &&
        this.form.get('name').touched
    );
  }

  constructor(private fb: FormBuilder) {

  }

  createMeal() {
    if (this.form.valid) {
      this.create.emit(this.form.value);
    }
  }

  addIngredient() {
    // Add a new FormControl to FormArray
    this.ingredients.push(new FormControl(''));
  }

  removeIngredient(i: number) {
    this.ingredients.removeAt(i);
  }
}

 

It uses ReactiveForm to create form.

 

Things to be notice:

1. Add type check for form array:

  get ingredients () {
    // Type check for ingredients, mark as FormArray
    // Therefore when we use 'ingredients',
    // We can get auto complete
    return this.form.get('ingredients') as FormArray;
  }

Then whenever you use 'this.ingredients', it will show auto complete.

 

2. FormArray method:

  addIngredient() {
    // Add a new FormControl to FormArray
    this.ingredients.push(new FormControl(''));
  }

  removeIngredient(i: number) {
    this.ingredients.removeAt(i);
  }

 

posted @ 2017-08-12 18:59  Zhentiw  阅读(390)  评论(0编辑  收藏  举报