Angular组件间通讯

1.1 组件的输入属性

父子组件 父组件–>子组件
效果图
在这里插入图片描述
1
1.1生成子组件
–> ng g component order
order.component.ts

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

@Component({
  selector: 'app-order',
  templateUrl: './order.component.html',
  styleUrls: ['./order.component.css']
})
export class OrderComponent implements OnInit {
  /*输入属性*/
  @Input()/*装饰器*/
  stockCode: string;
  @Input()
  amount: number;
  constructor() {
    setInterval(() => {
      this.stockCode = 'Apple';
    }, 3000);
  }
  ngOnInit() {
  }
}

order.component.html

<div>
  我是子组件
</div>
<div>{{amount}}{{stockCode}}股票
</div>

1.2父组件
app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  stock = '';
}

app.component.html

<div>
  我是父组件
</div>
<div>
<input type="text" placeholder="请输入股票代码"
  [(ngModel)]="stock">
  <app-order [stockCode]="stock" [amount]="100"></app-order>
</div>

1.2 组件的输出属性

1
1.1生成子组件
–> ng g component price-quote
price-quote.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-price-quote',
  templateUrl: './price-quote.component.html',
  styleUrls: ['./price-quote.component.css']
})
export class PriceQuoteComponent implements OnInit {
  stockCode = 'IBM';
  price: number;
  constructor() {
    setInterval(() => {
      const priceQuote: PriceQuote = new PriceQuote(this.stockCode, 100 * Math.random());
      this.price = priceQuote.lastPrice;
      }, 1000);
  }
  ngOnInit() {
  }
}
export class PriceQuote {
  constructor(public stockCode: string,
  public  lastPrice: number) {
  }
}

price-quote.component.html

<div>
  我是报价组件
</div>
<div>
  股票代码是:{{stockCode}},股票价格是:{{price | number:'2.2-2'}}
</div>

app.component.html

<app-price-quote></app-price-quote>

效果图
1.2
price-quote.component.ts

import {Component, EventEmitter, OnInit, Output} from '@angular/core';
...
export class PriceQuoteComponent implements OnInit {
  stockCode = 'IBM';
  price: number;
  /*输出属性*/
  @Output('priceChange')
    /*<PriceQuote>:发射数据的事件类型*/
  lastPrice: EventEmitter<PriceQuote> = new EventEmitter();
  constructor() {
    setInterval(() => {
      const priceQuote: PriceQuote = new PriceQuote(this.stockCode, 100 * Math.random());
      this.price = priceQuote.lastPrice;
      this.lastPrice.emit(priceQuote);
      }, 1000);
  }
  ngOnInit() {
  }
}
export class PriceQuote {
  constructor(public stockCode: string,
  public  lastPrice: number) {
  }
}

app.component.ts

import { Component } from '@angular/core';
import {PriceQuote} from './price-quote/price-quote.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  stock = '';
  /*接收子组件*/
  priceQuote: PriceQuote = new PriceQuote('', 0);
  priceQuoteHandler(event: PriceQuote) {
    this.priceQuote = event;
  }
}

app.component.html

<app-price-quote (priceChange)="priceQuoteHandler($event)" ></app-price-quote>
<div>
  这是在报价组件外部
</div>
<div>
  股票代码是:{{priceQuote.stockCode}},
  股票价格是{{priceQuote.lastPrice}}
</div>

效果图

2 使用中间人模式传递数据

1
price-quote.component.html

<div>
  我是报价组件
</div>
<div>
  股票代码是:{{stockCode}},股票价格是:{{price | number:'2.2-2'}}
</div>
<div>
  <input type="button" value="立即购买" (click)="buyStock($event)">
</div>

price-quote.component.ts

@Output()
  buy: EventEmitter<PriceQuote> = new EventEmitter();
  buyStock(event) {
    this.buy.emit(new PriceQuote(this.stockCode, this.price));
  }

2
order.component.html

<div>
  我是下单组件
</div>
<div>100{{priceQuote.stockCode}}股票,买入价格是:{{priceQuote.lastPrice | number:'2.2-2'}}
</div>

order.component.ts

@Input()
  priceQuote: PriceQuote;

