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
        }
    }
  }
];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容