目录
组件构成要素
先决条件
如何创建一个组件
组件的生命周期(高阶内容,掌握ngOnInit、ngOnDestroy、ngOnChanges、ngAfterViewInit())
组件之间的通信(重要,必须掌握)
动态组件
首页 web前端 js教程 一文看看angular10组件的相关概念

一文看看angular10组件的相关概念

Jul 27, 2021 am 10:06 AM
组件

本篇文章给大家了解一下angular10中的组件,介绍一下组件构成要素、组件的生命周期、组件间的通信以及动态组件的基本使用,下面来一起看看。

一文看看angular10组件的相关概念

【相关教程推荐:《angular教程》】

组件构成要素

  • html模板
  • typescript,定义行为
  • css组,可以引入多个css文件,因此可以在一个组件里面定义多个css文件
//从angular主模块中引入Component(组件装饰器或组件注解)
import { Component } from '@angular/core';

//装饰器中以json的形式声明元数据
@Component({
  //它指定了一个叫 <app-root> 的元素。 该元素是 index.html 文件里的一个占位符
  //为什么这个组件跟入口index建立了联系呢?因为入口main.ts中绑定了主模块为appModule
  selector: 'app-root',   //在模板中找对应的标签,找到后创建并插入该组件实例
  templateUrl: './app.component.html',  // html模板
  styleUrls: ['./app.component.css'],   // css样式,可以引入多个css文件
  // 这个属性(内联模板)和templateUrl(外联模板)二选一,template后面可以直接跟html字符串
  // 注意在模板语法(反引号)中是使用插值表达式,不能使用${}插入值
  template: `<h1>{{title}}</h1>`   
})
//组件控制器,写逻辑代码的地方
export class AppComponent {
  title = 'myAngular';
  //构造函数可以用来进行属性的声明和初始化语句
  //在angular里面有个特别重要的点要记住:只能通过构造函数注入依赖
  constructor() {}
}
登录后复制

先决条件

  • 安装 Angular CLI (npm install -g @angular/cli)
  • 建立一个 Angular 项目 (ng new )

如果不满足这两个条件,请参考 搭建环境。

如何创建一个组件

使用 Angular CLI 创建一个组件

ng generate component <project-name> // 创建一个组件

ng g c <project-name> // 缩写
登录后复制

常用的创建组件的一些其他选项

ng g c <project-name>  --skip-tests // 创建一个组件,且不用安装测试文件

ng g c <project-name> --inline-style // 缩写-s,内联样式

ng g c <project-name> --inline-template // 缩写-t,内联模板

ng g c <project-name> --module= <module-name> // 指定创建的组件在哪个模块引用,在多个模块的项目中会用到
登录后复制

除了通过angular cli自动生成组件外,也可以手动创建组件(不推荐),这里不介绍。

组件的生命周期(高阶内容,掌握ngOnInit、ngOnDestroy、ngOnChanges、ngAfterViewInit())

在工作中经常能够用到的生命周期就两个(ngOninit和ngOnDestroy),其他的生命周期很少用到;但是如果能够掌握组件的生命周期,对angular会掌握的得更加深刻。

生命周期含义

当 Angular 实例化组件类并渲染组件视图及其子视图时,组件实例的生命周期就开始了。生命周期一直伴随着变更检测,Angular 会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就结束了。当 Angular 在执行过程中创建、更新和销毁实例时,指令就有了类似的生命周期。

应用:

你的应用可以使用生命周期钩子方法来触发组件或指令生命周期中的关键事件,以初始化新实例,需要时启动变更检测,在变更检测过程中响应更新,并在删除实例之前进行清理。

如何实现生命周期事件

每个组件或指令都可以实现一个或多个生命周期钩子,这些生命周期钩子可以在适当的时候对组件或指令实例进行操作。

每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng 前缀构成的。比如,OnInit 接口的钩子方法叫做 ngOnInit()。如果你在组件或指令类中实现了这个方法,Angular 就会在首次检查完组件或指令的输入属性后,紧接着调用它