3
app.component.html

<!--监听&ndash;&gt;获得购买信息&ndash;&gt;传给下单组件-->
<app-price-quote (buy)="buyHandler($event)" ></app-price-quote>
<app-order [priceQuote]="priceQuote"></app-order>

app.component.ts

export class AppComponent {
  stock = '';
  /*接收子组件*/
  priceQuote: PriceQuote = new PriceQuote('', 0);
  buyHandler(event: PriceQuote) {
    this.priceQuote = event;
  }
}

效果图

3 组件生命周期以及Angular的变化发现机制

图片描述
钩子调用机制、使用场景
1生成组件
–> ng g component life
2
life.component.ts

import {
  AfterContentChecked,
  AfterContentInit,
  AfterViewChecked,
  AfterViewInit,
  Component,
  DoCheck,
  Input,
  OnChanges,
  OnDestroy,
  OnInit, SimpleChanges
} from '@angular/core';

  let logIndex = 1;
@Component({
  selector: 'app-life',
  templateUrl: './life.component.html',
  styleUrls: ['./life.component.css']
})
/*实现所有的钩子接口*/
export class LifeComponent implements OnInit, OnChanges, DoCheck, AfterContentInit, AfterContentChecked,
  AfterViewInit, AfterViewChecked, OnDestroy  {
  @Input()
  name: string;
  logIt (msg: string) {
    console.log(`#${ logIndex++ } ${ msg }`);
  }
  constructor() {
    this.logIt('name属性在constructor里的值是:' + name);
  }
  ngOnChanges(changes: SimpleChanges): void {
    const name = changes['name'].currentValue;
    this.logIt('name属性在ngOnChanges里的值是:' + name);
  }

  ngOnInit() {
    this.logIt('ngOnInit');
  }

  ngDoCheck(): void {
    this.logIt('ngDoCheck');
  }

  ngAfterContentInit(): void {
    this.logIt('ngAfterContentInit');
  }

  ngAfterContentChecked(): void {
    this.logIt('ngAfterContentChecked');
  }

  ngAfterViewInit(): void {
    this.logIt('ngAfterViewInit');
  }

  ngAfterViewChecked(): void {
    this.logIt('ngAfterViewChecked');
  }

  ngOnDestroy(): void {
    this.logIt('ngOnDestroy');
  }
}

app.component.ts

export class AppComponent {
  title = 'Tom';
}

app.component.html

<app-life [name]="title"></app-life>

console

ngOnChanges

父组件初始化、修改子组件参数时调用
可变:对象.属性—内容
var user = {name: “Tom”};
user.name = “Jerry”;
不可变:字符串—地址
var greeting = “Hello”;
greeting = “HelloWorld”;
1.生成组件
–> ng g component child
2.子组件
child.component.ts

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

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges{
  @Input()
  /**/
  greeting: string;
  @Input()
  user: {name: string};
  message = '初始化信息';
  constructor() { }

  ngOnInit() {
  }
  ngOnChanges(changes: SimpleChanges): void {
    console.log(JSON.stringify(changes, null, 2));
  }
}

child.component.html

<div class="child">
  <h2>我是子组件</h2>
  <div>问候语:{{greeting}}</div>
  <div>姓名:{{user.name}}</div>
  <div>消息:<input [(ngModel)]="message"></div>
</div>

3.父组件
app.component.ts

export class AppComponent {
  greeting = 'Hello';
  user = {name: 'Tome'};
}

app.component.html

<div class="parent">
  <h2>我是父组件</h2>
  <div>
    问候语:<input type="text" [(ngModel)]="greeting">
  </div>
  <div>
    姓名:<input type="text" [(ngModel)]="user.name">
  </div>
  <app-child [greeting]="greeting" [user]="user"></app-child>
</div>

4.效果图
console.log:父组件初始化、修改输入装饰器装饰的属性
效果图
修改父组件问候语输入框
效果图
修改父组件姓名输入框
效果图
修改子组件消息输入框
在这里插入图片描述

变更检测机制

