【Angular】组件

2021/09/09 09:44:51

创建组件

Angular CLI 创建

在项目根目录下运行 ng generate component <component-name> 命令,<component-name> 为组件名称

手动创建

<component-name>.component.ts 文件基本内容

import { Component } from "@angular/core"; // 引入 Angular 核心模块

// 组件用 @Component 装饰器
@Component({
  selector: "app-component-overview", // 指定组件填充目标选择器
  templateUrl: "./component-overview.component.html", // 指定组件的HTML模板路径,与 template 互斥
  template: `<div><div>`, // 组件HTML模板,与 templateUrl 互斥
  styleUrls: ["./component-overview.component.css"], // 指定组件 css 文件路径
  styles: ["h1 { font-weight: normal; }"], // 直接在组件内指定样式
})
export class ComponentOverviewComponent {}

@Component()

声明一个组件

示例

@Component({
  selector: "app-hero-list",
  templateUrl: "./hero-list.component.html",
  styleUrls: ["./hero-list.component.css"],
  providers: [HeroService],
})
export class HeroListComponent implements OnInit {
  /* . . . */
}

配置项

  • selector:是一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。 比如,如果应用的 HTML 中包含 <app-hero-list></app-hero-list>,Angular 就会在这个标签中插入一个 HeroListComponent 实例的视图。

  • templateUrl:该组件的 HTML 模板文件相对于这个组件文件的地址。 另外,你还可以用 template 属性的值来提供内联的 HTML 模板。 这个模板定义了该组件的宿主视图。

  • providers:注入服务。

组件生命周期

每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng 前缀构成的。

执行顺序

  1. ngOnChanges()ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。

    Angular 设置重新设置数据绑定的输入属性时响应,无法监听引用类型具体值的变化。

  2. ngOnInit() 在第一轮 ngOnChanges() 完成之后调用,只调用一次。

  3. ngDoCheck() 紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。

  4. ngAfterContentInit() 第一次 ngDoCheck() 之后调用,只调用一次。

  5. ngAfterContentChecked()ngAfterContentInit() 和每次 ngDoCheck() 之后调用

  6. ngAfterViewInit() 第一次 ngAfterContentChecked() 之后调用,只调用一次。

  7. ngAfterViewChecked()ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。

  8. ngOnDestroy() 在 Angular 销毁指令或组件之前立即调用。

组件通信

父向子传递属性

父组件将自身值传递给子组件,子组件用 @Input 接收

  • 父组件设置
<app-child [hero]="selectedHero"></app-child>
  • 子组件设置
import { Component, Input } from '@angular/core'; // 需要引入@Input装饰器

export class componentName {
    // 1.直接接收
    @Input() hero: Hero;

    // 2.设置getter、setter
    private _valueName; // 需要设置一个变量来接收
    @Input()
    get hero() { // 每次读取hero值就会触发
        // doSomething...
        return this._valueName;
    }
    set hero(name) { // 设置hero值时触发
        // doSomething...
        this._valueName = name;
    }
}

自定义事件

  • 父组件设置
<app-child (voted)="onVoted($event)"></app-child>
  onVoted(name) {
    // doSomething...
  }
  • 子组件设置

@Output() 必须是 EventEmitter 类型,它是 @angular/core 中用来发出自定义事件的类。

import { Output, EventEmitter } from "@angular/core";

export class HeroDetailComponent implements OnInit {
  @Output() voted = new EventEmitter(); // 实例化一个事件发射器

  ngOnInit() {
    this.voted.emit("here is child");
  }
}

父组件访问子组件属性

本地变量

父组件的无法通过本方法读取子组件的属性值或调用其方法

  • 父组件设置
<app-child #childVm></app-child>

<button (click)="clickBtn(childVm)">获取子组件</button>

ViewChild

  • 父组件设置
import { ViewChild } from '@angular/core'; // 引入 ViewChild 装饰器
import { HeroDetailComponent } from '../hero-detail/hero-detail.component'; // 引入子组件
export class HeroesComponent implements OnInit {
  @ViewChild(HeroDetailComponent)
  private childComponent: HeroDetailComponent;

  ngOnInit() {
    console.log(this.childComponent); // 与本地变量法获取的是同一个对象
  }
}

服务通信

不同的组件(有无关系均可)引用同一个服务实现通信,通过事件传递

  • 服务 message.service.ts
import { Injectable } from "@angular/core";

@Injectable({
  providedIn: "root",
})
export class MessageService {
  serviceData = [];

  add(message: string) {
    this.serviceData.push(message);
  }
}
  • A 组件
import { MessageService } from '../message.service';
export class componentA {
    constructor(private messageService: MessageService) { } // 在constructor函数上注册服务
    ngOnInit() {
        console.log(this.messageService.serviceData)
    }
    add() {
        this.messageService.add('from A');
    }
}
  • B 组件
import { MessageService } from '../message.service';
export class componentB {
    constructor(private messageService: MessageService) { } // 在constructor函数上注册服务
    ngOnInit() {
        console.log(this.messageService.serviceData)
    }
    add() {
        this.messageService.add('from B');
    }
}

组件样式

定义方式

  1. 在装饰器的 styleUrls 属性中指定文件路径
  2. 在装饰器的 styles 属性中直接定义
  3. 在装饰器的 template 属性中定义 <link> 标签
  4. html 文件中以 <style> 标签形式定义
  5. css 文件中使用 @import 引入其他css文件
  6. angular.json 文件中的 styles 内注册全局样式
@Component({
  styleUrls: ["./component-overview.component.css"], // 指定组件 css 文件路径
  styles: ["h1 { font-weight: normal; }"], // 直接在组件内指定样式
})
export class ComponentA {}

特殊选择器

  1. :host 选择宿主元素,即父组件中使用组件所用的标签 <componentA></componentA>
/* 设置宿主元素的样式 */
:host {
}

/* 只有当宿主元素有active的类名时才有效 */
:host(.active) {
}
  1. :host-context 在当前组件宿主元素的祖先节点中查找CSS 类
/* 只有当某个祖先元素有 CSS 类 theme-light 时,才会把样式应用到组件内部的所有 <h2> 元素中 */
:host-context(.theme-light) h2 {
}
  1. ::ng-deep 任何带有 ::ng-deep 的样式都会变成全局样式