几乎所有前端框架都在玩“组件化”,而且最近都不约而同地选择了“标签化”这种思路,Angular 也不例外。

对新版本的 Angular 来说,一切都是围绕着“组件化”展开的,组件是 Angular 的核心概念模型。

组件的定义

以下是一个最简单的 Angular 组件定义:

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'app-root',
  4. templateUrl: './app.component.html',
  5. styleUrls: ['./app.component.css']
  6. })
  7. export class AppComponent {
  8. title = 'itcast';
  9. }
  • @Component:这是一个 Decorator(装饰器),其作用类似于 Java 里面的注解。Decorator 这个语言特性目前(2017-10)处于 Stage 2(草稿)状态,还不是 ECMA 的正式规范。
  • selector:组件的标签名,外部使用者可以这样来使用这个组件:。默认情况下,ng 命令生成出来的组件都会带上一个 app 前缀,如果你不喜欢,可以在 angular-cli.json 里面修改 prefix 配置项,设置为空字符串将会不带任何前缀。
  • templateUrl:引用外部的 HTML 模板。如果你想直接编写内联模板,可以使用 template,支持 ES6 引入的“模板字符串”写法
  • styleUrls:引用外部 CSS 样式文件,这是一个数组,也就意味着可以引用多份 CSS 文件。
  • export class AppComponent:这是 ES6 里面引入的模块和 class 定义方式。

组件的模板

  • 内联模板
  • 模板文件

你可以在两种地方存放组件模板。 你可以使用template属性把它定义为内联的,或者把模板定义在一个独立的 HTML 文件中, 再通过@Component装饰器中的templateUrl属性, 在组件元数据中把它链接到组件。

无论用哪种风格,模板数据绑定在访问组件属性方面都是完全一样的。

具体模板语法参考:模板语法

组件通信

参考官方文档:https://angular.io/guide/component-interaction

组件就像零散的积木,我们需要把这些积木按照一定的规则拼装起来,而且要让它们互相之间能进行通讯,这样才能构成一个有机的完整系统。

在真实的应用中,组件最终会构成树形结构,就像人类社会中的家族树一样:

在树形结构里面,组件之间有几种典型的关系:父子关系、兄弟关系、没有直接关系。

相应地,组件之间有以下几种典型的通讯方案:

  • 直接的父子关系:父组件直接访问子组件的 public 属性和方法。
  • 直接的父子关系:借助于 @Input 和 @Output 进行通讯
  • 没有直接关系:借助于 Service 单例进行通讯。
  • 利用 cookie 和 localstorage 进行通讯。
  • 利用 session 进行通讯。

无论你使用什么前端框架,组件之间的通讯都离开不以上几种方案,这些方案与具体框架无关。

父子通信:Input Down

参考文档:https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding

  1. 父组件通过子组件标签传递属性
  2. 子组件在内部声明 @Input 接收
  • Input 是单向的

    • 父组件如果把数据改了,子组件也会更新
    • 但是反之不会
    • 有一个例外,引用类型修改

下面是一个示例:

子组件:

  1. import { Component, Input } from '@angular/core';
  2. import { Hero } from './hero';
  3. @Component({
  4. selector: 'app-hero-child',
  5. template: `
  6. <h3>{{hero.name}} says:</h3>
  7. <p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
  8. `
  9. })
  10. export class HeroChildComponent {
  11. // 声明接收父组件传递的数据
  12. @Input() hero: Hero;
  13. @Input('master') masterName: string; // 接收 master 重命名为 masterName
  14. }

父组件:

  1. import { Component } from '@angular/core';
  2. import { HEROES } from './hero';
  3. @Component({
  4. selector: 'app-hero-parent',
  5. template: `
  6. <h2>{{master}} controls {{heroes.length}} heroes</h2>
  7. <!-- 在子组件标签上传递数据 -->
  8. <app-hero-child *ngFor="let hero of heroes"
  9. [hero]="hero"
  10. [master]="master">
  11. </app-hero-child>
  12. `
  13. })
  14. export class HeroParentComponent {
  15. heroes = HEROES;
  16. master = 'Master';
  17. }

父子通信:Output Up

参考文档:https://angular.io/guide/component-interaction#parent-listens-for-child-event

