AngularとSpringBootでWebページを作ってみたい その2
はじめに
引き続きWebページを作成すべくあれこれ試しています。
今回は、Angularでページを作っていく上で必要になった知識?も合わせてまとめることにします。
node_moduleをインストールし直す
1台のマシンだけで開発している場合は問題がないのですが、
他のマシンにプロジェクトを移したい場合、node_moduleは持っていってもエラーになってしまいます。
(.gitignoreに含まれてもいます)
そのため、プロジェクトを移したあとで「npm install」を行い、node_moduleをインストールし直す必要があります。
インストールされる内容は、プロジェクト直下にあるpackage.jsonのものです。
なお、この中のアプリのバージョン(下記参照)を更新したい場合は、手動で書き直すのでしょうか…?
package.json
{ "name": "anime-crud-sample", "version": "0.0.0", "license": "MIT", 〜省略〜
ビルドデータの出力先の変更
以前書きましたが、
Angularで作成したページを実際サーバー上で表示するときは、「ng build」でビルドしたデータを使うことになります。
通常ではプロジェクト直下に作成される「dist」というフォルダの中にビルドしたデータが出力されるのですが、
これを任意の場所に出力するためには、プロジェクト直下にある.angular-cli.jsonを変更します。
.angular-cli.json
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "project": { "name": "anime-crud-sample" }, "apps": [ { "root": "src", "outDir": "ここを変更する" 〜省略〜
CSS Grid Layoutを使う
以前は下記を再現するには、tableを使用して結合などを利用していました。
しかし、CSS Grid Layoutを使うことでより簡単に再現できるようになります。
さっそく試してみます。
元のHTML
< div class='top-news-item-area'> < div class='top-news-item-date'>2017年6月20日< /div> < div class='top-news-item-title'>ニュースだよ< /div> < div class='top-news-item-article'>中身です< /div> < /div>
top-news-item-area(クラスを持つdiv)の中に、3つのdivが含まれています。
このHTMLに対して設定していきます。
top-news-item-area
まず、このLayoutが何行何列なのか、またそれぞれの高さ・幅を、
3つのdivの親であるtop-news-item-areaに設定します。
.top-news-item-area{ display: grid; /* Grid layoutを有効にする */ grid-template-rows: 50px 100px; /* 左から順に1行目、2行目の高さを指定 */ grid-template-columns: 20% 1fr; /* 左から順に1列目、2列目の幅を指定 */ }
- 今回は2行2列のため、grid-template-rowsとgrid-template-columnsにはそれぞれ2つずつ値を設定しています。
行・列数を増やす場合は更に値を追加していきます。 - grid-template-columnsで指定している「1fr」は、1列目で指定した幅の残りを2列目に全て割り当てるために使用しています。
top-news-item-date・top-news-item-title・top-news-item-article
Layoutの行・列数と高さ・幅が決まれば、あとは子となるdivの配置を決めるだけです。
.top-news-item-date{ grid-row: 1 / 3; /* 1行目から3行目まで(つまり2行全て)を指定する */ grid-column: 1 / 2; /* 1列目から2列目まで(つまり1列目)を指定する */ border: 1px solid #000; } .top-news-item-title{ grid-row: 1; /* 1行目を指定する */ grid-column: 2; /* 2列目を指定する */ border: 1px solid #000; } .top-news-item-article{ grid-row: 2 / span 1; /* 2行目から1行分(つまり2行目のみ)を指定する */ grid-column: 2; /* 2列目を指定する */ border: 1px solid #000; }
- top-news-item-titleのように1行または1列だけを指定する場合は、「/」以降の数字を省略できます。
- 「/」以降を省略した場合、top-news-item-articleのgrid-rowと同じように、指定の行(または列)から1行(列)分が設定されます。
結果
ここまでのものを表示すると、下記のようになります。
あとは角丸にしたり、marginなどで幅を調整したりすればOKです。
課題
Chrome、Firefoxだけであれば問題なく表示できるのですが、
少なくともAngularでCSS Grid Layoutを使うと、警告出る場合があります。
IEでは、「grid-row: 1 / 3;」のように「grid-row(またはgrid-column)」で「/」以降の数字を指定できないためです。
手元にIEがないため未確認ですが、下記のようにすればIEにも対応できるようです。
.top-news-item-area{ display: grid; display: -ms-grid; grid-template-rows: 50px 100px; grid-template-columns: 20% 1fr; -ms-grid-rows: 50px 100px; -ms-grid-columns: 150px 1fr; } .top-news-item-date{ -ms-grid-row: 1; -ms-grid-row-span: 2; -ms-grid-column: 1; -ms-grid-column-span: 1; grid-row: 1 / 3; grid-column: 1 / 2; border: 1px solid #000; } .top-news-item-title{ -ms-grid-row: 1; -ms-grid-column: 2; grid-row: 1; grid-column: 2; border: 1px solid #000; } .top-news-item-article{ -ms-grid-row: 2; -ms-grid-column: 2; grid-row: 2; grid-column: 2; border: 1px solid #000; }
ただし警告は消えません。。。
まぁこれに関する警告をでないようにすれば(たぶんtslint.json)良いのでしょうが、
ちょっと気持ち悪いですね。。。
RxJSでJSONデータ取得
さて、CSS Grid Layoutで作ったフォームに、データを入れてみます。
Spring bootでGETリクエストをした時にJSONを返すようにしておき、
RxJSを使ってそれを受け取ってHTMLに渡す、という処理を行います。
Spring boot
今回は一旦GETリクエストがあった場合に、JSONデータを固定値で生成して返す、という処理をすることにします。
ということで、「spring-boot-starter-web」を含めてSpring bootのプロジェクトを作成し、下記のクラスを追加します。
News.java
public class News { public int id; public String createdDate; public String title; public String article; }
- JSONデータを返すための値を保持するクラスです。
CrudSampleController.java
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController public class CrudSampleController { @GetMapping("/topnewslist") public ListnewsList(){ List newsItems = new ArrayList<>(); News newsItem1 = new News(); newsItem1.id = 0; newsItem1.createdDate = "2017.06.22"; newsItem1.title = "うどん一杯目"; newsItem1.article = "きつねうどん"; newsItems.add(newsItem1); News newsItem2 = new News(); newsItem2.id = 1; newsItem2.createdDate = "2017.06.23"; newsItem2.title = "うどん二杯目"; newsItem2.article = "味噌煮込みうどん"; newsItems.add(newsItem2); return newsItems; } }
- Controllerクラスです。
- localhost:8080/topnewslistにGETリクエストがあった場合にnewsList()が呼び出され、
Newsクラスのリストを返します。
(リストやクラスを戻り値にすると、自動でJSONとして受け取ることができるの便利ですね)
RxJS
フロント側に戻ります。
AngularのプロジェクトにはデフォルトでRxJSが含まれているため、
インストールなどは特にしなくてもそのまま使用できます。
GETリクエストを送るためには、app.module.tsにHTTPModuleを追加する必要があります。
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; import {HttpModule} from '@angular/http'; import { AppComponent } from './app.component'; import { SlideAnimeComponent } from './slide-anime/slide-anime.component'; import { FormsModule } from '@angular/forms'; import { GlobalHeaderComponent } from './global-header/global-header.component'; import { ContactPageComponent } from './contact-page/contact-page.component'; import { routing } from './app.routing'; import { MainPageComponent } from './main-page/main-page.component'; import { TopBannerComponent } from './top-banner/top-banner.component'; import { TopNewsComponent } from './top-news/top-news.component'; import { MenulistPageComponent } from './menulist-page/menulist-page.component'; @NgModule({ declarations: [ AppComponent, SlideAnimeComponent, GlobalHeaderComponent, ContactPageComponent, MainPageComponent, TopBannerComponent, TopNewsComponent, MenulistPageComponent ], imports: [ BrowserModule, BrowserAnimationsModule, FormsModule, HttpModule, routing ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
あとはHTTPとRxJSを使ってJSONデータを取得します。
top-news.ts
export interface TopNews { id: number; createdDate: string; title: string; article: string; }
- サーバー側から受け取ったJSONデータを格納するためのインターフェースです。
top-news.component.ts
import { Component, OnInit } from '@angular/core'; import { TopNews } from '../top-news'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; @Component({ selector: 'app-top-news', templateUrl: './top-news.component.html', styleUrls: ['./top-news.component.css'] }) export class TopNewsComponent implements OnInit { newsList: TopNews[]; constructor(private http_: Http) { http_.get("/topnewslist") .map(response => response.json()) /* response.json()の「()」を忘れないこと */ .subscribe(gotNews => this.newsList = gotNews); } ngOnInit() { } }
- 今回は初回ロード時のみデータを読み込むため、constructorで、DIでインスタンスを受け取ったらそのまま処理を行っています。
- constructor以外でも「this.http_」でアクセスできるため(なんだか不思議な感じがしますが)、任意のタイミングで実行することも可能です。
- 「map(response => response.json())」で、response.json()の「()」がついていなくてもエラーにはならないのですが、
戻り値が変わってしまい、subscribeの時に値が取り出せなくなってしまいます。
注意点
上記コードをng serveを使い、ビルドしない状態で実行するとエラーが発生します。
それは、同一ドメイン以外のURLにアクセスしに行く場合に必要なXMLHttpRequestの設定が抜けており、
ブラウザによってアクセスを禁止されるためです。
ただ今回は、最終的には同じドメインでやり取りをすることになるため、
ng buildでビルドを行い、Spring bootのプロジェクトに組み込んだ状態で確認を行うことにします。
おわりに
今回はほとんど触れられませんでしたが、RxJSももう少し突っ込んで調べてみたいと思います。
参照
Angular
CSS Grid Layout
- CSS グリッドレイアウト - CSS - MDN
- グリッド レイアウト (Windows) - msdn.aspx)
- CSS Grid Layout Module Level 1
- CSS Grid Layout を極める!(基礎編) - Qiita