ゲスト著者: Aayush Arora
Angular Elements は、カスタム要素としてパッケージ化された Angular コンポーネントです。現在、Chrome、Opera、Safari でサポートされており、それ以外のブラウザでもポリフィルを通して利用することができます。Angular Elements を登録すると、Angular の一般的なインターフェースと変更検知戦略を用いて、すべての Angular のインフラストラクチャをブラウザで使用できるようになります。
この Codelab では、独自の画像スライダー Angular コンポーネントを作成し、このコンポーネントを Angular 要素に変換して、Angular フレームワークから独立して動作させる方法を学びます。
演習内容
この Codelab では、Angular を使用して、次のような画像スライダー要素を作成します。
|
学習内容
- 画像スライダー カスタム コンポーネントを作成する方法
- 画像スライダー カスタム コンポーネントをカスタム要素に変換する方法
- ブラウザで動作するようにコンポーネントをパッケージ化する方法
必要なもの
- 新しいバージョンの angular-cli
- サンプルコード
- テキスト エディタ
- Angular コンポーネントの基本的な知識
この Codelab では、主に Angular 要素について説明します。関連のない概念やコードブロックについては詳しく触れず、コードはコピーして貼るだけの状態で提供されています。
コードをダウンロードする
次のリンクをクリックして、この Codelab のコードをすべてダウンロードします。
ダウンロードした zip ファイルを解凍すると、ルートフォルダ (angular-element-codelab-master)
が展開されます。このフォルダには、
(image-slider)
と (image-slider-finished)
の 2 つのフォルダがあります。すべてのコーディング作業は、「image-slider」という名前のディレクトリで行います。
プロジェクトを実行する
プロジェクトを実行するには、ルート ディレクトリ(image-slider)でコマンド(ng-serve)を実行する必要があります。
アプリがブートストラップされ、次のように表示されます。
画像スライダーの作成方法
ここでは、Angular クリック バインディングでボタンをバインドして画像スライダーを作成します。画像、alt タグ、リンクなどを含むオブジェクトの配列を作成し、これらの画像をコンテナ内に上から順に配置して、クリック時にコンテナを変換します。
まず最初に画像スライダー コンポーネントを作成し、それから Angular 要素に変換します。
- 画像とタイトルのコンテナ
- データを含む配列
- データをバインドするテンプレート
プロジェクトの作業を開始する方法はいくつかありますが、今回はプロジェクトをできるだけシンプルにして、Angular 要素に集中できるように、基本的なコードと CSS は作成済みの状態から始めます。
配列とデータサービスを作成する
sliderArray は以下のアイテムで構成します。
- スライダー画像の URL の 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
からオブジェクトを取得するには、コンポーネント内にサービスをインポートして、Observable にサブスクライブする必要があります。
これには、次の 3 つの手順を行います。
- コンポーネント配列を初期化する
getData()
関数から返された Observable にサブスクライブする- Observable にサブスクライブした後にデータの型をチェックするインターフェース Result を作成する
- データをコンポーネント配列に割り当てる
コンポーネント配列を初期化する
slider.component.ts
内でオブジェクトの配列であるコンポーネント配列を宣言し、初期化します。
宣言するには:
sliderArray: object[];
初期化するには:
constructor(private data: DataService) {
this.sliderArray = [];
}
次に、コンストラクタ内にサービスをインポートして初期化する必要があります。
constructor(private data: DataService) {}
これで、サービスを使用して、サービス メソッドを呼び出す準備が整いました。
データサービスからデータを取得する
サービスからデータを取得するには、getData()
メソッドを呼び出して、返される Observable にサブスクライブし、インターフェース Result,
を作成します。これにより、データの型をチェックして、正しいデータを取得しているかどうかを確認できます。
これは ngOnInit
メソッド内で行います。
this.data.getData().subscribe((result: Result)=>{
})
コンポーネント配列にデータを割り当てる
最後に、コンポーネント配列にデータを割り当てます。
this.data.getData().subscribe((result: Result)=>{
this.sliderArray = result.sliderArray;
})
コンポーネントの配列内でデータを取得したら、テンプレートにこのデータをバインドできます。
slider.component.html,
には、HTML テンプレートがすでに用意されています。次の手順では、このテンプレートに sliderArray
をバインドします。
*ngFor
ディレクティブを使用してテンプレートにデータをバインドし、最後にテンプレートに変換を追加して、スライダーが動作するようにします。
ここでは、次の 3 つの手順を行います。
- テンプレートに
sliderArray
をバインドする - スライダー ボタンのイベント バインディングを追加する
ngStyle
とngClass
を使用して CSS 変換を追加する
コンポーネントに slideArray をバインドする
img-container
、text-container
、slider.
のコンテナがあります。
*ngFor
ディレクティブを使用して、3 つすべてのコンテナでデータをバインドします。
<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% に変換され、4 つの異なる状態になります。
ngStyle と ngClass を使用して CSS 変換を追加する
最初にすべての画像を不透明度 0 に設定します。選択したインデックスが画像インデックスと等しい場合は、ngClass directive
を使用してクラス selected
を追加します。この selected
クラスは、不透明度 1 を画像に追加して、ユーザーに画像が表示されるようにします。
<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>
ここでは、次の 5 つの手順を行います。
- 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
],
entryComponents: [SliderComponent],
})
createCustomElement モジュールをインポートして使用する
ここでは、@angular/elements.
の createCustomElement
モジュールを使用します。createCustomElement
関数のパラメータには SliderComponent,
を使用する必要があります。その後、slider
を DOM に登録します。
import { createCustomElement } from '@angular/elements';
export class AppModule {
constructor(private injector: Injector) {
const slider = createCustomElement(SliderComponent, { injector });
}
}
スライダーを 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 を実行して、ビルドコマンドを使用して生成した dist フォルダを配信します。ng package
コマンドで取得した gzip
を展開して、npm module
として公開することもできます。