Angular5 Component通信

Angular5是一个非常强大的前端框架,学习曲线较陡,掌握了不同模块(主要指Component)之间的通信机制,就等于掌握了Angular5。下面详尽列举各种通信方式供您参考:

一、从Component代码到Template

1. 作为Html内容

使用场景:页面加载,自动显示title的值,字号为h3。

html标签,花括号里面的是模板表达式(template expression):

<h3>{{title}}</h3>

ts代码,写在Component类内部:

title = "前端编程"。

2. 作为DOM property的值

使用场景:页面加载,自动显示图片。
<img scr="{{imageUrl}}" style="height:30px">
ts代码:
imageUrl="http://xxx.xxx.com/1.png";

二、响应事件

1. 标准Html tag的标准事件

使用场景:如按钮的点击事件:
<button (click)="onSave($event)">保存</button>
ts代码:

onSave(event) {

....

}

三、父子Component间通信

1. 通过@Input绑定输入属性

使用场景:利用第三方Component展示某个对象的细节

Angular5不允许绑定到一个不同Component的属性,除非使用@Input或者(以后说)@Output显式地申明。

比如传递一个当前的收货地址到一个地址细节的Component:

<app-address-detail [address]="currentAddress"></app-address-detail>
ts代码:
@Input() address: Address;

2. 通过@ViewChild并且使用Component方式,父---->子

使用场景:页面一部分一个点击事件,导致另一部分产生变化

比如:点击一个按钮,显示点击次数,其中显示点击次数的逻辑由另外一个Component负责。

子Component代码:

@Component({

    selector: 'app-number',

    template: `<b>{{message}}</b>`

})

export class NumberComponent {

    message: string = '';

    count: number = 0;

    increaseByOne() {

        this.count = this.count + 1;

        this.message = "次数: " + this.count;

}

父template:

<button type="button" (click)="increase()">增加</button>

<app-number></app-number>

父Component代码:

export class NumberParentComponent {

    @ViewChild(NumberComponent)

    private numberComponent: NumberComponent;

    increase() {

        this.numberComponent.increaseByOne();

    }
}

3. @ViewChild和ElementRef,通过Directive

使用场景:点击页面一部分,另一部分颜色发生变化
需要获取Directive所在的整个元素(element),在Directive方法内部,修改那个元素的Dom属性,颜色。

子Directive代码:

@Directive({
  selector: '[chColor]'
})
export class ChColorDirective implements AfterViewInit{
  constructor(private elementRef: ElementRef) {
  }

  ngAfterViewInit(){
    this.elementRef.nativeElement.style.color = 'green';
  }

  change(changedColor: String){
    this.elementRef.nativeElement.style.color = changedColor;
  }
}

父Template代码:

<p chColor>改变我的颜色</p>
<div>
  修改颜色:
  <input type="button" name="red" (click)="changeColor('red')"> 红色
</div>

4. @ViewChildren,通过Component

使用场景:比如删除一组子Component,每个子Com前面有checkbox。
父Component的Template:

<section>
  <h4 *ngIf="todos.getAll().length">Todo列表</h4>
  <todo-item *ngFor="let todo of todos.getAll()" [todo]="todo"></todo>
</section>

父component代码:

export class TodoAppComponent implements AfterViewInit {
  @ViewChildren(TodoComponent) todoComponents: QueryList<TodoComponent>;
  constructor(private todos: TodoList) {}
  ngAfterViewInit() {
    // viewChildren在这个地方变得可用
  }

因为Angular的DOM编译器会先处理父Component,然后再处理children,这样在初始化的时候,todosComponent是未定义的,undefined。他们的值在ngAfterViewInit函数里面设置。比如把获取到的todosComponent编程数组再赋值给todos。
获得了子Component列表之后,就可以按照需求处理了。

5. 往一个模板里插入一块动态内容,单槽

使用场景:父Component往子Component里面插入一块Html
往什么地方插呢?这是由<ng-content>标签定义了插槽。
子Card Component的Template:

<div class="card">
  <!--单槽插入点-->
  <ng-content></ng-content>
</div>

父Component的Template:

<card>
  <!--下面是动态内容-->
  <div class="card-content">
    <p>This is dynamic content</p>
  </div>
</card>

6. 往一个模板里插入多块内容,多槽

使用场景:父Component往子Component里面插入多块Html内容,插入地点由selector匹配

子Card Component的Template:

<div>
  <!--tag-->
  <ng-content select="header"></ng-content>
  <!--css 选择器-->
  <ng-content select="div.body"></ng-content>
  <!--attribute-->
  <ng-content select="[card][body]"></ng-content>
  <!--带值的attribute-->
  <ng-content select="[card-type=body]"></ng-content>
</div>

父Component的Template:

<card>
  <header>...</header>
  <div class="body">我是body</div>
  <div body card>...</div>
  <div card-type="body">...</div>
</card>

7. 当Template涉及三个Components,@ContentChild

使用场景:在第二个children里面获取第三个Component信息

@Component({
  selector: 'app-footer',
  template: '<ng-content></ng-content>'
})
class FooterComponent{}

@Component(...)
class TodoAppComponent implements AfterContentInit {
  @ContentChild(FooterComponent) footer: FooterComponent;
  ngAfterContentInit() {
    // this.footer now points to the instance of 'FooterComponent'
  }
}

@Component({
  selector: 'demo-app',
  template: `
    <content>
      <todo-app>
        <app-footer>
          <small>Yet another todo app!</small>
        </app-footer>
      </todo-app>
    </content>
  `
})
export class AppComponent{}

这里AppComponent使用TodoAppComponent并在它的一对tag之间传递FooterComponent给它,我们称FooterComponent是TodoAppComponent的content child。

8. 内层Component向父Component发事件

使用场景:父Component删除子Component,但是删除操作在子Component上,比如一个按钮。
子Component暴露一个EventEmitter为属性,然后绑定到父Component的一个函数,绑定之后,父Component就开始侦听这个事件了。
子Component:

@Component({
  template: `
    <div>
      <button (click)="delete()">删除</button>
    </div>`
})
export class HeroDetailComponent{
  // this component makes a request but it can't actually delete a hero
  deleteRequest = new EventEmitter<Hero>();

