Angular 8 component communication infrastructure usage scenario

Component Interaction

Parent-child component interaction (common)

Input, Output attributes

One of the most basic scenarios is very precautionary. Using method, the data in the parent component is bound to the variables declared by the child component to ensure that the child component can be used for internal rendering template. The main concern is the internal code format of the child component:


import { Component, OnInit, Input } from '@angular/core';
@Component({
  selector: 'app-children',
  templateUrl: './children.component.html',
  styleUrls: ['./children.component.scss']
})
export class ChildrenComponent implements OnInit {
  @Input() List: Array<any>;
  constructor() { }
  ngOnInit() {
  }
}

Declare this variable as an input attribute through the @Input decorator, which is used to bind properties like dom, but with some differences:

Current total record number: {{dataList.length}}
<app-children [List]="dataList"></app-children>

Bind [] symbols to data in the current component or page. In this scenario, the direction of data transmission is simpler and simpler. In actual development, more sub-components will operate on part of the data, and the parent component will receive changes at all times.
In Angular, the subcomponent propagates events by introducing the EventEmitter class from @angular/core, demo as follows:

//Intra-component code
@Output () removeHandler = new EventEmitter();
//Operational events in component templates
removeAll(){
    this.List = [];
    this.removeHandler.emit(Data or event name);
}

Template:

<h4>Subcomponent display table:</h4>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Phone</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let item of List">
      <td>{{item.name}}</td>
      <td>{{item.phone}}</td>
    </tr>
  </tbody>
</table>
<button (click)="removeAll()">Clean up all</button>

Parent component:

<app-children (removeHandler)="removeData($event)" [List]="dataList"></app-children>
removeData(e) {
    console.log(e);
    this.dataList = e; //How to update data to this component according to scenarios
  }

onChanges life cycle intercepts changes in input attributes

In addition, the official demo also provides two kinds of interception input value changes to update local data, one is to getter and setter the properties bound by sub-components; the other is to use the component declaration period hook ngOnChanges function for processing in development, which I modify below. Components:

export class ChildrenComponent implements OnInit, OnChanges {
  @Input() List: Array<any>;
  changeList = [];
  @Output() removeHandler = new EventEmitter();
  constructor() { }
  ngOnInit() {

  }
  ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
    let logs = [];
    if (!changes.List.firstChange) {
      logs = changes.List.currentValue;
      this.changeList = logs;
    }
  }
  removeAll() {
    this.changeList = [];
    this.removeHandler.emit(this.changeList);
  }
}
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Phone</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let item of changeList">
      <td>{{item.name}}</td>
      <td>{{item.phone}}</td>
    </tr>
  </tbody>
</table>
<button (click)="removeAll()">Clean up all</button>

Note that in this case, because I only bind one input attribute, if there are more than one, the specified data should be updated using a for loop.

Local variable interaction

Official definition:

The parent component cannot use data binding to read the properties of the child component or to call the methods of the child component. But in the parent component template, you can create a new local variable to represent the child component, and then use this variable to read the attributes of the child component and invoke the method of the child component.

Subcomponent modification;

export class ChildrenComponent implements OnInit {
  // @Input() List: Array<any>;
  changeList = [];
  // @Output() removeHandler = new EventEmitter();
  constructor() { }
  ngOnInit() {

  }
  addData(data) {
    this.changeList = [...this.changeList, data];
  }
  removeAll() {
    this.changeList = [];
  }
}

html template unchanged;
Parent component:

<h4>Add a new record</h4>
<div class="basic-data">
  <label>name: </label>
  <input type="text" name="name" [(ngModel)]="name">
  <label>phone: </label>
  <input type="text" name="phone" [(ngModel)]="phone">
  <button (click)="table.addData({name:name,phone:phone})">Add to</button>
  <button (click)="table.removeAll()">Eliminate</button>
</div>
//Current total record number: {table.changeList.length}
<app-children #table></app-children>
export class BasicComponent implements OnInit {

