angular总结3 组件交互

来源:互联网 发布:mysql 默认值约束 编辑:程序博客网 时间:2024/06/08 01:14
####输入属性

父组件传递数据给子组件,这是单向传递

//父html<div>父组件</div><div>    <input type='text' [(ngModel)]='stock'    //引用子组件    <app-order [stockCode]='stock' [amount]='100></app-order></div>//父ts:stock='';//子html<div>子组件</div><div>买{{amount}}{{stockCode}}股票</div>//子ts:@Input()    stockCode: string;@Input()    amount: number;

输出属性

//父html<div>父组件,获取价格为:{{price}}</div>//changePrice在子组件output时被命名<app-price-quote (changePrice)="priceHandler($event)"></app-price-quote>//父tsstock='';price:number = 0;//event接受的是子组件传递过来的参数,此处指子组件的price.priceHandler(event:number) {    this.price = event;}//子html<div>子组件</div><div>股票代码是{{stockCode}},股票价格是{{price | number:'2.2-2'}}</div>//子tsstockCode:tring='IBM';price:number;//定义输出数据,changePrice为捕获事件的名称@Output('changePrice')lastPrice: EventEmitter<number> = new EventEmitter();constructor() {    setInterval(()=> {        this.price = 100*Math.random();        //发射数据        this.lastPrice.emit(this.price);    });}

中间人模式

两个无关联的组件可通过两者的共有父组件来通信。app.component.ts是所有组件的中间组件。
注:若两组件间无中间组件,应借助于服务来共享数据。

//中间人html    <app-price-quote (buy)='buyHandler($event)'></app-price-quote>    <app-order [price]='price'></app-order>//中间tsprice: number=0;buyHandler(event:number) {    this.price = event;}//组件1 html<div>下单组件,数据来源于组件2</div><div>买入价格是{{price}}</div>//组件1 ts@Input()    price:number;//组件2 html<div>报价组件,我会将数据传递给组件1</div><div>    <input type='button' value='立即购买' (click)='buyStock($event)'></div>//组件2 ts@Output()    buy:EventEmitter<number> = new EventEmitter();price:number = 20;buyStock(event) {    this.buy.emit(this.price);}

组件生命周期

OnChanges:
父组件在初始化或修改子组件的输入参数(input)时被调用

变量分为可变变量与不可变变量
可变变量:
var user:{name:string} = {name: ‘TOM’}
user.name=’senai’ //对象的引用地址未变
不可变变量:
name:string = ‘hello’
name = ‘world’ //name指向了world这个字符串的地址,但hello字符串地址仍存在
ngChanges监控的是输入参数不可变变量

//父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>//父tsgreeting:string  = "Hello";user:{name:string} = {name: "Tom"};//子html<div class="child">  <h2>我是子组件</h2>  <div>问候语:{{greeting}}</div>  <div>姓名:{{user.name}}</div>  <div>消息: <input [(ngModel)]="message"></div></div>//子ts@Input()  greeting:string;@Input()  user:{name:string};message:string = "初始化消息";//首次初始化时输出greeting与user的值的变化,由空至有值//当父组件的输入框值变化时,仅输出greeting值变化,因onchange仅监控不可变变量//onchange未捕捉到User值的变化,但页面中子组件的user值随父组件的user值发生了变化,这个变化是由变更检测机制决定的。ngOnChanges(changes: SimpleChanges): void {    console.log(JSON.stringify(changes, null, 2));}
变更检测和DoCheck

由zone.js控制,目的是保证组件的属性变化和页面的变化是同步的,浏览器的任何异步事件都会触发变更检测(eg:点击按钮、输入数据、数据从服务器返回调用setModel方法等等)
angular有两种变更检测: default策略:如果所有组件都使用default策略,
不管变更发生在哪个组件上,zone.js都会检查整个组件树
onPush策略:如果有一个特定的组件声明自己的变更检测为onPush,只有当这个组件的OnPush发生变化时,zone.js才会检查这个组件及其子组件。

view钩子:ngAfterViewChecked与ngAfterViewInit

ngAfterViewInit在ngAfterViewChecked之前调用
这两个勾子是在组件的视图被组装完毕以后去调用
如果组件有子组件,只有当子组件的所有视图都组装完毕后才会调用父组件的。
不要在这两个勾子内改变视图中的值,如果想改变,应写在timeOut循环中。

父组件调用子组件的方法