import { Component } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-root&#39;,
  styleUrls: [&#39;./app.component.css&#39;],  
  template: `<h1>{{title}}</h1>`   
})
// 实现OnInit生命周期,可以实现多个生命周期
export class AppComponent implements OnInit{ 
  title = &#39;myAngular&#39;;
  constructor() {}
  ngOnInit(){
      console.log("Angular在首次检查完组件的输入属性后,然后调用它,只调用一次")
  }
}
登录后复制

生命周期概览

钩子时机用途注意
ngOnChanges()当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前当 Angular 设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。
ngOnInit()在第一轮 ngOnChanges() 完成之后调用,只调用一次。在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。组件获取初始数据的好地方很重要,只调用一次
ngDoCheck()紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。它和ngOnChanges一样发生的很频繁
ngAfterContentInit()第一次 ngDoCheck() 之后调用,只调用一次。当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。只调用一次
ngAfterContentChecked()ngAfterContentInit() 和每次 ngDoCheck() 之后调用每当 Angular 检查完被投影到组件或指令中的内容之后调用。
ngAfterViewInit()第一次 ngAfterContentChecked() 之后调用,只调用一次。初始化完组件视图及其子视图之后调用。只调用一次
ngAfterViewChecked()ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。每次做完组件视图和子视图的变更检测之后调用。
ngOnDestroy()在 Angular 销毁指令/组件之前调用。当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。
取消订阅可观察对象、
清除定时器、
反注册该指令在全局或应用服务中注册过的所有回调。
很重要

重点生命周期详解

初始化组件和指令 ngOnInit

  • 在构造函数外部执行复杂的初始化。组件的构造应该既便宜又安全。比如,你不应该在组件构造函数中获取数据。当在测试中创建组件时或者决定显示它之前,你不应该担心新组件会尝试联系远程服务器。ngOnInit() 是组件获取初始数据的好地方
  • 在 Angular 设置好输入属性之后设置组件。构造函数应该只把初始局部变量设置为简单的值。请记住,只有在构造完成之后才会设置指令的数据绑定输入属性。如果要根据这些属性对指令进行初始化,请在运行 ngOnInit() 时设置它们

在实例销毁时进行清理 ngOnDestroy

这里是释放资源的地方,这些资源不会自动被垃圾回收。如果你不这样做,就存在内存泄漏的风险。

  • 取消订阅可观察对象和 DOM 事件。
  • 停止 interval 计时器。
  • 反注册该指令在全局或应用服务中注册过的所有回调。
  • ngOnDestroy() 方法也可以用来通知应用程序的其它部分,该组件即将消失

页面埋点

可以在组件中通过ngOnInit和ngOnDestroy方法统计页面的时长,

更好的做法可以通过指令实现ngOnInit和ngOnDestroy生命周期,用来统计页面时长

也可以通过路由守卫来记录页面出入栈所需要的时间

@Directive({selector: &#39;[appSpy]&#39;})
export class SpyDirective implements OnInit, OnDestroy {

  constructor(private logger: LoggerService) { }

  ngOnInit()    { this.logIt(`onInit`); }

  ngOnDestroy() { this.logIt(`onDestroy`); }

  private logIt(msg: string) {
    this.logger.log(`Spy #${nextId++} ${msg}`);
  }
}
登录后复制

使用变更检测钩子 ngOnchanges

一旦检测到该组件或指令的输入属性发生了变化,Angular 就会调用它的 ngOnChanges() 方法。

因为这个方法会频繁执行,所以要注意判断的计算量,会影响性能。

// 可以监听输入属性的变化
ngOnChanges(changes: SimpleChanges) {
  for (const propName in changes) {
    const chng = changes[propName];
    const cur  = JSON.stringify(chng.currentValue);
    const prev = JSON.stringify(chng.previousValue);
    this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
  }
}
登录后复制

组件之间的通信(重要,必须掌握)

一、父子组件的通信(基础)

父传子

1、父组件通过属性绑定向子组件传值

import { Component } from &#39;@angular/core&#39;;
@Component({
  selector: &#39;app-parent&#39;,
  template: `
    <app-child [msg]="msg"></app-child>
  `
})
export class ParentComponent {
  msg = &#39;父组件传的值&#39;;
}
登录后复制

2、子组件通过@Input接收数据

import { Component, Input } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-child&#39;,
  template: `
    <p>{{msg}}</p>
  `
})
export class ChildComponent {
  @Input() msg: String;
}
登录后复制