@Output 的本质是事件机制,我们可以利用它来订阅子组件上发布的事件,子组件上这样写:

  1. import { Component, EventEmitter, Input, Output } from '@angular/core';
  2. @Component({
  3. selector: 'app-voter',
  4. template: `
  5. <h4>{{name}}</h4>
  6. <button (click)="vote(true)" [disabled]="voted">Agree</button>
  7. <button (click)="vote(false)" [disabled]="voted">Disagree</button>
  8. `
  9. })
  10. export class VoterComponent {
  11. @Input() name: string;
  12. @Output() onVoted = new EventEmitter<boolean>();
  13. voted = false;
  14. vote(agreed: boolean) {
  15. this.onVoted.emit(agreed); // 传递的数据就是事件对象
  16. this.voted = true;
  17. }
  18. }

在父组件中订阅处理:

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'app-vote-taker',
  4. template: `
  5. <h2>Should mankind colonize the Universe?</h2>
  6. <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
  7. <app-voter *ngFor="let voter of voters"
  8. [name]="voter"
  9. (onVoted)="onVoted($event)">
  10. </app-voter>
  11. <!-- $event在这里是自定义事件对象,接收到的是子组件内部发布事件传递的数据 -->
  12. `
  13. })
  14. export class VoteTakerComponent {
  15. agreed = 0;
  16. disagreed = 0;
  17. voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
  18. onVoted(agreed: boolean) {
  19. agreed ? this.agreed++ : this.disagreed++;
  20. }
  21. }

父子通信:父组件直接访问子组件 public 成员

参考文档:https://angular.io/guide/component-interaction#parent-interacts-with-child-via-local-variable

对于有直接父子关系的组件,父组件可以直接访问子组件里面 public 型的属性和方法,示例代码片段如下:

  1. <app-foo #child></app-foo>
  2. <button (click)="child.increment()">调用子组件的方法</button>

显然,子组件里面必须暴露一个 public 型的 childFn 方法,就像这样:

  1. export class FooComponent implements OnInit {
  2. public message: string = 'foo message'
  3. public count: number = 0
  4. constructor() { }
  5. public increment (): void {
  6. this.count++
  7. }
  8. ngOnInit() {
  9. }
  10. }

以上是通过在模板里面定义局部变量的方式来直接调用子组件里面的 public 型方法。在父组件的内部也可以访问到子组件的实例,需要利用到 @ViewChild 装饰器,示例如下:

  1. @ViewChild(ChildComponent)
  2. private childComponent: ChildComponent;

关于 @ViewChild 在后面的内容里面会有更详细的解释。

很明显,如果父组件直接访问子组件,那么两个组件之间的关系就被固定死了。父子两个组件紧密依赖,谁也离不开谁,也就都不能单独使用了。所以,除非你知道自己在做什么,最好不要直接在父组件里面直接访问子组件上的属性和方法,以免未来一改一大片。

没有直接关系通信:Service 单例

参考文档:https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

利用 Cookie 和 localStorage 进行通信

利用 Session 进行通信

小结

组件间的通讯方案是通用的,无论你使用什么样的前端框架,都会面临这个问题,而解决的方案无外乎本文所列出的几种。

组件生命周期

参考文档:https://angular.io/guide/lifecycle-hooks

  • ngOnChanges()

  • ngOnInit()

    • 只执行一次
  • ngDoCheck()

  • ngAfterContentInit()

    • 只执行一次
  • ngAfterContentChecked()

  • ngAfterViewInit()

    • 只执行一次
  • ngAfterViewChecked()

  • ngOnDestroy()

    • 只执行一次
  • Angular 一共暴露了8个“钩子”,构造函数不算。

  • 并没有组件或者指令会实现全部钩子。

  • 绿色的4个钩子可能会被执行很多次,紫色的只会执行一次。

  • Content 和 View 相关的4个钩子只对组件有效,指令上不能使用。因为在新版本的 Angular 里面,指令不能带有 HTML 模板。指令没有自己的 UI,当然就没有 View 和 Content 相关的“钩子”了。

  • 请不要在生命周期钩子里面实现复杂的业务逻辑,尤其是那4个会被反复执行的钩子,否则一定会造成界面卡顿。

动态组件

参考文档:https://angular.io/guide/dynamic-component-loader

!> 注意:用代码动态创建组件这种方式在一般的业务开发里面不常用,而且可能存在一些隐藏的坑,如果你一定要用,请小心避雷。

组件内容分发

第三方组件库