  delete() {
    this.deleteRequest.emit(this.hero);
  }
}

父Component的Template:
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero">
当子Component触发事件时,Angular调用父Component的deleteHero方法,把要传递的hero作为$event变量传递出来。

四、同一个Component,修改Html另一部分的Native属性

1. 通过@ViewChild和模板引用变量访问Native Element

使用场景:获取一行子Html并修改该Html属性,颜色
Template代码:

<div>
  姓名:<input type="text" #name><br/>
</div>

ts代码:

export class ExampleComponent implements AfterViewInit {
  @ViewChild('name')
  private name: ElementRef;

  ngAfterViewInit() {
    this.name.nativeElement.style.color = 'red';
  }
}

五、非父子Component间通信

1. 通过路由传递参数(Observable)

使用场景:地址列表,还有两个按钮(上一个,下一个地址)浏览历史地址,不希望每切换一个地址都重新创建一个新的地址Component,而是复用一个地址Component。
当router创建新的Component时,会调用ngOnInit()函数,这样写:

  this.address = this.route.paramMap
    .switchMap(params: ParamMap) =>
      this.service.getAddress(params.get('id')));

这里this.service.getAddress返回一个Observable<Address>对象。

2. 通过路由传递参数(非Observable)

使用场景:地址列表,从列表中每选择一个,切换为该地址的详细信息,如果想显示另外一个地址的详细信息,必须先回到列表页面;这页意味着每选择一个地址都重新创建一个新的地址Component实例。

let id = this.route.snapshot.paramMap.get('id');
this.address = this.service.getAddress(id);

3. 复杂参数通过可选参数传递(Optional Parameters)

使用场景:需要传递参数的场景太多,不可能每一个参数都对应一个route,如果那样的话路由配置会相当复杂,这时候可选参数就派上用场了,不需要更改路由配置。
观察地址栏,可选参数不同于路由参数,也不是查询参数,而是矩阵参数,矩阵参数也是一个标准,并非angular发明的,形式如下:
;id=15;foo=foo

发送端ts代码:
this.router.navigate(['/list', {id: addressId, foo: 'foo'}]);
接收端还是通过ActivatedRoute获取地址Id:

this.addresses = this.route.paramMap
  .switchMap(params: ParamMap) =>
    // (+) before 'params.get()' turns the string into a number
    this.selectedId = +params.get('id');
    return this.service.getAddresses();
  });

六、DOM和Directive之间的通信

1. Directive更改Host元素的外观和行为,@HostListener

使用场景:一个Html tag,比如p,应用了一个Directive,当用户鼠标移到这个p上面,高亮这个p元素;鼠标移开,取消高亮。
分析这个需求,要求Directive能监听到Host元素的事件(Hover, Leave等),这是@HostListener的作用,把@HostListener放到某个函数前面,当事件发生时就会调用这个函数。

Template:
<p appHighlight>点亮我</p>
Directive代码:

@HostListener('mouseenter') onmouseenter() {
  this.hightlight('yellow');
}

@HostListener('mouseleave') onmouseleave() {
  this.highlight(null);
}

private highlight(color: string) {
  this.el.nativeElement.style.backgroundColor = color;
}

以上设置背景颜色的方式假定了我们的程序运行在浏览器环境下。但是Angular瞄准的是跨平台,所以Angular提供了一种平台独立的方式来设置因素的属性,Renderer2正是用于这种目的。
this.render.setStyle(el.nativeElement, 'width', '200px');

2. @HostBinding更改Host元素属性

使用场景:更改Host元素的外观或者行为,通过把Directive内部的属性和Host元素的属性绑定起来,实现了,内部属性一发生变化,Host元素相应的属性就会发生变化。
Directive代码:
@HostBinding('class.card-outline')private isHovering: boolean;
然后在Directive内部的事件处理函数中更改isHovering的值,从而间接改变Host元素的属性。

3. Resolver

使用场景:由一个Component导航到另外一个Component,可能要事先到服务器取出数据,如果数据存在,则继续导航;如果不存在,则取消导航,Resolver就是取数据的。
路由配置:

path: 'crisis-center',
component: CrisisCenterComponent,
children: [
  {
    path: '',
    Component: CrisisListComponent,
    children: [
      {
        path: ':id',
        component: CrisisDetailComponent,
        canDeactivate: [CanDeactivateGuard],
        resolve: {
          crisis: CrisisDetailResolver
        }
    }
  }
];

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

novodexx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值