Angularで引っかかったあれこれ
はじめに
- AngularとSpringBootでWebページを作ってみたい その1
- AngularとSpringBootでWebページを作ってみたい その2
- AngularとSpringBootでWebページを作ってみたい その3
相変わらず上の続きなのですが、Angular周りで細々したことに引っかかりまくったため、ここにまとめることとします。
静的なファイルの読み込み
たとえばヘッダーのロゴ画像など、静的にファイルを読み込みたい場合。
普通にHTMLで書くときと同じように、HTMLファイルと同じ階層に画像ファイルを置いても読み込むことはできません。
angular-cliを使っている場合、ファイルの置き場所は.angular-cli.jsonの中の、app > assets の中で指定されます。
.angular-cli.json
〜省略〜 "apps": [ { 〜省略〜 "assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.ts", "polyfills": "polyfills.ts", "test": "test.ts", "tsconfig": "tsconfig.app.json", "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ "styles.css" ], "scripts": [], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } } ], 〜省略〜
デフォルトでは favicon.ico と src > assets が指定されており、
例えば src > assets > img > image01.jpg となるようフォルダと画像ファイルを追加した場合、
「< img src=“assets/img/image01.jpg”>」で表示できるようになります。
動的にページタイトルを設定する
ルーティングは以前やりましたが、ページが切り替わったらタイトルも変更したいところ。
ページタイトルを切り替えるには、対象のページのクラスにDIで「Title」クラスを注入し、
「setTitle()」を使って設定します。
main-page.component.ts
import { Component, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; @Component({ selector: 'app-main-page', templateUrl: './main-page.component.html', styleUrls: ['./main-page.component.css'] }) export class MainPageComponent implements OnInit { constructor(private titleService: Title) {} ngOnInit() { this.titleService.setTitle('メインページ'); } }
IE対応
気になってはいたのです。。。
IEでちゃんと開けるのかということに。
とはいえ、下記を見ればIE9以降であればサポートはされているようなので、
諸事情によりサポート切れのブラウザで表示する必要が。。。みたいな環境でなければ問題ないはずw
ところが、IE11で開くとなにやらエラーが発生してページが正しく動作しませんorz
IEに対応するためには、プロジェクト > src にあるpolyfills.tsの下記の部分をアンコメントする必要があります。
polyfills.ts
/** IE9, IE10 and IE11 requires all of the following polyfills. **/ // import 'core-js/es6/symbol'; // import 'core-js/es6/object'; // import 'core-js/es6/function'; // import 'core-js/es6/parse-int'; // import 'core-js/es6/parse-float'; // import 'core-js/es6/number'; // import 'core-js/es6/math'; // import 'core-js/es6/string'; // import 'core-js/es6/date'; // import 'core-js/es6/array'; // import 'core-js/es6/regexp'; // import 'core-js/es6/map'; // import 'core-js/es6/weak-map'; // import 'core-js/es6/set';
ちなみにここで登場するcore-jsは、ブラウザ間の差異を吸収してくれるもののようです。
(この辺もちゃんと調べたいと思いつつ何もできてないマンですはい)
デフォルトでこれがオンになってない理由は、余計なファイルを含めたくない、ということなのでしょうか。
Componentをどう分けるか
これまで1ページの中の要素でも、ひとかたまりごとにComponentに分けて作っていました。
ただ、この辺りのお話を見ていると、そうではなく複数の箇所から共通で使う部分だけを切り分ける、
とした方が良いように思えてきました。
- Angular座談会(前編)。コンポーネントの設計はどうする? モジュール分割の方法は? ビルド遅くない? 開発者が生々しく語る現場の悩みとは。ng-japan 2017 - Publickey
- Angular座談会(後編)。コンポーネントの設計はどうする? モジュール分割の方法は? ビルド遅くない? 開発者が生々しく語る現場の悩みとは。ng-japan 2017 - Publickey
なるほど。つい細かくComponentに分けがちだったのだけど、複数の画面で使いまわしたりしないならまとめちゃって良いのね🤔
— Optional<🍅増井将則> (@masanori_msl) 2017年7月1日
確かに、画面数とか増えてきたら大変だよな〜とは思ってますた😫 ->RT
今から思えば、たしかにハンズオンも、先にまとめて作ったあと切り分けていたなぁ、と。
これもバランスだとは思いますが、今後はComponentをもう少しまとめてみようと思います。
画像切り替え
以前は画像のパスをimgタグ渡して、動的に画像を切り替える、ということをしていましたが、
これには問題がありました。
最初にページを表示した時に、画像がキャッシュされていないので画像切り替え時に一瞬前の画像が見えたりしていたのです。
大量に画像を表示するなどの場合は別ですが、今回のように決まった数を表示する場合は、
画像を全てキャッシュしておいて、それを切り替えた方が良さそうです。
banner-image.ts
export interface BannerImage { imagePath: string; transitionPath: string; bannerState: string; }
main-page.component.ts
import { Component, OnInit, HostListener } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { Title } from '@angular/platform-browser'; import { BannerImage } from '../banner-image' @Component({ selector: 'app-main-page', templateUrl: './main-page.component.html', styleUrls: ['./main-page.component.css'], animations: [ trigger('bannerState', [ state('active', style({ transform: 'scale(1.0)', filter: 'brightness(100%)', display: 'block' })), state('inactive', style({ transform: 'scale(0.6)', filter: 'brightness(20%)', display: 'none' })), transition('active => inactive', animate('400ms ease-in')), transition('inactive => active', animate('10ms ease-in')) ] )] }) export class MainPageComponent implements OnInit { private bannerImages: BannerImage[]; private activeImageNum: number; private nextImageNum: number; constructor(private titleService: Title) {} ngOnInit() { this.titleService.setTitle('メインページ'); this.bannerImages = [ {imagePath: "assets/img/banner01.jpg", transitionPath: '', bannerState: 'inactive'}, {imagePath: "assets/img/banner01.jpg", transitionPath: '', bannerState: 'inactive'}, {imagePath: "assets/img/banner01.jpg", transitionPath: '', bannerState: 'inactive'}, ]; this.activeImageNum = - 1; this.nextImageNum = 0; this.onBannerChanged(); } @HostListener('document:keyup', ['$event']) onArrowKeyUp(event: any){ if(event.keyCode === 39){ // RightArrow. this.nextImageNum = (this.activeImageNum < this.bannerImages.length - 1)? this.activeImageNum + 1 : 0; this.bannerImages[this.activeImageNum].bannerState = 'inactive'; } else if(event.keyCode === 37){ // LeftArrow this.nextImageNum = (this.activeImageNum > 0)? this.activeImageNum - 1: this.bannerImages.length - 1; this.bannerImages[this.activeImageNum].bannerState = 'inactive'; } } onBannerChanged(){ if(this.activeImageNum === this.nextImageNum){ return; } this.bannerImages[this.nextImageNum].bannerState = 'active'; this.activeImageNum = this.nextImageNum; } }
main-page.component.html
< section id="top-banner-area" > < div *ngFor='let bannerImage of bannerImages' > < a href='bannerImage.transitionPath'> < img [src]="bannerImage.imagePath" class="top-banner" [@bannerState]="bannerImage.bannerState" (@bannerState.done)="onBannerChanged()" /> < /a> < /div> < /section> < app-top-news>< /app-top-news>
参照
静的ファイルの読み込み
- angular - Whats the default path for static files in Angular2? - Stack Overflow
- angular cli - angular/angular-cli Wiki - GitHub