关于此 Codelab
1. 简介
客座作者:Aayush Arora
Angular 元素是打包为自定义元素的 Angular 组件。其目前受 Chrome、Opera 和 Safari 支持,而在其他浏览器中则可通过 polyfill 使用。这些元素借助通用 Angular 接口和变更检测策略利用整个 Angular 基础架构。注册后,这些元素可在浏览器内使用。
此 Codelab 将引导您创建自己的 Angular 图像滑块组件,然后帮助您将其转换为 Angular 元素,使其在 Angular 框架之外也可正常运行。
构建内容
在此 Codelab 中,您将使用 Angular 构建一个图像滑块元素。该元素将:
|
学习内容
- 如何构建图像滑块自定义组件
- 如何将图像滑块自定义组件转换为自定义元素
- 如何打包组件以使其在浏览器内正常运行
所需条件
- 最新版本的 angular-cli
- 示例代码
- 一个文本编辑器
- 掌握 Angular 组件的基础知识
此 Codelab 重点介绍 Angular 元素。对不相关的概念和代码块仅做简略介绍,并直接提供代码块供您复制粘贴后使用。
3. 如何构建图像滑块自定义组件?
如何创建图像滑块?
就此图像滑块而言,我们将使用 Angular 点击绑定来绑定按钮。我们将创建一个对象数组,其中包含图像、alt 标记、链接等。然后,我们要将这些图像叠放在一个容器中,并在点击事件发生时转换该容器。
我们将创建一个图像滑块组件,然后将其转换为 Angular 元素。
- 用于存储图像和标题的容器
- 一个包含数据的数组
- 用于绑定数据的模板
4. 实现图像滑块组件
启动任何项目都可以采用多种方式,在本例中,为了使项目尽可能简单而让您专注于 Angular 元素,我们为您提供了基本代码以及 css。
创建数组和数据服务
请注意,sliderArray 将包含:
- 一个 img 键,用于代表滑块中的图像网址
- 一个 alt 标记,用于提供图像的替代信息
- 一段文本,用于提供有关图像的说明
src/assets
目录中已有的 data.json
文件应与以下示例类似。
sliderArray = [
{img: 'http://bloquo.cc/img/works/1.jpg', alt: '', text: '365 Days Of weddings a year'},
{img: 'http://bloquo.cc/img/works/2.jpg', alt: '', text: '365 Days Of weddings a year'},
{img: 'http://bloquo.cc/img/works/3.jpg', alt: '', text: '365 Days Of weddings a year'},
{img: 'http://bloquo.cc/img/works/4.jpg', alt: '', text: '365 Days Of weddings a year'},
{img: 'http://bloquo.cc/img/works/5.jpg', alt: '', text: '365 Days Of weddings a year'}
];
我们需要使用一项服务来提取组件中的此数据。在文件 data.service.ts
中,我们将使用 @angular/common/http
中的 httpClient
模块编写 getData()
方法,用于从我们在上面创建的数组中提取数据。
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
const URL = '../assets/data.json';
@Injectable({
providedIn: 'root'
})
export class DataService {
constructor(private http: HttpClient) {}
getData() {
return this.http.get(URL)
}
}
从数据服务中提取数据
我们需要在组件内导入服务,然后就可以订阅可观察对象以从 data.json
获取对象
为此,我们需要执行以下三个步骤:
- 初始化组件数组
- 订阅由
getData()
函数返回的可观察对象 - 创建接口 Result,以在订阅可观察对象后对数据进行类型检查。
- 将数据分配到组件数组。
初始化组件数组
我们将在对象数组 slider.component.ts
内声明并初始化组件数组:
为了进行声明,请编写以下代码:
sliderArray: object[];
为了进行初始化,请编写以下代码:
constructor(private data: DataService) {
this.sliderArray = [];
}
接下来,我们需要在构造函数内导入并初始化服务
constructor(private data: DataService) {}
现在,我们就可以使用服务并调用服务方法了。
从数据服务中获取数据
为了从服务中获取数据,我们将调用 getData()
方法并订阅其返回的可观察对象。此外,我们还将创建一个接口 Result,
,以便进行类型检查来确保获取的数据正确。
我们将在 ngOnInit
方法内执行此操作:
this.data.getData().subscribe((result: Result)=>{
})
将数据分配到组件数组
最后,我们要将数据分配到组件数组:
this.data.getData().subscribe((result: Result)=>{
this.sliderArray = result.sliderArray;
})
我们获取组件数组内的数据后,就可以将模板与此数据绑定了。
slider.component.html,
中已有一个 HTML 模板。下一步是将此模板与 sliderArray
绑定。
5. 将数据与模板绑定
我们要使用 *ngFor
指令将数据与模板绑定,最后在模板中添加转换以使滑块正常运行。
此过程包含三个步骤:
- 将
sliderArray
绑定到模板 - 为滑块按钮添加事件绑定
- 使用
ngStyle
和ngClass
添加 css 转换
将 slideArray 绑定到组件
我们已经有一个包含 img-container
、text-container
和 slider.
的容器。
我们将使用 *ngFor
指令绑定全部三个容器中的数据
<div class="container">
<div class="img-container" *ngFor="let i of sliderArray; let select = index;">
<img src="{{i.img}}" alt="{{i.alt}}" >
</div>
<div>
<div class="text-container">
<div class="page-text" *ngFor="let i of sliderArray;let select = index;">
<h3>{{i.text}}</h3>
</div>
</div>
</div>
</div>
<div class="slider">
<div class="slide-button-parent-container" *ngFor="let i of sliderArray; let x =index">
<div class="select-box">
<div class="slide-button">
</div>
</div>
</div>
</div>
将事件绑定到 slideArray
绑定数据后,我们将使用 Angular click binding
将点击事件绑定到每个滑动按钮。我们将创建一个名为 selected(x)
的函数,其中 x 是数组的索引。
selected(x) {
this.downSelected(x);
this.selectedIndex = x;
}
downSelected(i) {
this.transform = 100 - (i) * 50;
this.selectedIndex = this.selectedIndex + 1;
if(this.selectedIndex > 4) {
this.selectedIndex = 0;
}
}
此处需要注意的要点如下:
- downselected 函数会按照
selected
函数被点击时传递的索引的 50 倍减小转换属性的值。 - 此逻辑会将文本框转换为 100%、50%、-50%、-100%,从而造成四种不同状态。
使用 ngStyle 和 ngClass 添加 CSS 转换
最初,我们将所有图像的不透明度设置为“零”,当所选索引等于图像索引时,我们使用 ngClass directive
添加一个 selected
类。此 selected
类会为图像添加不透明度“一”,使图像对用户可见。
<div class="img-container" *ngFor="let i of sliderArray; let select = index;"
[ngClass]="{'selected': select == selectedIndex}">
</div>
然后,我们将根据使用 select()
函数计算的 transform
值转换文本框。
<div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
</div>
执行完上述所有步骤后,您会发现如下所示的最终代码:
<div class="container">
<div class="img-container" *ngFor="let i of sliderArray; let select = index;"
[ngClass]="{'selected': select == selectedIndex}">
<img src="{{i.img}}" alt="{{i.alt}}" >
</div>
<!--</div>-->
<div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
<div class="text-container">
<div class="page-text" *ngFor="let i of sliderArray;let select = index;" [ngClass]="{'selected': select == selectedIndex}">
<h3>{{i.text}}</h3>
</div>
</div>
</div>
</div>
<div class="slider">
<div class="slide-button-parent-container" *ngFor="let i of sliderArray; let x =index" (click)="selected(x)" >
<div class="select-box">
<div class="slide-button" [ngClass]="{'slide-button-select': x == selectedIndex}" >
</div>
</div>
</div>
</div>
6. 将组件转换为 Angular 元素
此过程包括五个步骤:
- 对 Angular 元素使用
Shadow DOM
- 使用
entryComponents
- 从
@angular/elements
导入并使用CreateCustomElement
模块 - 定义
custom-element
- 运行
ngDoBootstrap
方法
对 Angular 元素使用 shadow DOM
现在,我们的图像滑块已可正常运行,只需使其成为 Angular Element
即可。
有意思的是,只需进行一点较小的更改就能使组件 DOM 成为 shadow DOM。
我们需要导入 ViewEncapsulation
模块,并且必须在该模块中使用 ShadowDom
方法。
@Component({
selector: 'app-slider',
templateUrl: './slider.component.html',
styleUrls: ['./slider.component.css'],
encapsulation: ViewEncapsulation.ShadowDom
})
使用 entryComponents
入口组件是 Angular 以命令方式加载的一个组件。您可以通过在 NgModule 中引导入口组件来指定该组件。
在此处,我们将在 @NgModule
内的 entryComponents
数组中指定 SliderComponent
@NgModule({
declarations: [
SliderComponent
],
imports: [
BrowserModule,
HttpClientModule
]
})
导入并使用 createCustomElement 模块
在此处,我们需要使用 @angular/elements.
中的 createCustomElement
模块。您需要将 SliderComponent,
用作 createCustomElement
函数的参数。然后,我们需要在 DOM 中注册 slider
。
import { createCustomElement } from '@angular/elements';
export class AppModule {
constructor(private injector: Injector) {
const slider = createCustomElement(SliderComponent, { injector });
}
}
为了将 slider 注册为 DOM 元素,我们将使用 customElements.define
方法对其进行定义。
customElements.define('motley-slider', slider);
最后,我们必须使用 ngDoBootstrap()
方法引导此自定义元素。完整的代码如下所示:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { SliderComponent } from './slider/slider.component';
import { HttpClientModule} from "@angular/common/http";
@NgModule({
declarations: [
SliderComponent
],
imports: [
BrowserModule,
HttpClientModule
]
})
export class AppModule {
constructor(private injector: Injector) {
const slider = createCustomElement(SliderComponent, { injector });
customElements.define('motley-slider', slider);
}
ngDoBootstrap() {}
}
打包 Angular 元素
我们需要使用新命令修改 package.json
,需要修改的是 package.json
文件内的脚本对象。
让我们检查一下修改后的脚本对象:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod --output-hashing=none",
"package": "cat dist/my-app/{runtime,polyfills,scripts,main}.js | gzip > elements.js.gz",
"serve": "http-server",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}
现在,我们可以运行 ng build & ng package
命令,最后再运行 ng serve 以提供使用 build 命令生成的 dist/ 文件夹。此外,我们还可以使用通过 ng package
命令获取的 gzip
,将其解压缩,并可将其发布为 npm module
。