8. Service as Dependent Injection
8.1. Dependent Injection
Injectable Service could be injected through constructor:
we will use FormBuilder service to replace FormGroup in person-form.component.ts file. We inject that service via constructor:
  constructor(private formBuilder: FormBuilder) { }
the private modifier notify typescript to create member variable formBuilder. We create service in the constructor because we will use it later in ngOnit() method, the constructor ran before ngOnit(), that is why we put the creating FormBuilder service here.
the Full update person-form.component.ts file here:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder} from '@angular/forms';
@Component({
  selector: 'app-person-form',
  templateUrl: './person-form.component.html',
  styleUrls: ['./person-form.component.css']
})
export class PersonFormComponent implements OnInit {
  personForm : FormGroup;
  constructor(private formBuilder: FormBuilder) { }
  ngOnInit(): void {
    this.personForm = this.formBuilder.group({
      id: this.formBuilder.control(''),
      name: this.formBuilder.control('', Validators.compose([
        Validators.required,
        Validators.pattern('[\\w\\-\\s\\/]+')
      ])),
      gender: this.formBuilder.control('', this.genderValidator)
    })
  }
  genderValidator(control: FormControl){
    if (control.value.trim().length === 0) {
      return null;
    }
    if(control.value === 'male' || control.value === 'female'){
      return null;
    }else{
      return { 'gender': true };
    }
  }
  onSubmit(person){
    console.log(person);
  }
}
so in this case, the service provider FormBuilder injected in PersonFormComponent via its constructor.
8.2. Service
- Create new Service by angular CLI command:
ng g service person-list
- Add the service in app.module.tsif you don’t use@Injectable, I just list this step here, we are using@Injectabledecorator which is preferred.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ShowPersonComponent } from './show-person/show-person.component';
import { ShowPersonListComponent } from './show-person-list/show-person-list.component';
import { GenderDirective } from './gender.directive';
import { GenderpipePipe } from './genderpipe.pipe';
import { PersonFormComponent } from './person-form/person-form.component';
import { PersonListService } from './person-list.service';
@NgModule({
  declarations: [
    AppComponent,
    ShowPersonComponent,
    ShowPersonListComponent,
    GenderDirective,
    GenderpipePipe,
    PersonFormComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule
  ],
  providers: [
    PersonListService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
notice that PersonListService was added in Providers, not other array, cause this is service provider.
- move the static personsobject array fromshow-persion-list.component.tstoperson-list.service.tsfile, and then create theget(),add()anddelete()method (CRUD processes). Following is theperson-list.service.tslooks like:
import { Injectable } from '@angular/core';
@Injectable({
  providedIn: 'root'
})
export class PersonListService {
  persons = [{
    id: 1,
    name: 'r0ngsh3n',
    gender: 'male'
  },
  {
    id: 2,
    name: 'johndoe',
    gender: 'male'
  },
  {
    id: 3,
    name: 'jeandoe',
    gender: 'female'
  }
  ]
  get(){
    return this.persons;
  }
  add(value){
    this.persons.push(value);
  }
  delete(value){
    if(this.persons.indexOf(value) >= 0 ){
      this.persons.splice(this.persons.indexOf(value));
    }
  }
}
You must have to have @Injectable decorator in the service if you skip step 2. This is important.
- Import person-list.service.tsintoshow-person-list.component.ts, and inngOnInit()method, you can embeded theget'moethod
import { Component, OnInit } from '@angular/core';
import { PersonListService } from '../person-list.service';
@Component({
  selector: 'app-show-person-list',
  templateUrl: './show-person-list.component.html',
  styleUrls: ['./show-person-list.component.css']
})
export class ShowPersonListComponent implements OnInit {
  persons; 
  constructor(private service: PersonListService){}
  ngOnInit(){
    this.persons = this.service.get();
  }
}
we remove the static person' array, and in the ngOnInit method, we assign the person array by service.
8.3. alternative Service - Inject Token
Value provider usually provides list of global consts for look up, those values could be found through the project.
It is like ‘Global Enum’ but here it call Inject Token
- Create Inject Token file called provider.ts
import { InjectionToken } from '@angular/core';
export const genderListToken = new InjectionToken('genderListToken');
export const genderList = {
    genders : ['male', 'female']
}
here, we have InjectionToken and our value list genderList both, we need to import them in the app.moudule.ts file.
- Add in app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ShowPersonComponent } from './show-person/show-person.component';
import { ShowPersonListComponent } from './show-person-list/show-person-list.component';
import { GenderDirective } from './gender.directive';
import { GenderpipePipe } from './genderpipe.pipe';
import { PersonFormComponent } from './person-form/person-form.component';
import { genderListToken, genderList } from './provider';
@NgModule({
  declarations: [
    AppComponent,
    ShowPersonComponent,
    ShowPersonListComponent,
    GenderDirective,
    GenderpipePipe,
    PersonFormComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule
  ],
  providers: [ 
    { provide: genderListToken, useValue: genderList }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
after import, we add following line in the providers section:
  { provide: genderListToken, useValue: genderList }
- Use the Inject Token in person-form.component.tsfile asselectoption.
import { Component, OnInit, Inject } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder} from '@angular/forms';
import { PersonListService } from '../person-list.service';
import { genderListToken } from '../provider';
@Component({
  selector: 'app-person-form',
  templateUrl: './person-form.component.html',
  styleUrls: ['./person-form.component.css']
})
export class PersonFormComponent implements OnInit {
  personForm : FormGroup;
  constructor(private formBuilder: FormBuilder, 
    private personlistService: PersonListService
    @Inject(genderListToken) public genderList ) { }
  ngOnInit(): void {
    this.personForm = this.formBuilder.group({
      id: this.formBuilder.control(''),
      name: this.formBuilder.control('', Validators.compose([
        Validators.required,
        Validators.pattern('[\\w\\-\\s\\/]+')
      ])),
      gender: this.formBuilder.control('', this.genderValidator)
    })
  }
  genderValidator(control: FormControl){
    if (control.value.trim().length === 0) {
      return null;
    }
    if(control.value === 'male' || control.value === 'female'){
      return null;
    }else{
      return { 'gender': true };
    }
  }
  onSubmit(person){
    this.personlistService.add(person);
    console.log(person);
  }
}
we import Inject from angular/core module, and import genderListToken from provider.ts, notice we don’t need to import genderList from provider.ts, then in the constructor we create Inject public object genderList.
- Modify the html file to use dropdownmenu as gender selector
<header>
    <h2>Add New Person</h2>
</header>
<form [formGroup]="personForm" (ngSubmit)="onSubmit(personForm.value)">
    <ul>
        <li>
            <label for="id">Id</label>
            <input type="text" name="id" id="id" formControlName="id">
        </li>
        <li>
            <label for="name">Name</label>
            <input type="text" name="name" id="name" formControlName="name">
            <div *ngIf="personForm.get('name').hasError('pattern')">your name is not valid</div>
        </li>
        <li>
            <label for="gender">Gender</label>
            <select name="gender" id="gender" formControlName="gender">
                <option *ngFor="let gender of genderList.genders" [value]="gender"></option>
            </select>
            <!-- <input type="text" name="gender" id="gender" formControlName="gender">
            <div *ngIf="personForm.get('gender').hasError('gender')">You only can use male or female</div> -->
        </li>
    </ul>
    <button type="submit" [disabled] = "!personForm.valid">Save</button>
</form>
we changed the gender section from input to select, and in the option, we use for-loop to loop through the genderList.genders array and show the values.