  constructor() { }
  name = '';
  phone = '';
  ngOnInit() {

  }
}

The logic inside the component is deleted because the properties and methods of the subcomponent can be accessed directly through the bound # table.

The parent component calls @ViewChild

Sometimes we need to directly access the attributes and methods within the subcomponent in the component. Local variables are not applicable. Consider using @ViewChild().

Parent component modification:

<h4>Add a new record</h4>
<div class="basic-data">
  <label>name: </label>
  <input type="text" name="name" [(ngModel)]="name">
  <label>phone: </label>
  <input type="text" name="phone" [(ngModel)]="phone">
  <button (click)="addData()">Add to</button>
  <button (click)="removeAll()">Eliminate</button>
</div>
//Current total record number: {{dataList.length}}
<app-children></app-children>
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChildrenComponent } from '../children/children.component';
@Component({
  selector: 'app-basic',
  templateUrl: './basic.component.html',
  styleUrls: ['./basic.component.scss']
})
export class BasicComponent implements OnInit {
  @ViewChild(ChildrenComponent, { static: false })
  private children: ChildrenComponent;
  constructor() { }
  name = '';
  phone = '';
  dataList = [];
  ngOnInit() {

  }
  addData() {
    this.dataList = [...this.dataList, {
      name: this.name,
      phone: this.phone
    }];
    this.children.addData({
      name: this.name,
      phone: this.phone
    });
  }
  removeAll() {
    this.dataList = [];
    this.children.removeAll();
  }
}

If there is an initialization operation in the child component, it can be initialized in the AfterViewInit declaration cycle of the parent component.

Arbitrary Components - Service s

It is a good choice to use service interaction when there is often no parent-child component Association for more interactions in the business.

Service file code:

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

@Injectable({
  providedIn: 'root'
})
export class WorkerListService {
  workList = [];
  workListSource = new Subject<any>();
  private workListSource$ = this.workListSource.asObservable();
  constructor() { }
  addInfo(info) {
    this.workList = [...this.workList, info];
    this.workListSource.next(this.workList);
  }
  updateWorkList(infos: Array<any>) {
    this.workList = infos;
    this.workListSource.next(this.workList);
  }

}

Explain the following code section, the first premise that data can be subscribed to must be an Observable type, for ordinary array of(SomeArray) is already an Observable type to subscribe to, for the return http request is also an Observable type, in the page, this is listed in the official document of Ng Observable class. Type. The next() method tells the observable object to update the content so that your subscribe can grab the updated information.

Original parent component:

import { Component, OnInit } from '@angular/core';
import { WorkerListService } from '../worker-list.service';
@Component({
  selector: 'app-basic',
  templateUrl: './basic.component.html',
  styleUrls: ['./basic.component.scss']
})
export class BasicComponent implements OnInit {
  constructor(
    private workListService: WorkerListService
  ) { }
  name = '';
  phone = '';
  dataList = [];
  ngOnInit() {
    this.workListService.workListSource.subscribe(res => {
      this.dataList = res;
    });
  }
  addData() {
    this.workListService.addInfo({
      name: this.name,
      phone: this.phone
    });
  }
  removeAll() {
    this.dataList = [];
    this.workListService.updateWorkList(this.dataList);
  }
}

Atomic components:

import { Component, OnInit } from '@angular/core';
import { WorkerListService } from '../worker-list.service';
@Component({
  selector: 'app-children',
  templateUrl: './children.component.html',
  styleUrls: ['./children.component.scss']
})
export class ChildrenComponent implements OnInit {
  changeList = [];
  constructor(
    private workListService: WorkerListService
  ) {
    this.workListService.workListSource.subscribe(res=>{
      this.changeList = res;
    });
  }
  ngOnInit() {

  }
}

This ensures that the component can get the latest data updates at any time without worrying about whether it is a parent-child component, which is also the most common way.

Keywords: Front-end angular Attribute

Added by websitesca on Tue, 06 Aug 2019 12:15:08 +0300