package.json–>dependencies–>zone.js
zone.js:保证组件的属性的变化和页面的变化同步
浏览器中发生异步事件触发检测:点击、输入、、、、
变更检测策略:
变更检测策略
子组件变化,其父组件下所有,除OnPush
变更检测策略

DoCheck

child.component.ts

import {Component, DoCheck, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges, DoCheck {
  @Input()
  /**/
  greeting: string;
  @Input()
  user: {name: string};
  message = '初始化信息';
  oldUsername: string;
  changeDetected = false;
  noChangeCount = 0;
  constructor() { }

  ngOnInit() {
  }
  ngOnChanges(changes: SimpleChanges): void {
    console.log(JSON.stringify(changes, null, 2));
  }
  ngDoCheck(): void {
    if (this.user.name !== this.oldUsername) {
      this.changeDetected = true;
      console.log('DoCheck:user.name Before:' + this.oldUsername + ';After:' + this.user.name);
      this.oldUsername = this.user.name;
    }
    if (this.changeDetected) {
      this.noChangeCount = 0;
    } else {
      this.noChangeCount = this.noChangeCount + 1;
      console.log('DoCheck:user.name没变化时,ngDoCheck方法已经被调用' + this.noChangeCount);
    }
    this.changeDetected = false;
  }
}

效果图
效果图

view

1.–> ng g component childTwo
child-two.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-child-two',
  templateUrl: './child-two.component.html',
  styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent implements OnInit {
  constructor() { }
  ngOnInit() {
  }
  greeting (name: string) {
    console.log('hello:' + name);
  }
}

1.在父组件控制器用typescript代码,装饰器 @ViewChild('child1')
app.component.html

<app-child-two #child1></app-child-two>

app.component.ts

import {Component, OnInit, ViewChild} from '@angular/core';
import {ChildTwoComponent} from './child-two/child-two.component';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  /*装饰器,获得子组件的引用*/
  @ViewChild('child1')
  child1: ChildTwoComponent;
  constructor () {}
  ngOnInit (): void {
    this.child1.greeting('Tom');
  }
}

效果图
2.在父组件的模板中调用子组件的方法
app.component.html

<app-child-two #child2></app-child-two>
<button (click)="child2.greeting('Jerry')">调用child2的greeting方法</button>

效果图
3.观察
app.component.ts