子传父

1、子组件通过自定义事件,向父组件发送数据

import { Component, Input, EventEmitter, Output} from &#39;@angular/core&#39;;
,
@Component({
  selector: &#39;app-child&#39;,
  template: `
    <p>{{msg}}</p>
    <button (click)="vote()" >发送</button>
  `
})
export class ChildComponent {
  @Input() msg: String;
  @Output() voted = new EventEmitter<boolean>();
  
  vote() {
    this.voted.emit("子组件传的值");
  }
}
登录后复制

2、父组件通过监听自定义事件,接收子组件的传值

import { Component } from &#39;@angular/core&#39;;
@Component({
  selector: &#39;app-parent&#39;,
  template: `
    <app-child [msg]="msg" (voted)="voted($event)"></app-child>
  `
})
export class ParentComponent {
  msg = &#39;父组件传的值&#39;;
  voted(val){ //监听自定义事件的传值
      console.log(val)
  }
}
登录后复制

子组件怎么监听输入属性值的变化?(2种方法)

1、可以使用一个输入属性@Input()的 setter,以拦截父组件中值的变化。

import { Component, Input } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-child&#39;,
  template: &#39;<h3>"{{name}}"</h3>&#39;
})
export class ChildComponent {
  @Input()
  get name(): string { return this._name; }
  set name(name: string) {
    this._name = (name && name.trim()) || &#39;<no name set>&#39;;
  }
  private _name = &#39;&#39;;
}
登录后复制

2、通过ngOnChange()来截听输入属性值的变化

当需要监视多个、交互式输入属性的时候,本方法比用属性的 setter 更合适。

import { Component, Input, OnChanges, SimpleChanges } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-version-child&#39;,
  template: `
    <h3>Version {{major}}.{{minor}}</h3>
    <h4>Change log:</h4>
    <ul>
      <li *ngFor="let change of changeLog">{{change}}</li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major: number;
  @Input() minor: number;
  changeLog: string[] = [];

  ngOnChanges(changes: SimpleChanges) { //ngOnchange适合监听子组件多个输入属性的变化
    const log: string[] = [];
    for (const propName in changes) {
      const changedProp = changes[propName];
      const to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        const from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(&#39;, &#39;));
  }
}
登录后复制

父组件怎么读取子组件的属性和调用子组件的方法?(2种方法)

1、通过本地变量代表子组件

父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,如下例所示。

思考:父组件可以通过这种方式读取子组件的私有属性和私有方法吗?

父组件

import { Component } from &#39;@angular/core&#39;;
import { CountdownTimerComponent } from &#39;./countdown-timer.component&#39;;

@Component({
  selector: &#39;app-countdown-parent-lv&#39;,
  template: `
  <h3>Countdown to Liftoff (via local variable)</h3>
  <button (click)="timer.start()">Start</button> //调用子组件方法
  <button (click)="timer.stop()">Stop</button>
  <div class="seconds">{{timer.seconds}}</div> //读取子组件属性
  <app-countdown-timer #timer></app-countdown-timer>
  `,
  styleUrls: [&#39;../assets/demo.css&#39;]
})
export class CountdownLocalVarParentComponent { }
登录后复制

子组件

import { Component, OnDestroy } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-countdown-timer&#39;,
  template: &#39;<p>{{message}}</p>&#39;
})
export class CountdownTimerComponent implements OnDestroy {

  intervalId = 0;
  message = &#39;&#39;;
  seconds = 11;

  ngOnDestroy() { this.clearTimer(); }

  start() { this.countDown(); }
  stop()  {
    this.clearTimer();
    this.message = `Holding at T-${this.seconds} seconds`;
  }

  private clearTimer() { clearInterval(this.intervalId); }

  private countDown() {
    this.clearTimer();
    this.intervalId = window.setInterval(() => {
      this.seconds -= 1;
      if (this.seconds === 0) {
        this.message = &#39;Blast off!&#39;;
      } else {
        if (this.seconds < 0) { this.seconds = 10; } // reset
        this.message = `T-${this.seconds} seconds and counting`;
      }
    }, 1000);
  }
}
登录后复制

2、父组件调用@viewChild() (基础,推荐使用)

这个本地变量方法是个简单便利的方法。但是它也有局限性(只能在模板html中使用),因为父组件-子组件的连接必须全部在父组件的模板中进行。父组件本身的ts代码对子组件没有访问权。

当父组件类需要访问子组件时,可以把子组件作为 ViewChild,注入到父组件里面。

父组件类中访问子组件的属性和方法:

import { AfterViewInit, ViewChild } from &#39;@angular/core&#39;;
import { Component } from &#39;@angular/core&#39;;
import { CountdownTimerComponent } from &#39;./countdown-timer.component&#39;; //引入子组件

@Component({
  selector: &#39;app-countdown-parent-vc&#39;,
  template: `
  <h3>Countdown to Liftoff (via ViewChild)</h3>
  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>
  <div class="seconds">{{ seconds() }}</div>
  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: [&#39;../assets/demo.css&#39;]
})
export class CountdownViewChildParentComponent implements AfterViewInit {