//父html<app-child #child1></app-child><app-child #child2></app-child>//在模板中直接使用本地变量来调用子组件的方法。<button (click)="child2.greeting('jerry')">调用</button>//父ts//定义子组件的引用,使用此引用可调用子组件的方法@ViewChild('child1');child1:ChildComponent;message: string;ngOninit():void {    setInterval(()=> {        this.child1.greeting('tom');    }, 5000);}ngAfterViewInit(): voidconsole.log('父组件的视图初始化完毕');    //会抛出异常,因为在变更检测周期中,angular禁止在一个视图已经被组装好后再去更新此视图,而view勾子是在组件被组装好后触发 所以这两个view勾子都不可直接为其直接变更值,若想改变应将代码放至另一个指令循环中    //this.message='hello';    //应写为:    setTimeout(()=> {        this.message='hello';    },0);}ngAfterViewChecked(): void {    console.log('父组件的视图变更检测完毕');//子html//子tsgreeting(name: string) {    console.log('hello'+name);}//仅被调用一次ngAfterViewInit(): voidconsole.log('子组件的视图初始化完毕');}ngAfterViewChecked(): void {    console.log('子组件的视图变更检测完毕');

打印结果为:

  • 子组件的视图初始化完毕
  • 子组件的变更检测完毕
  • 父组件的视图初始化完毕
  • 父组件的 变更检测完毕
ngContent指令

投影:在不使用路由的情况下,将父组件的内容映射到子组件中,以达到动态加载子组件的目的。使用ng-content

//父html<div class='wrapper'>    <h2>父组件</h2>    //将要投影到子组件的内容定义在子组件的引用之间。    <app-child>        //header,footer样式用于为子组件指定位置        <div class='header'>父组件投影到子组件的header,title为{titile}</div>        <div class='footer'>父组件投影到子组件的footer</div>    </app-child>    //通过属性绑定来加载DOM    <div [innerHTML]='divContent'></div></div>//父tstitle:string = 'senai';divContent= '<div>插入到指定div中</div>';//子html<div class='wrapper'>    <h2>子组件</h2>    //使用ng-content来标记投影点,为投影内容占位。    <ng-content select='.header'></ng-content>    <ng-content select='.footer'></ng-content></div>//子ts
ngAfterContentChecked和ngAfterContentInit
被投影进来的内容组装完成后进行调用

先组装投影进来的内容,然后组装子组件中视图的内容。最后组装父组件自身的视图内容。
与view勾子不同,可在afterContent勾子中修改属性内容,因为此勾子被调用时整个视图未组装完毕,只是投影部分的内容被组装完毕。所以可在此处修改属性。

父子模板同ng-content//父tsmessage: string = 'hello';    ngAfterContentInit():void {    console.log('父组件投影内容初始化完毕');    this.message = 'hello senai';}    ngAfterContentChecked():void {    console.log('父组件投影内容变更检测完毕');}    ngAfterViewInit(): void {        console.log('父组件视图内容初始化完毕');        }//子tsngAfterContentInit():void {    console.log('子组件投影内容初始化完毕');}    ngAfterContentChecked():void {    console.log('子组件投影内容变更检测完毕');}

执行结果为:

  • 父组件投影内容初始化完毕
  • 父组件投影内容变更检测完毕
  • 子组件投影内容初始化完毕
  • 子组件投影内容变更检测完毕
  • 父组件视图内容初始化完毕

    生命周期总结:
    属性初始化阶段: constructor, ngOnChanges, ngOnInit, ngDoCheck
    constructor(实例化对象),
    ngOnChanges(控制输入属性),
    ngOnInit(初始化除输入属性外的其他所有属性)
    ngDoCheck(检测所有属性的变更)
    此四项完成后,视图所需的值都被初始化完毕。

    视图渲染阶段:
    ngAfterContetentInit与ngAfterContentChecked:
    投影进来的内容渲染完毕后调用此两个勾子,如果有子组件,调用子组件的投影内容渲染,直到所有子组件调用完毕后,调用下面两个勾子:

    ngAfterViewInit与ngAfterViewChecked:    视图组件渲染完毕后调用,先调用子组件的再调用父组件的。ngOnDestroy:    销毁组件,组件在路由时被被销毁,当从一个 路由跳转至另一路由时,前一路由的组件会被销毁,而后一个路由地址的组件会被创建。

组件交互总结:
1. 父子组件之间应该避免直接访问彼此的内部,而应该通过输入输出属性来通讯。
2. 组件可以通过输出属性发射自定义事件,这些事件可以携带任何你想携带的数据。
3. 在没有父子关系的组件之间,尽量使用中间人模式进行通讯。
4. 父组件可以在运行时投影一个或多个模板片段到子组件中。
5. 每个angular组件都提供了一组生命周期钩子,供你在某些特定的事件发生时执行相应的逻辑。
6. angular的变更检测机制会监控组件属性的变化并自动更新视图。这个检测非常频繁并且默认是针对整个组件树的,所以实现相关钩子时要慎重。
7. 你可以标记你的组件树中的一个分支,使其被排除在变更检测机制之外。