import {AfterViewChecked, AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {ChildTwoComponent} from './child-two/child-two.component';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit, AfterViewChecked {
  /*装饰器,获得子组件的引用*/
  @ViewChild('child1')
  child1: ChildTwoComponent;
  constructor () {}
  ngOnInit (): void {
    setInterval(() => {
      this.child1.greeting('Tom');
    }, 5000);
  }
  ngAfterViewInit(): void {
    console.log('父组件的视图初始化完毕');
  }
  ngAfterViewChecked(): void {
    console.log('父组件的视图变更检测完毕');
  }
}

child-two.component.ts

import {AfterViewChecked, AfterViewInit, Component, OnInit} from '@angular/core';
@Component({
  selector: 'app-child-two',
  templateUrl: './child-two.component.html',
  styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent implements OnInit, AfterViewInit, AfterViewChecked {
  constructor() { }
  ngOnInit() {
  }
  greeting (name: string) {
    console.log('hello:' + name);
  }
  ngAfterViewInit(): void {
    console.log('子组件的视图初始化完毕');
  }
  ngAfterViewChecked(): void {
    console.log('子组件的视图变更检测完毕');
  }
}

效果图
问题图
AppComponent.html:36 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'null: undefined'. Current value: 'null: SYS'.
问题:禁止视图被组装好后再次更新视图
app.component.ts

import {AfterViewChecked, AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {ChildTwoComponent} from './child-two/child-two.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit, AfterViewChecked {
  message: string;
  constructor () {}
  ngAfterViewInit(): void {
    console.log('父组件的视图初始化完毕');
    this.message = 'SYS';
  }
  ngAfterViewChecked(): void {
    console.log('父组件的视图变更检测完毕');
  }
}

app.component.html

{{message}}

解决:换个javascript运行周期中运行
修改app.component.ts

import {AfterViewChecked, AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {ChildTwoComponent} from './child-two/child-two.component';
...
  ngAfterViewInit(): void {
    console.log('父组件的视图初始化完毕');
    setTimeout(() => {
      this.message = 'SYS';
    }, 0);
  }
...
投影
  1. –> ng g component projection
  2. projection.component.html
<div class="wrapper">
  <h2>我是子组件</h2>
  <div>这个div定义在子组件中</div>
  <!--ng-content指令:标记投影点-->
  <ng-content></ng-content>
</div>
  1. app.component.html
<div class="wrapper">
  <h2>我是父组件</h2>
  <div>这个div定义在父组件中</div>
  <app-projection>
    <div>这个div是父组件投影到子组件的</div>
  </app-projection>
</div>

效果图
2. projection.component.html

<div class="wrapper">
  <h2>我是子组件</h2>
  <ng-content select=".header"></ng-content>
  <div>这个div定义在子组件中</div>
  <ng-content select=".footer"></ng-content>
</div>
  1. app.component.html
<div class="wrapper">
  <h2>我是父组件</h2>
  <div>这个div定义在父组件中</div>
  <app-projection>
  /*注:{{插值表达式只能用父组件属性}}*/
    <div class="header">页头,这个div是父组件投影到子组件的,title是{{title}}</div>
    <div class="footer">页脚,这个div是父组件投影到子组件的</div>
  </app-projection>
</div>

效果图
app.component.html

<!--innerHTML:浏览器特定API,只能在浏览器用-->
<div [innerHTML]="divContent"></div>

app.component.ts

 divContent = '<div>慕课网</div>';

效果图

ngContent

projection.component.ts

import {AfterContentChecked, AfterContentInit, Component, OnInit} from '@angular/core';
@Component({
  selector: 'app-projection',
  templateUrl: './projection.component.html',
  styleUrls: ['./projection.component.css']
})
export class ProjectionComponent implements OnInit, AfterContentInit, AfterContentChecked {
  constructor() { }
  ngOnInit() {
  }
  ngAfterContentInit(): void {
    console.log('子组件的投影内容初始化完毕');
  }
  ngAfterContentChecked(): void {
    console.log('子组件的投影内容变更检测完毕');
  }
}

app.component.ts

import {
  AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, OnInit,
  ViewChild
} from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterContentInit, AfterContentChecked, AfterViewInit {
  ngAfterContentInit(): void {
    console.log('父组件的投影内容初始化完毕');
  }
  ngAfterContentChecked(): void {
    console.log('父组件的投影内容变更检测完毕');
  }
  ngAfterViewInit(): void {
    console.log('父组件的视图初始化完毕');
  }
}

console

ngOnDestory

–> ng g component destory
app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
...
/*1.路由配置*/
const routerConfig: Routes = [
  {path: '', component: ProjectionComponent},
  {path: 'destory', component: DestoryComponent}
]
@NgModule({
  ...
  imports: [
    BrowserModule,
    FormsModule,
    /*2.声明,把路由配置导入模块*/
    RouterModule.forRoot(routerConfig)
  ],
  ...
})
export class AppModule { }

app.component.html

<a [routerLink]="['/']">projection</a>
<a [routerLink]="['/destory']">destory</a>
<!--插座-->
<router-outlet></router-outlet>

projection.component.html

<div>projection</div>

projection.component.ts

import {AfterContentChecked, AfterContentInit, Component, OnDestroy, OnInit} from '@angular/core';
@Component({
  selector: 'app-projection',
  templateUrl: './projection.component.html',
  styleUrls: ['./projection.component.css']
})
export class ProjectionComponent implements OnInit, OnDestroy {
  ngOnDestroy(): void {
    console.log('projection组件被销毁');
  }
  constructor() { }
  ngOnInit() {
  }
}

destory.component.ts

<div>destory</div>

destory.component.ts

import {Component, OnDestroy, OnInit} from '@angular/core';
@Component(
  selector: 'app-destory',
  templateUrl: './destory.component.html',
  styleUrls: ['./destory.component.css']
})
export class DestoryComponent implements OnInit, OnDestroy  {
  ngOnDestroy(): void {
    console.log('destory组件被销毁');
  }
  constructor() { }
  ngOnInit() {
  }
}

效果图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值