  //通过 @ViewChild 属性装饰器,将子组件 CountdownTimerComponent 注入到私有属性 timerComponent 里面。
  @ViewChild(CountdownTimerComponent) 
  private timerComponent: CountdownTimerComponent;

  seconds() { return 0; }

  // angular创建了组件的子视图后会调用它,注意获取子组件的属性,要在子组件视图加载之后
  ngAfterViewInit() {
    // 访问子组件属性
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }

  start() { this.timerComponent.start(); } // 访问子组件的方法
  stop() { this.timerComponent.stop(); }
}
登录后复制

注意:(使用场景很多,必须掌握)

ngAfterViewInit() 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以它先把秒数显示为 0.

然后 Angular 会调用 ngAfterViewInit 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。应用在显示秒数之前会被迫再等一轮。

使用 setTimeout() 来等下一轮,然后改写 seconds() 方法,这样它接下来就会从注入的这个计时器组件里获取秒数的值。

二、组件通过服务来通信(发布订阅者模式,基础,必须掌握)

父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通信。

不仅局限于父子组件,只要组件与组件共享同一个服务,就可以实现数据通信。

<!--parent.component.html-->
<p style="width: 1000px;margin: auto">
<p class="card" style="width: 500px;float: left">
 <p class="card-header"> 父组件</p>

 <p class="card-body">
  <h5 class="card-title">父组件</h5>
  <p class="form-group">
   <label for="serviceoutput">父组件服务输入:</label>
   <input type="text"
       class="form-control"
       id="serviceoutput"
       placeholder="服务输入"
       [(ngModel)]="serviceInput"
   >
  </p>

  <button class="btn btn-primary" (click)="clickService()">Service方式</button>
 </p>

</p>
<app-child></app-child>
</p>
登录后复制
<!--child.component.html-->
<p class="card" style="width: 500px;">
 <p class="card-header">子组件</p>

 <p class="card-body">
  <h5 class="card-title">子组件</h5>
  <p class="form-group">
   <label for="serviceoutput">子组件服务输入:</label>
   <input type="text"
       class="form-control"
       id="serviceoutput"
       placeholder="服务输入"
       [(ngModel)]="serviceInput"
   >
  </p>

  <button class="btn btn-primary" (click)="clickService()">Service方式</button>
 </p>
</p>
登录后复制
//服务重点
//meditor.service.ts

import {Injectable} from &#39;@angular/core&#39;;
import {Subject} from &#39;rxjs/Subject&#39;;
import {Observable} from &#39;rxjs/Observable&#39;;

@Injectable()
export class MeditorService {
 private subject = new Subject<MeditorMsg>();
 constructor() {}
 // 获取订阅者
 public getObservable(): Observable<MeditorMsg> {
  return this.subject.asObservable();
 }

 // 推送信息
 public push(msg: MeditorMsg) {
  this.subject.next(msg);
 }
}

