上記記事の第3回。
Angularは1系以来なので、流石にわからないことが多く一気にやるのではなくすこし刻みながら作業を進めた。認証については下記に別途書いてます
また今回SSRはしていません。そのうちまた機会があればやるかもしれない。完成リポジトリは以下
チュートリアルのサイト
- https://medium.com/@hamedbaatour/build-a-real-world-beautiful-web-app-with-angular-6-a-to-z-ultimate-guide-2018-part-i-e121dd1d55e
- https://medium.com/@hamedbaatour/build-a-real-world-beautiful-web-app-with-angular-8-the-ultimate-guide-2019-part-ii-fe70852b2d6d
今回のチュートリアルのサイト、結構そのまま進めていってもエラーになっていることが多くて結構きつくて、途中から同じものをつくるというよりも、お天気アプリっぽい気圧のチェックアプリを作る方針に変更
背景のグラデーションは天気によって変わります
UIにはAngular Materialを採用して、ダークモードには対応しなかった。チュートリアルでは各コンポーネントにダークモードの判定を持たせていて、それによって切り分けるみたいな実装だったんだけど、さすがにそこまでやる気力まではわかなかった
写真には find47 を、気圧取得のためのAPIには OpenWeatherMap 使っています
Angular CLI
Angularで何かをするときには ng
コマンドを使う。今回わりと使ったコマンドは
-
ng g c path/foo
コンポーネントの作成 -
ng g s path/bar
サービスの作成 -
ng add package
npm packageの追加
上記の3つ。上記2つについては削除するコマンドは用意されていないので、間違って作った場合には手動で削除する必要があります
こういったものが用意されているので、今回は用意されたものをそのまま使ったんだけど、TSLintのみでPrettierが入っておらず、HTMLやCSSのフォーマッタがないのが結構つらい……
あとはデフォルトの設定だとメンバ変数に _
から始まる命名でTSLintに怒られるので、あまり初期設定を変更はしたくなかったんだけども、"variable-name": false
だけは追加しています
状態管理
NgRx は単語だけは知っていたので使おうかとも思ったんだけど、Serviceに状態を持たせることができて、それで困ることは一人でこの規模だと特に問題が起こることもなさそうなので、NgRxは使わなかった
import { Injectable } from '@angular/core';
import { environment } from './../../environments/environment';
import { HttpClient } from '@angular/common/http';
import { Weather, City } from 'src/types/weather';
@Injectable({
providedIn: 'root'
})
export class WeatherService {
private _cities: City[] = [
{ value: '2128295', viewValue: '札幌', image: 'https://find47.jp/ja/i/RhnEE/image_file' },
{ value: '1850147', viewValue: '東京', image: 'https://find47.jp/ja/i/cVikh/image_file' },
{ value: '1853908', viewValue: '大阪', image: 'https://find47.jp/ja/i/plmNy/image_file' },
{ value: '1863958', viewValue: '福岡', image: 'https://find47.jp/ja/i/xewpq/image_file' }
];
private _weather: Weather;
private _city: City;
constructor(private http: HttpClient) { }
set weather(weather: Weather) {
this._weather = weather;
}
get weather() {
return this._weather;
}
get cities() {
return this._cities;
}
set city(city: City) {
this._city = city;
}
get city() {
return this._city;
}
setCityByValue(cityValue: string) {
this._city = this._cities.find(city => city.value === cityValue);
}
fetch() {
return this.http.get(`https://api.openweathermap.org/data/2.5/weather?id=${this._city.value}&appid=${environment.openWeatherMapApi}`);
}
}
WeatherServiceでは setter/getter が単純に面倒くさくて、もうpublicでもよさそうな気がした。コンポーネント側ではHTMLで使うものについてだけsetter/getterを作りました
RxJS/Promise
このアプリでRxJSを使ったのはAPIをfetchしてくるところと、routingのところだけなので特に深く理解していなくても実装ができてしまった。
angularfire が使いやすくて、今回のアプリ程度の簡単なものであれば、ほとんど何も考えることなくいい感じの認証ができる。
HTML/CSS
HTMLが慣れてないことに加えてFormatterが特に設定していないと入ってなくてすごい辛かった
チュートリアルを本当にさらっとだけ斜め読みして書いていると、classだけはおもった動作をしなくてts側で bar = "bar"
になっているときにhtml側で以下のようにすると
<div class="foo" [class]="bar"></div>
// => <div class="bar"></div>
<div class="foo" [ngClass]="bar"></div>
// => <div class="foo bar"></div>
こうなる。一応出力はできるけど [class]
は既存のクラスを上書きしてしまう(プロパティバインディングというらしい)ので基本的に ngClass
を使ったほうが良さそう。
感想
Angular1系を触っていて、2で梯子を外された経験をしたせいか本当に嫌いだったAngularだったんだけど、実際に今のAngularを触ってみると普通に良かった。ただ敷かれているレールから外れるのが嫌で、TSのバージョンの低さとかLintがTSLintだったりPrettierがはいってなかったりとそのあたりを変えたときにバージョンアップが面倒になったりしないのかな、とかそういうのは気になった
学習コストについてはAngularだけ特別高いのかといわれると、Reactと比べて特別高いということはないような気がする。やりたいことは公式がほぼ用意してくれているので、技術選定でも考えることは少なそう。ただReactに比べてマニュアルを確認する機会は多くなりそうな気はする