6月メモ・リンク集

6月に調べたことのメモです。

Angular 関連

ngrx 関連

ngrx 実装メモ

f:id:ryotah:20180706181849p:plain

store をつくるとき、どのような順で整理して実装しているか、についてのメモです。
例えば「ID順で表示される顧客一覧画面(テキストによる検索機能と追加読み込み機能)」をつくる場合は以下のように整理します。

  1. その機能 (Feature)で「できること」のリストアップ
    • 顧客一覧表示
    • 顧客検索
    • 一覧を追加読み込み
    • 検索条件を編集
    • 別画面(別 Feature)に移動
  2. 必要な情報は何か
    • 取得した顧客情報
    • 顧客一覧のローディング状況
    • 現在のページ番号
    • 検索条件
  3. 各 state を作成
    • customer-list.reducre.ts
      • customerList: Customer[];
      • loading: boolean
      • page: number
      • query: string
  4. action になりそうなものをリストアップ
    • 検索開始(検索条件が空の場合はID順で取得)
    • 検索成功
    • 検索失敗
    • 追加読み込み開始
    • 追加読み込み成功
    • 追加読み込み失敗
    • 検索条件を更新
    • ページを離れる
  5. action と reducer の関係を考える
    • 検索開始
      • loading = true
      • (その他のデータは一旦リセット)
    • 検索成功
      • customerList = 検索結果
      • loading = false
      • page = 1
    • 検索失敗
      • loading = false
    • 追加読み込み開始
      • loading = true
    • 追加読み込み成功
      • customerList += 検索結果
      • loading = false
      • page += 1
    • 追加読み込み失敗
      • loading = false
    • ページを離れる
      • データをリセット
  6. 無駄が多そうな場合調整する
    今回は「検索」と「追加読み込み」がほぼ同じなので共通化できそう

結果

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」はサイズではありません。広がる時と縮む時の率です。