// 中间者信息
export interface MeditorMsg {
 id: string;
 body: any;
}
登录后复制
subscription: Subscription = null; //初始化一个订阅对象

//子组件构造函数,用于监听数据推送
constructor(private meditor: MeditorService) {
  this.subscription = meditor.getObservable().subscribe(
   msg => {
    console.log(msg);
    if (msg.id === &#39;parent&#39;) {   //id为parent,获取父组件数据
     this.serviceInput = msg.body;
    }
   }
  );
 }
// 子组件将数据推送到中间着,给订阅者
clickService() {
  this.meditor.push({id: &#39;parent&#39;, body: this.serviceInput});
 }

//父组件构造函数,用于监听数据推送
constructor(private meditor: MeditorService) {
  this.subscription = meditor.getObservable().subscribe(
   msg => {
    console.log(msg);
    if (msg.id === &#39;child&#39;) {    //id为child,获取子组件数据
     this.serviceInput = msg.body;
    }
   }
  );
 }

// 父组件将数据推送到中间着,给订阅者
clickService() {
  this.meditor.push({id: &#39;parent&#39;, body: this.serviceInput});
 }
 
// 注意:订阅一个对象,就是在生命周期结束前,要取消订阅。
ngOnDestroy() {
    this.subscription.unsubscribe();
}
登录后复制

思考: 这种发布订阅者模式适合全局状态管理吗?

三、可以通过本地缓存来实现通信(Cookie,LocalStorage、SessionStorage)

<!-- catch_namae_type.ts -->
// 目的是好维护
// 项目当中用到的页面缓存,需要在这里进行声明;key-value保持一致
// 声明规则,不同类型的缓存使用前缀 session_/ local_ / cookie_
// 动态设置缓存不用在这里声明,但是需要在key后面加&#39;_noSetType_&#39;标识
export const CatchNameType = {
  session_userInfo: &#39;session_userInfo&#39;, // 用户信息
  session_toekn: &#39;session_token&#39;, // token
  local_loginInfo: &#39;local_loginInfo&#39;, // 本地缓存用户名密码
};

<!--catch.ts-->
import { Injectable } from &#39;@angular/core&#39;;
// 定义这个类,主要是看全局定义了哪些本地缓存
import { CatchNameType } from &#39;./catch_namae_type&#39;;

// -------------------------------------------------------缓存工具类(三类方法)
// Cookie           (方法有:set/get/remove)
// SStorage(sessionStorage)  (方法有:set/get/remove/clear)
// LStorage(localStorage)    (方法有:set/get/remove/clear)
@Injectable({
  providedIn: &#39;root&#39;,
})
export class Catch {
  // cookie
  public static Cookie = {
    /**
     * cookie 存贮
     * @param key  属性
     * @param value  值
     * @param String expire  过期时间,单位天
     */
    set(key: string, value: any, expire: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        const d = new Date();
        d.setDate(d.getDate() + expire);
        document.cookie = `${key}=${value};expires=${d.toDateString()}`;
      }
    },
    get(key: string): string {
      const cookieStr = unescape(document.cookie);
      const arr = cookieStr.split(&#39;; &#39;);
      let cookieValue = &#39;&#39;;
      // tslint:disable-next-line: prefer-for-of
      for (let i = 0; i < arr.length; i++) {
        const temp = arr[i].split(&#39;=&#39;);
        if (temp[0] === key) {
          cookieValue = temp[1];
          break;
        }
      }
      return cookieValue;
    },
    remove(key: string): void {
      document.cookie = `${encodeURIComponent(key)}=;expires=${new Date()}`;
    },
  };

  // sessionStorage
  public static SStorage = {
    set(key: string, value: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        sessionStorage.setItem(key, JSON.stringify(value));
      }
    },
    get(key: string): any {
      const jsonString =
        sessionStorage.getItem(key) === &#39;undefined&#39;
          ? undefined
          : sessionStorage.getItem(key);
      return jsonString ? JSON.parse(jsonString) : null;
    },

    remove(key: string): void {
      sessionStorage.removeItem(key);
    },
    clear(): void {
      sessionStorage.clear();
    },
  };

  // localStorage
  public static LStorage = {
    set(key: string, value: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        localStorage.setItem(key, JSON.stringify(value));
      }
    },
    get(key: string): any {
      const jsonString =
        localStorage.getItem(key) === &#39;undefined&#39;
          ? undefined
          : localStorage.getItem(key);
      return jsonString ? JSON.parse(jsonString) : null;
    },
    remove(key: string): void {
      localStorage.removeItem(key);
    },
    clear(): void {
      localStorage.clear();
    },
  };

  // 设置缓存的时候是否在catch_name_type里面声明
  static is_set_catch_name_type(key: string): boolean {
    let allow = false;
    // 对动态设置缓存不进行检查
    if (key.indexOf(&#39;_noSetType_&#39;) !== -1) {
      allow = true;
      console.log(&#39;动态设置缓存&#39;, key);
      return allow;
    }
    // 对命名规则进行检查
    const nameRule =
      key.indexOf(&#39;session_&#39;) !== -1 ||
      key.indexOf(&#39;local_&#39;) !== -1 ||
      key.indexOf(&#39;cookie_&#39;) !== -1;
    if (!nameRule) {
      allow = false;
      console.log(&#39;命名规则错误&#39;, key);
      return allow;
    }
    // 静态设置的缓存需要配置类型
    Object.values(CatchNameType).forEach((item) => {
      if (item === key) {
        allow = true;
      }
    });
    if (!allow) {
      console.log(&#39;缓存操作失败,请检查配置缓存类型&#39;);
    }
    return allow;
  }
}
登录后复制

四、页面路由传参也可以实现单向通信

这部分内容,我会在路由章节整理。

组件通信总结

所以组件通信大概有如下几种:

1、父子组件通信(1、@Input() @output 2、本地变量#val 3、@viewChild())

2、通过服务

3、页面缓存

4、页面级组件传参(两个页面等同于两个组件)

动态组件

组件的模板不会永远是固定的。应用可能会需要在运行期间按需加载一些新的组件。 通过下面的例子可以了解动态组件的基本使用

1、创建组件,被引入的组件

@Component({
  template: `<span>hello world</span>`
})
export class DynamicComponent { }
登录后复制

2、创建容器组件,用于加载动态组件

@Component({
  selector: &#39;app-container&#39;,
  template: `
    <div #dynamicContainer></div>
    <button (click)="createComponent()">Create</button>
  `
})
export class AppContainerComponent {
  // 声明容器
  @ViewChild("dynamicContainer", { read: ViewContainerRef }) container: ViewContainerRef;
}
登录后复制

在AppContainerComponent组件中,通过@ViewChild装饰器来获取视图中的模板元素,如果没有指定第二个查询参数,则默认返回 组件实例或相应的DOM元素,但这个示例中,我们需要获取ViewContainerRef实例也就是视图容器。可以在视图容器中创建、插入、删除组件等。

3、动态创建组件

在创建组件之前,需要注入ComponentFactoryResolver服务对象,该服务是动态加载组件的核心,可以将一个组件实例呈现到另一个 组件视图上。

  • 使用ComponentFactoryResolve将已声明但未实例化的组件解析成为可以动态加载的component
//依赖组件类型获取对应的factory,从名字上可以看出该factory是用来初始化组件的
const componentFactory = this.ComponentFactoryResolver(DynamicComponent);
//调用视图容器的createComponent方法并将组件添加到容器上。该方法内部会调用factory的create方法来初始化组件。
const modalContainerRef = this.container.createComponent(componentFactory);
登录后复制

4、为组件属性赋值

通过如下的方式为组件属性进行赋值

modalContainerRef.instance.property = ***;
登录后复制

5、销毁组件

在使用组件后,记得要销毁组件。

modalContainerRef.destroy();
登录后复制

6、组件注册

为了能够动态创建组件需要将组件在模块的entryComponents中声明。因为在entryComponents中声明的组件Angular都会创建一个 ComponentFactory并将其存储在ComponentFactoryResolver中,这是动态加载必需的步骤。

更多编程相关知识,请访问:编程入门!!

以上是一文看看angular10组件的相关概念的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何安装Win10旧版本组件DirectPlay 如何安装Win10旧版本组件DirectPlay Dec 28, 2023 pm 03:43 PM

不少用户在玩win10的的一些游戏的时候总是会遇到一些问题,比如说卡屏和花屏等等情况,这个时候我们是可以采用打开directplay这个功能来解决的,而且功能的操作方法也很简单。win10旧版组件directplay怎么安装1、在搜索框里面输入“控制面板”然后打开2、查看方式选择大图标3、找到“程序和功能”4、点击左侧的启用或关闭win功能5、选择旧版这里的勾选上就可以了

如何使用 Vue 实现日历组件? 如何使用 Vue 实现日历组件? Jun 25, 2023 pm 01:28 PM

Vue是一款非常流行的前端框架,它提供了很多工具和功能,如组件化、数据绑定、事件处理等,能够帮助开发者构建出高效、灵活和易维护的Web应用程序。在这篇文章中,我来介绍如何使用Vue实现一个日历组件。1、需求分析首先,我们需要分析一下这个日历组件的需求。一个基本的日历应该具备以下功能:展示当前月份的日历页面;支持切换到前一月或下一月;支持点击某一天,

VUE3开发基础:使用extends继承组件 VUE3开发基础:使用extends继承组件 Jun 16, 2023 am 08:58 AM

Vue是目前最流行的前端框架之一,而VUE3则是Vue框架的最新版本,相较于VUE2,VUE3具备了更高的性能和更出色的开发体验,成为了众多开发者的首选。在VUE3中,使用extends继承组件是一个非常实用的开发方式,本文将为大家介绍如何使用extends继承组件。extends是什么?在Vue中,extends是一个非常实用的属性,它可以用于子组件继承父

Angular组件及其显示属性:了解非block默认值 Angular组件及其显示属性:了解非block默认值 Mar 15, 2024 pm 04:51 PM

Angular框架中组件的默认显示行为不是块级元素。这种设计选择促进了组件样式的封装,并鼓励开发人员有意识地定义每个组件的显示方式。通过显式设置CSS属性 display,Angular组件的显示可以完全控制,从而实现所需的布局和响应能力。

聊聊Vue怎么通过JSX动态渲染组件 聊聊Vue怎么通过JSX动态渲染组件 Dec 05, 2022 pm 06:52 PM

Vue怎么通过JSX动态渲染组件?下面本篇文章给大家介绍一下Vue高效通过JSX动态渲染组件的方法,希望对大家有所帮助!

如何打开win10旧版组件的设置 如何打开win10旧版组件的设置 Dec 22, 2023 am 08:45 AM

win10旧版组件是需要用户自己去设置里面打开的,因为很多的组件平时都是默认关闭的状态,首先我们需要进入到设置里面,操作很简单,跟着下面的步骤来就可以了win10旧版组件在哪里打开1、点击开始,然后点击“win系统”2、点击进入控制面板3、再点击下面的程序4、点击“启用或关闭win功能”5、在这里就可以选择你要的打开了

Vue组件开发:进度条组件实现方法 Vue组件开发:进度条组件实现方法 Nov 24, 2023 am 08:56 AM

Vue组件开发:进度条组件实现方法前言:在Web开发中,进度条是一种常见的UI组件,在数据请求、文件上传、表单提交等场景中常用来显示操作的进度。在Vue.js中,通过自定义组件的方式,我们可以很方便地实现一个进度条组件,本文将介绍一种实现方法,并提供具体的代码示例。希望能对Vue.js初学者有所帮助。组件的结构和样式首先,我们需要定义进度条组件的基本结构和样

Vue组件实战:分页组件开发 Vue组件实战:分页组件开发 Nov 24, 2023 am 08:56 AM

Vue组件实战:分页组件开发介绍在Web应用程序中,分页功能是必不可少的一个组件。一个好的分页组件应该展示简洁明了,功能丰富,而且易于集成和使用。在本文中,我们将介绍如何使用Vue.js框架来开发一个高度可定制化的分页组件。我们将通过代码示例来详细说明如何使用Vue组件开发。技术栈Vue.js2.xJavaScript(ES6)HTML5和CSS3开发环

See all articles