ng-组件的更多相关文章

  1. ng组件通讯的几种方式

    通过输入型绑定把数据从父组件传到子组件. 如<app-hero-child *ngFor="let hero of heroes"  [hero]="hero&qu ...

  2. Flume NG Getting Started(Flume NG 新手入门指南)

    Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...

  3. 2021 年最值得推荐的 7 个 Angular 前端组件库 - DevUI

    摘要:DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸.灵活.至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠.取悦眼球的设计.如果你正在开发 ToB 的工 ...

  4. Angular移除不必要的$watch之性能优化

    双向绑定是Angular的核心概念之一,它给我们带来了思维方式的转变:不再是DOM驱动,而是以Model为核心,在View中写上声明式标签.然后,Angular就会在后台默默的同步View的变化到Mo ...

  5. Flume 中文入门手冊

    原文:https://cwiki.apache.org/confluence/display/FLUME/Getting+Started 什么是 Flume NG? Flume NG 旨在比起 Flu ...

  6. [转]Angular移除不必要的$watch之性能优化

    双向绑定是Angular的核心概念之一,它给我们带来了思维方式的转变:不再是DOM驱动,而是以Model为核心,在View中写上声明式标签.然后,Angular就会在后台默默的同步View的变化到Mo ...

  7. angular schametics 使用记录

    什么是 schametics Schematics是Angular团队发布的一个代码生成工具.它提供了API,可以操作文件并在Angular项目中添加新的依赖项,ng cli 创建模板就是用它. 它也 ...

  8. 现代富文本编辑器Quill的内容渲染机制

    DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师.官方网站:devui.designNg组件库:ng-devui(欢迎S ...

  9. 手把手教你使用Rollup打包📦并发布自己的工具库🔧

    DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师. 官方网站:devui.design Ng组件库:ng-devui(欢 ...

  10. 大厂是如何用DevCloud流水线实现自动化部署Web应用的?

    DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师. 官方网站:devui.design Ng组件库:ng-devui(欢 ...

随机推荐

  1. POJ_1222_高斯消元

    题目描述: 每组数据给出一个5*6的0 1矩阵,每次操作可以把某个位置及其四周的位置0 1置换,要求输出操作位置的矩阵. 思路: 每个位置操作2次则等于没有操作,所以每个位置有操作和不操作两种选择,爆 ...

  2. error C2338: No Q_OBJECT in the class with the signal (NodeCreator.cpp)

    在Qt中,当派生类需要用到信号与槽机制时,有两个要求. 1.该类派生自QObject类. 2.类中有Q_OBJECT宏. 本次报错的原因就是因为没有在类中添加Q_OBJECT宏. 而我的出错原因更傻逼 ...

  3. sun.misc.Unsafe中一些常用方法记录

    sun.misc.Unsafe中一些常用方法记录 前情摘要 sun公司提供了可以用于直接操作内存的类,这个类就是sun.misc.Unsafe.因为Java本身是不会涉及到直接操作内存的,Java A ...

  4. python3 jenkins api操作

    一.安装依赖包 pip install python-jenkins 二.常用操作 0.调用jenkins(以下用的server都是这里的环境) import jenkins server = jen ...

  5. 珠峰-架构6-es6

    let aa = ; { console.log(aa); } // ----- let aa = ; { console.log(aa); // 报错 aa is not defined let a ...

  6. C语言实现双人控制的战斗小游戏

    实现功能 1.双人分别控制小人移动 2.子弹碰撞 3.可改变出弹方向 4.血条实体化 前言 这个游戏是看了知乎一位非常好的老师的专栏后练手写的,(至于是哪位,知乎搜C语言小游戏最牛逼的那位) 有老师系 ...

  7. QT学习之路-QT服务器-mysql数据库相关问题集锦(1)

    时间:2017-04-07 异常信息: Error - RtlWerpReportException failed with status code :-1073741823. Will try to ...

  8. C# 利用委托事件进行窗体间的传值(新手必看)

    引言: 窗体间传值是每个学习WinForm新手的常见问题,最初级的方法就是 在窗体中先获取到要接受值窗体.然后通过.得到某个空间或者属性,直接赋值,这个需要接收放的窗体属性或者空间必须是public ...

  9. java 入门如何设计类

    2019/12/24   |    在校大二上学期    |    太原科技大学 初学java后,我们会发现java难点不在于Java语法难学,而是把我们挂在了如何设计类的“吊绳”上了.这恰恰也是小白 ...

  10. Android 有关在ListView RecycleView 中使用EditText Checkbox的坑

    这是一篇文字超多的博客,哈哈哈,废话自行过滤··· 遇到问题 在开发中我们常会在ListView , RecycleView 列表中添加EditText输入框,或者checkbox复选框.   复选框 ...