非常教程

AngularJS参考手册

组件与模板

动态组件

组件的模板不会永远是固定的。应用可能会需要在运行期间加载一些新的组件。

这本烹饪书为你展示如何使用 ComponentFactoryResolver 来动态添加组件。

到在线例子 / 下载范例查看本烹饪书的源码。

动态组件加载

下面的例子展示了如何构建动态广告条。

英雄管理局正在计划一个广告活动,要在广告条中显示一系列不同的广告。几个不同的小组可能会频繁加入新的广告组件。 再用只支持静态组件结构的模板显然是不现实的。

你需要一种新的组件加载方式,它不需要在广告条组件的模板中引用固定的组件。

Angular 自带的 API 就能支持动态加载组件。

指令

在添加组件之前,先要定义一个锚点来告诉 Angular 要把组件插入到什么地方。

广告条使用一个名叫 AdDirective 的辅助指令来在模板中标记出有效的插入点。

src/app/ad.directive.ts

content_copyimport { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[ad-host]',
})
export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

AdDirective 注入了 ViewContainerRef 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。

@Directive 装饰器中,要注意选择器的名称:ad-host,它就是你将应用到元素上的指令。下一节会展示该如何做。

加载组件

广告条的大部分实现代码都在 ad-banner.component.ts 中。 为了让这个例子简单点,HTML 被直接放在了 @Component 装饰器的 template 属性中。

<ng-template> 元素就是刚才制作的指令将应用到的地方。 要应用 AdDirective,回忆一下来自 ad.directive.ts 的选择器 ad-host。把它应用到 <ng-template>(不用带方括号)。 这下,Angular 就知道该把组件动态加载到哪里了。

src/app/ad-banner.component.ts (template)

content_copytemplate: `
            <div class="ad-banner">
              <h3>Advertisements</h3>
              <ng-template ad-host></ng-template>
            </div>
          `

<ng-template> 元素是动态加载组件的最佳选择,因为它不会渲染任何额外的输出。

解析组件

深入看看 ad-banner.component.ts 中的方法。

AdBannerComponent 接收一个 AdItem 对象的数组作为输入,它最终来自 AdServiceAdItem 对象指定要加载的组件类,以及绑定到该组件上的任意数据。 AdService 可以返回广告活动中的那些广告。

AdBannerComponent 传入一个组件数组可以在模板中放入一个广告的动态列表,而不用写死在模板中。

通过 getAds() 方法,AdBannerComponent 可以循环遍历 AdItems 的数组,并且每三秒调用一次 loadComponent() 来加载新组件。

src/app/ad-banner.component.ts (excerpt)

content_copyexport class AdBannerComponent implements OnInit, OnDestroy {
  @Input() ads: AdItem[];
  currentAdIndex = -1;
  @ViewChild(AdDirective) adHost: AdDirective;
  interval: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    this.loadComponent();
    this.getAds();
  }

  ngOnDestroy() {
    clearInterval(this.interval);
  }

  loadComponent() {
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    let adItem = this.ads[this.currentAdIndex];

    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);

    let viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();

    let componentRef = viewContainerRef.createComponent(componentFactory);
    (<AdComponent>componentRef.instance).data = adItem.data;
  }

  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }
}

这里的 loadComponent() 方法很重要。 来一步步看看。首先,它选取了一个广告。

loadComponent() 如何选择广告

loadComponent() 方法使用某种算法选择了一个广告。

(译注:循环选取算法)首先,它把 currentAdIndex 递增一,然后用它除以 AdItem 数组长度的余数作为新的 currentAdIndex的值, 最后用这个值来从数组中选取一个 adItem

loadComponent() 选取了一个广告之后,它使用 ComponentFactoryResolver 来为每个具体的组件解析出一个 ComponentFactory。 然后 ComponentFactory 会为每一个组件创建一个实例。

接下来,你要把 viewContainerRef 指向这个组件的现有实例。但你怎么才能找到这个实例呢? 很简单,因为它指向了 adHost,而这个 adHost 就是你以前设置过的指令,用来告诉 Angular 该把动态组件插入到什么位置。

回忆一下,AdDirective 曾在它的构造函数中注入了一个 ViewContainerRef。 因此这个指令可以访问到这个你打算用作动态组件宿主的元素。

要把这个组件添加到模板中,你可以调用 ViewContainerRefcreateComponent()

createComponent() 方法返回一个引用,指向这个刚刚加载的组件。 使用这个引用就可以与该组件进行交互,比如设置它的属性或调用它的方法。

对选择器的引用

