この記事はIonic Advent Calendar 2018 18日目の記事になります。
今年になってIonicを触り始め、技術書典5にもIonicの本を出させてもらいました。
この記事では、カスタムコンポーネントで神ゲーであるペルソナ5のフレームUIを再現します。
開発環境
-
Ionic
- Ionic CLI : 4.1.0
- Ionic Framework : ionic-angular 3.9.2
- @ionic/app-scripts : 3.2.1
-
System
- NodeJS : v10.9.0
- npm : 6.2.0
- OS : macOS High Sierra
Ionicでペルソナ5みたいな枠を使いたいッッッ!
こんなの↓
想定
想定としては、枠の中はion-card
と同様の使い方をしたいです。
ページのsassファイルを汚してしまうのは嫌なのでカスタムコンポーネント化します。
不規則なデザインかつ、大きさ(幅と高さ)も可変式にする必要があるのでsvgを使ってデザインを行います。
カスタムコンポーネント作成
$ ionic generate component p5-card
htmlテンプレート編集
<ion-card id="p5-card" (tap)="p5TapEvent()" no-margin>
<!-- 外側の黒い枠 -->
<svg class="p5-card-frame" viewBox="0,0,100,100" width=100 height=100 preserveAspectRatio="none" fill="black">
<polygon points="0,95 90,100 100,0 5,10"></polygon>
</svg>
<!-- 内側の白い枠 -->
<svg class="p5-card-frame" viewBox="0,0,100,100" width=100 height=100 preserveAspectRatio="none" fill="white">
<polygon points="5,90 89,95 95,5 7,15"></polygon>
</svg>
<!-- <p5-card>ここがng-contentとして挿入される</p5-card> -->
<ng-content></ng-content>
</ion-card>
やっていることは、<ion-card>
の幅と高さを100として計算し、svgで枠の形のボックスを挿入しているだけです。
こうすれば、ion-card
の恩恵を受けつつ枠を作ることができます。
.scss編集
p5-card {
.p5-card-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height:100%;
z-index: -1;
}
#p5-card {
position: relative;
background-color: rgba(0,0,0,0);
border: none !important;
box-shadow:none;
padding: 15px 30px 15px 20px;
text-align: center;
width: 100%;
}
}
.ts編集
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'p5-card',
templateUrl: 'p5-card.html'
})
export class P5CardComponent {
@Output() p5Tap = new EventEmitter();
constructor() {
}
p5TapEvent() {
this.p5Tap.emit();
}
}
アウトプットイベントにp5Tap
というイベントを作成しました。
このように、Outputデコレータを使えばカスタムコンポーネント独自のイベントを作ることができます。
問題発生
このままionic serve
してもエラーが出ます。
標準では、カスタムコンポーネント内ではion-*
といったIonicのコンポーネントを使うことができません。
では、使える使えるようにしましょう。
components.module.ts
を編集します。
components.module.ts編集
import { NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from 'ionic-angular';
import { P5CardComponent } from './p5-card/p5-card';
@NgModule({
declarations: [P5CardComponent,
],
imports: [CommonModule,
IonicModule],
exports: [P5CardComponent,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class ComponentsModule {}
ここで新たにimportしたのは
- P5CardComponent
- CUSTOM_ELEMENTS_SCHEMA
- CommonModule
- IonicModule
P5CardComponentは言うまでもなく、先ほど作成したカスタムコンポーネントです。
CUSTOM_ELEMENTS_SCHEMA
まずschemasと言うのは、
Angularのコンポーネントやディレクティブではない、Angular外で定義された要素とプロパティをHTMLパーサがどのように取り扱うか指定するもの
CUSTOM_ELEMENTS_SCHEMA
は、
Angularの標準コンポーネント以外をカスタムコンポーネント内で使っても認識するようにするもの
こうすることで、カスタムコンポーネント内でion-*
系のコンポーネントや自分で定義したコンポーネントを使えるようになります。
CommonModule
CommonModule
は
カスタムコンポーネント内で、Angular標準のDirectiveやPipeを使えるようにするもの
つまり、
- ngIf
- ngFor
- ngSwitch
などの構造Directiveや
- AsyncPipe
- DecimalPipe
などのPipeをカスタムコンポーネントで使えるようになります。
IonicModule
app.module.ts
で以下のように定義したデザインの条件をカスタムコンポーネントでも使えるようにします。
@NgModule({
declarations: [ MyApp ],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp, {
backButtonText: 'Go Back',
iconMode: 'ios',
modalEnter: 'modal-slide-in',
modalLeave: 'modal-slide-out',
tabsPlacement: 'bottom',
pageTransition: 'ios-transition'
}, {}
)],
bootstrap: [IonicApp],
entryComponents: [ MyApp ],
providers: []
})
IonicModuleをimportしていないと、アイコンやモーダルなど影響を受けるコンポーネントは正常に動作しなくなります。
完成
課題
paddingの値を適当に決めてしまっているので、検証してベストな値を見つけたい。
おまけ
この記事の要領でトグルとスライダーも作ってみた。