angular总结3 组件交互

####输入属性

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

//父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>
//父ts
stock='';
price:number = 0;
//event接受的是子组件传递过来的参数,此处指子组件的price.
priceHandler(event:number) {
    this.price = event;
}
//子html
<div>子组件</div>
<div>股票代码是{{stockCode}},股票价格是{{price | number:'2.2-2'}}</div>
//子ts
stockCode: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>
//中间ts
price: 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>

//父ts
greeting: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
//子ts
greeting(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>
//父ts
title: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
//父ts
message: string = 'hello';
    ngAfterContentInit():void {
    console.log('父组件投影内容初始化完毕');
    this.message = 'hello senai';
}
    ngAfterContentChecked():void {
    console.log('父组件投影内容变更检测完毕');
}
    ngAfterViewInit(): void {
        console.log('父组件视图内容初始化完毕');
        }
//子ts
ngAfterContentInit():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. 你可以标记你的组件树中的一个分支,使其被排除在变更检测机制之外。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值