通常,Angular 编译器会为模板中所引用的每个组件都生成一个 ComponentFactory 类。 但是,对于动态加载的组件,模板中不会出现对它们的选择器的引用。

要想确保编译器照常生成工厂类,就要把这些动态加载的组件添加到 NgModuleentryComponents 数组中:

src/app/app.module.ts (entry components)

content_copyentryComponents: [ HeroJobAdComponent, HeroProfileComponent ],

公共的 AdComponent 接口

在广告条中,所有组件都实现了一个公共接口 AdComponent,它定义了一个标准化的 API,来把数据传给组件。

下面就是两个范例组件及其 AdComponent 接口:

hero-job-ad.component.ts

hero-profile.component.ts

ad.component.ts

content_copyimport { Component, Input } from '@angular/core'; import { AdComponent }      from './ad.component'; @Component({  template: `    <div class="job-ad">      <h4>{{data.headline}}</h4>       {{data.body}}    </div>  `})export class HeroJobAdComponent implements AdComponent {  @Input() data: any; }

最终的广告栏

最终的广告栏是这样的:

动态组件

参见在线例子 / 下载范例。

AngularJS

Angular 是一个开发平台。它能帮你更轻松的构建 Web 应用。Angular 集声明式模板、依赖注入、端到端工具和一些最佳实践于一身,为你解决开发方面的各种挑战。

AngularJS目录

1.快速上手 | quick start
2.语言服务
3.安全
4.环境准备与部署
5.Service Worker
6.保持最新
7.从 AngularJS 升级
8.服务端渲染
9.Visual Studio 2015 快速上手
10.风格指南
11.国际化
12.测试
13.路由与导航
14. 教程 | Tutorial
15.架构
16.组件与模板
17.表单
18.可观察对象与RxJS
19.引导启动
20.Angular 模块
21.依赖注入
22.HttpClient
23.词汇表
24.AngularJS 应用
25.AngularJS 模块
26.AngularJS 事件
27.AngularJS HTML DOM
28.AngularJS 过滤器
29.AngularJS 控制器
30.AngularJS 指令
31.AngularJS 表达式
32.AngularJS 简介
33.AngularJS 参考手册
34.AngularJS 实例
35.AngularJS 输入验证
36.AngularJS 表单
37.AngularJS SQL
38.AngularJS 表格
39.AngularJS Http
40.AngularJS 包含
41.AngularJS Bootstrap
42.AngularJS API
43.AngularJS ng-checked 指令
44.AngularJS ng-change 指令
45.AngularJS ng-blur 指令
46.AngularJS ng-bind-template 指令
47.AngularJS ng-bind-html 指令
48.AngularJS ng-bind 指令
49.AngularJS ng-app 指令
50.AngularJS Scope(作用域)
51.AngularJS ng-model 指令
52.AngularJS ng-dblclick 指令
53.AngularJS ng-cut 指令
54.AngularJS ng-csp 指令
55.AngularJS ng-copy 指令
56.AngularJS ng-controller 指令
57.AngularJS ng-cloak 指令
58.AngularJS ng-click 指令
59.AngularJS ng-class-odd 指令
60.AngularJS ng-class-even 指令
61.AngularJS ng-class 指令
62.AngularJS ng-keyup 指令
63.AngularJS ng-keypress 指令
64.AngularJS ng-keydown 指令
65.AngularJS ng-init 指令
66.AngularJS ng-include 指令
67.AngularJS ng-if 指令
68.AngularJS ng-href 指令
69.AngularJS ng-hide 指令
70.AngularJS ng-focus 指令
71.AngularJS ng-disabled 指令
72.AngularJS ng-non-bindable 指令
73.AngularJS ng-mouseup 指令
74.AngularJS ng-mouseover 指令
75.AngularJS ng-mousemove 指令
76.AngularJS ng-mouseleave 指令
77.AngularJS ng-mouseenter 指令
78.AngularJS ng-mousedown 指令
79.AngularJS ng-model-options 指令
80.AngularJS ng-model 指令
81.AngularJS ng-list 指令
82.AngularJS ng-style 指令
83.AngularJS ng-srcset 指令
84.AngularJS ng-src 指令
85.AngularJS ng-show 指令
86.AngularJS ng-selected 指令
87.AngularJS ng-repeat 指令
88.AngularJS ng-readonly 指令
89.AngularJS ng-paste 指令
90.AngularJS ng-options 指令
91.AngularJS ng-open 指令
92.AngularJS ng-value 指令
93.AngularJS ng-switch 指令
94.AngularJS ng-submit 指令
95.AngularJS 服务(Service)
96.AngularJS Select(选择框)
97.AngularJS 动画
98.AngularJS 依赖注入