6月メモ・リンク集
6月に調べたことのメモです。
Angular 関連
- (WIP) Angular Testing メモ - ryotah’s blog
- Angular のドキュメント + テストに関する前提知識(用語、Jasmine使い方など)をまとめようとしたもの
ngrx 関連
ngrx 実装メモ
store をつくるとき、どのような順で整理して実装しているか、についてのメモです。
例えば「ID順で表示される顧客一覧画面(テキストによる検索機能と追加読み込み機能)」をつくる場合は以下のように整理します。
- その機能 (Feature)で「できること」のリストアップ
- 顧客一覧表示
- 顧客検索
- 一覧を追加読み込み
- 検索条件を編集
- 別画面(別 Feature)に移動
- 必要な情報は何か
- 取得した顧客情報
- 顧客一覧のローディング状況
- 現在のページ番号
- 検索条件
- 各 state を作成
customer-list.reducre.ts
customerList: Customer[];
loading: boolean
page: number
query: string
- action になりそうなものをリストアップ
- 検索開始(検索条件が空の場合はID順で取得)
- 検索成功
- 検索失敗
- 追加読み込み開始
- 追加読み込み成功
- 追加読み込み失敗
- 検索条件を更新
- ページを離れる
- action と reducer の関係を考える
- 検索開始
- loading = true
- (その他のデータは一旦リセット)
- 検索成功
- customerList = 検索結果
- loading = false
- page = 1
- 検索失敗
- loading = false
- 追加読み込み開始
- loading = true
- 追加読み込み成功
- customerList += 検索結果
- loading = false
- page += 1
- 追加読み込み失敗
- loading = false
- ページを離れる
- データをリセット
- 検索開始
- 無駄が多そうな場合調整する
今回は「検索」と「追加読み込み」がほぼ同じなので共通化できそう- 検索開始 => effects で 「データリセット」+ 「追加読み込み開始」にスプリットする
- 参考:
結果
Actions
// store/actions/customer-lits.actions.ts export enum CustomerListActionTypes { LoadFirstList = '[CustomerList] Load First List', LoadList = '[CustomerList] Load List', LoadListFail = '[CustomerList] Load List Fail', LoadListSuccess = '[CustomerList] Load List Success', ResetList = '[CustomerList] Reset List', UpdateQueryFromParamMap = '[CustomerList] Update Query From Param Map', } export class LoadFirstList implements Action { readonly type = CustomerListActionTypes.LoadFirstList; } export class LoadList implements Action { readonly type = CustomerListActionTypes.LoadList; } export class LoadListFail implements Action { readonly type = CustomerListActionTypes.LoadListFail; } export class LoadListSuccess implements Action { readonly type = CustomerListActionTypes.LoadListSuccess; constructor(public payload: Customer[]) {} } export class ResetList implements Action { readonly type = CustomerListActionTypes.ResetList; } export class UpdateQueryFromParamMap implements Action { readonly type = CustomerListActionTypes.UpdateQueryFromParamMap; constructor(public payload: ParamMap) {} } export type CustomerListActions = | LoadFirstList | LoadList | LoadListFail | LoadListSuccess | ResetList | UpdateQueryFromParamMap;
Reducers
// store/reducers/customer-lits.reducer.ts export interface State { customerList: Customer[]; loading: boolean; page: number; query: string; } const initialState: State = { customerList: [], loading: false, page: 0, query: '', }; export function reducer( state = initialState, action: CustomerListActions ): State { switch (action.type) { case CustomerListActionTypes.LoadList: return { ...state, loading: true }; case CustomerListActionTypes.LoadListFail: return { ...state, loading: false }; case CustomerListActionTypes.LoadListSuccess: return { ...state, customerList: [...state.customerList, ...action.payload], loading: false, page: state.page + 1, }; case CustomerListActionTypes.ResetList: return initialState; case CustomerListActionTypes.UpdateQueryFromParamMap: return { ...state, query: action.payload.get('query') || '' }; default: return state; } } export const getCustomerList = state => state.customerList; export const getLoading = state => state.loading; export const getPage = state => state.page; export const getQuery = state => state.query;
Effects
// store/effects/customer-lits.effects.ts @Injectable() export class CustomerListEffects { @Effect() loadFirst$ = this.actions$.pipe( ofType<LoadFirstList>(CustomerActionTypes.LoadFirstList), mergeMap(action => [new ResetList(), new LoadList()]) ); @Effect() load$ = this.actions$.pipe( ofType<LoadList>(CustomerActionTypes.LoadList), withLatestFrom( this.store.select(fromStore.getQuery), this.store.select(fromStore.getPage) ), switchMap(([action, query, page]) => this.service.getList(query, page + 1).pipe( map(customerList => new LoadListSuccess(customerList)), catchError(error => of(new LoadListFail())) ) ) ); constructor( private actions$: Actions, private service: CustomerListService, private store: Store<fromStore.State> ) {} }
Container
// コンテナコンポーネント export class CustomerListContainer { constructor( private route: ActivatedRoute, private router: Router, protected store: Store<fromStore.State> ) { route.paramMap.pipe(takeUntil(this.unsubscribe)).subscribe(queries => { this.store.dispatch(new UpdateQueryFromParamMap(queries)); this.store.dispatch(new LoadFirstList()); }); // ... }
CSS 関連
「flex-basis」と「width」の違いは何でしょうか? 「flex-basis」は、flexコンテナの主軸の方向に相当するということです。
…
「flex-basis」は2つとも同じ値です。つまり、十分なスペースがあれば(コンテナは600pxと余白とパディングの余裕があります)、どちらも幅が300pxになります。
しかし、コンテナが大きくなるにつれて、「.square#one」(flex-grow :2)は2倍の速さで広がります。逆に、コンテナが縮むにつれて、「.square#two」(flex-shrink :2)は2倍の速さで縮小します。
…
「.square#one」が広がる時に、「.square#two」の2倍のサイズには広がりません。同様に縮む時も1/2のサイズに縮むのではありません。
それぞれの値で指定した「2 1」、「1 2」はサイズではありません。広がる時と縮む時の率です。