ふるてつのぶろぐ

福岡在住のエンジニアです。

写真提供:福岡市

Angular8 で Web アプリを作ろう - 基本に立返ってマスタメンテ画面

今日すること

こんにちは、ふるてつです。
わたし的にMaterialの使い方にだいぶ慣れてきました。
そこでいったん基本に返りたいと思い、簡単なマスタメンテ画面を作ってみました。
今日はその作業について書きます。

会社マスタ

比較的軽そうな会社マスタメンテ画面を作ります。
一覧と詳細画面の2枚になります。
結果からお見せすると一覧画面は下記のような感じになりました。
作りたてでスタイルが良くないところがありますが、別途そのうち調整します。
f:id:tetsufuru:20190803175926p:plain:w500

そして詳細画面は下記になります。
f:id:tetsufuru:20190803180829p:plain:w500

一覧画面について
  1. メニューは別のComponentにしていますが、それ以外の部分、検索条件や検索結果は1つのComponentだけで実装しています。
  2. 検索時のサーバとの通信部分はこれまでとおなじでrxjsmergeオペレータを使います。mergeの引数にページネーションの変数を設定すると、ページネーションを変更するたびに通信するようになります。マージはhttps://material.angular.io/components/table/examplesの"Table retrieving data through HTTP"の個所を参考にすると良いです。

  3. 検索結果の表示はMaterialData tableを使います。
    サーバ側からは必要な件数のみが返ってきます。

  4. 一覧の中にある登録日付や更新日付はpipeを使ってフォーマットを指定して、さらにタイムゾーンも試しに指定しました。
    htmlの中では下記のような感じで指定します。暫定ですがtimezoneには'UTC/Asia/Tokyo'を、localeには'ja-JP'を画面表示時に固定で設定しています。
    将来的にはユーザ毎にDBを見て切替できるようにします。
    しかしこのあたりの機能はすごく便利ですねぇ。
    {{element.createTime|date:'medium':timezone:locale}}
  5. 詳細画面の呼び出しには2パターンあります。 新規ボタンをクリックした場合は下記のように/company-detail/newで呼び出し。
    private onNew() {
     this.router.navigate(['/company-detail/new']);
    }
    一覧のいずれかのレコードをクリックした時は、クリックしたマスタのシーケンス番号をつけて遷移するようにしました。/company-detail/:companySeqという感じです。
    private listClicked(searchCompanyDto: SearchCompanyDto) {
     this.router.navigate(['/company-detail', searchCompanyDto.companySeq]);
    }
詳細画面について
  1. 詳細画面も1つのComponentで作りました。
  2. /company-detail/newで呼び出されたときは新規登録用として入力項目が空の状態で開きます。
  3. companySeqパラメータ付きのURL/company-detail/:companySeqで呼び出されたときは、サーバから該当するマスタデータを取得して編集用として開きます。
  4. 入力のバリデーションはリアクティブフォームを使います。https://angular.jp/guide/reactive-forms
  5. 更新前の確認ダイアログも表示するようにしました。Materialのリファレンスに沿うと簡単に組み込めます。https://material.angular.io/components/dialog/overview
画面のソースについて

一覧画面と詳細画面の2画面になると書くことが多くなりました。
そこでソースを以下に追記しました。なにかの参考にしていただければ嬉しいです。
不備や良くないやり方などあるかもしれませんがそこはご容赦ください。

今日のソース 一覧画面
company-list.component.html

<div class="mainColor">
  <div class="wrapper mainBody">
    <form [formGroup]="mainForm">
      <!-- Screen Title Area -->
      <div class="titleArea">
        {{ 'companyListScreen.title' | translate }}
      </div>

      <!-- Search Conditions Area -->
      <mat-card>
        <mat-card-content>
          <app-error-messages></app-error-messages>
          <div id="searchConditionsArea">
            <!-- -------------------- 1 -------------------- -->
            <div id="searchCondition1">
              <mat-form-field class="form-field">
                <input id="companyName" matInput type="text" formControlName="companyName"
                  placeholder="{{ 'companyListScreen.companyName' | translate }}">
              </mat-form-field>
            </div>
            <div id="searchCondition2">
              <mat-form-field class="form-field">
                <input id="companyKana" matInput type="text" formControlName="companyKana"
                  placeholder="{{ 'companyListScreen.companyKana' | translate }}">
              </mat-form-field>
            </div>
            <div id="searchCondition3">
              <label class="labelFor">{{ 'companyListScreen.deleted'  | translate}}</label>
              <mat-checkbox id="deleted" formControlName="deleted"></mat-checkbox>
            </div>
          </div>
        </mat-card-content>
      </mat-card>

      <!-- Button Area -->
      <div id="searchButtonArea">
        <div id="paginatorArea">
          <mat-paginator [length]="resultsLength" [pageSize]="50" [pageSizeOptions]="[10, 50, 100]"></mat-paginator>
        </div>
        <div id="newBtnArea">
          <button mat-raised-button color="primary" id="newBtn" class="btn" (click)="onNew()"
            type="button">{{ "companyListScreen.newBtn" | translate }}
          </button>
        </div>
        <div id="clearBtnArea">
          <button mat-raised-button color="primary" id="clearBtn" class="btn" (click)="onClear()"
            type="button">{{ "companyListScreen.clearBtn" | translate }}
          </button>
        </div>
        <div id="searchBtnArea">
          <button mat-raised-button color="primary" id="searchBtn" class="btn" type="submit"
            (click)="onSearch()">{{ "companyListScreen.searchBtn" | translate }}
          </button>
        </div>
      </div>

      <!-- evaluationResult Area-->
      <div id="evaluationResult">
        <div class="loading-shade" *ngIf="isLoadingResults">
          <mat-spinner class="loading-spinner" *ngIf="isLoadingResults"></mat-spinner>
        </div>

        <div class="example-container">
          <table mat-table *ngIf="resultsLength>0" [dataSource]="searchCompanyDtos">
            <ng-container matColumnDef="companySeq">
              <th mat-header-cell *matHeaderCellDef style="width: 10%;">
                {{ "companyListScreen.searchResult.companySeq" | translate }}
              </th>
              <td mat-cell *matCellDef="let element"> {{element.companySeq}} </td>
            </ng-container>
            <ng-container matColumnDef="companyName">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.companyName" | translate }}
              </th>
              <td mat-cell *matCellDef="let element"> {{element.companyName}} </td>
            </ng-container>
            <ng-container matColumnDef="companyKana">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.companyKana" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.companyKana}} </td>
            </ng-container>
            <ng-container matColumnDef="companyAddress1">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.companyAddress1" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.companyAddress1}} </td>
            </ng-container>
            <ng-container matColumnDef="numOfEmployee">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.numOfEmployee" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.numOfEmployee}} </td>
            </ng-container>

            <ng-container matColumnDef="deleted">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.deleted" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.deleted}} </td>
            </ng-container>
            <ng-container matColumnDef="evaluationSetting">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.evaluationSetting" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.evaluationSetting}} </td>
            </ng-container>
            <ng-container matColumnDef="createTime">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.createTime" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.createTime|date:'medium':timezone:locale}}
              </td>
            </ng-container>
            <ng-container matColumnDef="createUser">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.createUser" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.createUser}} </td>
            </ng-container>
            <ng-container matColumnDef="updateTime">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.updateTime" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.updateTime|date:'medium':timezone:locale}} </td>
            </ng-container>
            <ng-container matColumnDef="updateUser">
              <th mat-header-cell *matHeaderCellDef>
                {{ "companyListScreen.searchResult.updateUser" | translate }}</th>
              <td mat-cell *matCellDef="let element"> {{element.updateUser}} </td>
            </ng-container>

            <tr mat-header-row *matHeaderRowDef="displayCompanyListColumns; sticky: true"></tr>
            <tr mat-row *matRowDef="let row; columns: displayCompanyListColumns;" (click)="listClicked(row)"></tr>
          </table>
        </div>
      </div>
    </form>
  </div>
</div>

company-list.component.ts

import { merge, of } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { SearchCompanyDto } from 'src/app/entity/company/search-company-dto';
import { CompanyService } from 'src/app/service/company/company.service';

import { HttpParams } from '@angular/common/http';
import { Component, OnInit, ViewChild, Inject, LOCALE_ID } from '@angular/core';
import { FormBuilder, FormControl } from '@angular/forms';
import { MatPaginator } from '@angular/material/paginator';
import { AppConst } from 'src/app/app-const';
import { Router } from '@angular/router';
import { Title } from '@angular/platform-browser';

@Component({
  selector: 'app-company-list',
  templateUrl: './company-list.component.html',
  styleUrls: ['./company-list.component.css']
})
export class CompanyListComponent implements OnInit {
  // These are the search condition settings.
  public companyName = new FormControl('', []);
  public companyKana = new FormControl('', []);
  public deleted = new FormControl('', []);

  public mainForm = this.formBuilder.group({
    companyName: this.companyName,
    companyKana: this.companyKana,
    deleted: this.deleted
  });

  public searchCompanyDtos: SearchCompanyDto[];
  public displayCompanyListColumns: string[] = [
    'companySeq',
    'companyName',
    'companyKana',
    'companyAddress1',
    'deleted',
    'createUser',
    'createTime',
    'updateUser',
    'updateTime',
  ];

  public resultsLength = 0;
  public isLoadingResults = false;

  // timezone & locale
  public locale: string;
  public timezone: string;

  @ViewChild(MatPaginator, { static: true }) public paginator: MatPaginator;

  constructor(
    private formBuilder: FormBuilder,
    private companyService: CompanyService,
    private title: Title,
    private router: Router
  ) { }

  ngOnInit() {
    this.setUpLocale();
    this.setUpBrowserTitle();
  }

  private setUpLocale() {
    this.locale = AppConst.LOCALE;
    this.timezone = AppConst.TIMEZONE;
  }

  private setUpBrowserTitle() {
    this.title.setTitle(AppConst.APP_TITLE + AppConst.APP_SUB_TITLE_COMPANY_LIST);
  }

  private onNew() {
    this.router.navigate(['/company-detail/new']);
  }

  private onClear() {
    this.clearSearchCondition();
    this.clearSearchResultList();
  }

  private onSearch() {
    merge(this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.companyService.getCompanyList(this.createHttpParams());
        }),

        map(data => {
          // Flip flag to show that loading has finished.
          this.isLoadingResults = false;
          this.resultsLength = data.resultsLength;
          this.paginator.pageIndex = data.pageIndex;
          return data.searchCompanyDtos;
        }),

        catchError(() => {
          this.isLoadingResults = false;
          return of(null as any);
        })

      ).subscribe(data => this.searchCompanyDtos = data);
  }

  private createHttpParams(): HttpParams {
    const conditions = {
      companyName: this.companyName.value,
      companyKana: this.companyKana.value,
      deleted: this.deleted.value,

      pageSize: this.paginator.pageSize.toString(),
      pageIndex: this.paginator.pageIndex.toString()
    };

    const paramsOptions = { fromObject: conditions };
    const params = new HttpParams(paramsOptions);

    return params;
  }

  private clearSearchCondition() {
    this.companyName.setValue('');
    this.companyKana.setValue('');
    this.deleted.setValue('');
  }

  private clearSearchResultList() {
    this.searchCompanyDtos = null;
    this.resultsLength = 0;
  }
  private listClicked(searchCompanyDto: SearchCompanyDto) {
    this.router.navigate(['/company-detail', searchCompanyDto.companySeq]);
  }

}

詳細画面
company-detail.component.html

<div class="mainColor">
  <div class="loading-shade" *ngIf="isLoadingResults">
    <mat-spinner class="loading-spinner" *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="wrapper mainBody">
    <form [formGroup]="mainForm">
      <div class="editingArea">

        <mat-card>
          <mat-card-content>
            <app-error-messages></app-error-messages>
            <!-- Screen Title Area -->
            <div class="titleArea">
              {{ 'companyDetailScreen.title' | translate }}
            </div>
            <!-- Input Area Company Information-->
            <div class="subTitleArea">
              {{ 'companyDetailScreen.subTitle01' | translate }}
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthShort">
                  <input id="companySeq" matInput type="text"
                    placeholder="{{ 'companyDetailScreen.companySeq' | translate }} " disabled value={{companySeq}}>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id="companyName" matInput type="text" maxlength="50" formControlName="companyName"
                    placeholder="*{{ 'companyDetailScreen.companyName' | translate }}">
                  <mat-error *ngIf="companyName.hasError('required')">
                    {{ 'validateErrorMessage.required' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id=" companyKana" matInput type="text" maxlength="50" formControlName="companyKana"
                    placeholder="{{ 'companyDetailScreen.companyKana' | translate }}">
                  <mat-error *ngIf="companyKana.hasError('required')">
                    {{ 'validateErrorMessage.required' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthShort">
                  <input id="companyPostalCode" matInput type="text" maxlength="10" formControlName="companyPostalCode"
                    placeholder="*{{ 'companyDetailScreen.companyPostalCode' | translate }}">
                  <mat-error *ngIf="companyPostalCode.hasError('required')">
                    {{ 'validateErrorMessage.required' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthShort">
                  <input id="companyAddress1" matInput type="text" maxlength="100" formControlName="companyAddress1"
                    placeholder="*{{ 'companyDetailScreen.companyAddress1' | translate }}">
                  <mat-error *ngIf="companyAddress1.hasError('required')">
                    {{ 'validateErrorMessage.required' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthLong">
                  <input id="companyAddress2" matInput type="text" maxlength="100" formControlName="companyAddress2"
                    placeholder="*{{ 'companyDetailScreen.companyAddress2' | translate }}">
                  <mat-error *ngIf="companyAddress2.hasError('required')">
                    {{ 'validateErrorMessage.required' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthLong">
                  <input id="companyAddress3" matInput type="text" maxlength="100" formControlName="companyAddress3"
                    placeholder="{{ 'companyDetailScreen.companyAddress3' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field">
                  <input id="companyPhoneNumber" matInput type="tel" maxlength="15" formControlName="companyPhoneNumber"
                    placeholder="{{ 'companyDetailScreen.companyPhoneNumber' | translate }}">
                </mat-form-field>
              </div>
            </div>

            <!-- Input Area Person In Charge -->
            <div class="subTitleArea">
              {{ 'companyDetailScreen.subTitle02' | translate }}
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id="departmentInCharge1" matInput type="text" maxlength="100"
                    formControlName="departmentInCharge1"
                    placeholder="{{ 'companyDetailScreen.departmentInCharge1' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id="departmentInCharge2" matInput type="text" maxlength="100"
                    formControlName="departmentInCharge2"
                    placeholder="{{ 'companyDetailScreen.departmentInCharge2' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id="departmentInCharge3" matInput type="text" maxlength="50"
                    formControlName="departmentInCharge3"
                    placeholder="{{ 'companyDetailScreen.departmentInCharge3' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field">
                  <input id="personInChargeFirstName" matInput type="text" maxlength="50"
                    formControlName="personInChargeFirstName"
                    placeholder="{{ 'companyDetailScreen.personInChargeFirstName' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field">
                  <input id="personInChargeLastName" matInput type="text" maxlength="50"
                    formControlName="personInChargeLastName"
                    placeholder="{{ 'companyDetailScreen.personInChargeLastName' | translate }}">
                </mat-form-field>
              </div>
              <div class="editingInputArea">
                <mat-form-field class="form-field inputWidthMiddle">
                  <input id="personInChargeEmailAddress" matInput type="email" maxlength="255"
                    formControlName="personInChargeEmailAddress"
                    placeholder="{{ 'companyDetailScreen.personInChargeEmailAddress' | translate }}">
                  <mat-error *ngIf="personInChargeEmailAddress.hasError('email')">
                    {{ 'validateErrorMessage.format' | translate }}
                  </mat-error>
                </mat-form-field>
              </div>
            </div>

            <!-- Input Area Others -->
            <div class="subTitleArea">
              {{ 'companyDetailScreen.subTitle03' | translate }}
              <div class="editingInputArea">
                <label class="labelFor"> {{ 'companyDetailScreen.deleted'  | translate}}</label>
                <mat-checkbox id="deleted" formControlName="deleted"></mat-checkbox>
                <mat-form-field *ngIf="deleted.value" class="form-field inputWidthShort" style="padding-left: 1rem;">
                  <input id="deletedDay" matInput type="email" formControlName="deletedDay"
                    placeholder="{{ 'companyDetailScreen.deletedDay' | translate }}">
                </mat-form-field>
              </div>
            </div>

          </mat-card-content>
        </mat-card>

        <!-- Button Area -->
        <div class="editingButtonArea">
          <button mat-raised-button color="primary" id="backBtn" class="btn" type="button"
            (click)="onBack()">{{ "commonScreen.backBtn" | translate }}
          </button>
          <button mat-raised-button color="primary" id="updateBtn" class="btn" style="margin-left: 2rem;" type="submit"
            [disabled]="!mainForm.valid" (click)="onUpdate()">{{ "commonScreen.updateBtn" | translate }}
          </button>
        </div>

      </div>
    </form>
  </div>
</div>

company-detail.component.ts

import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AppConst } from 'src/app/app-const';
import { CompanyDto } from 'src/app/entity/company/company-dto';
import { YesNoDialogData } from 'src/app/entity/dialog/yes-no-dialog-data';
import { CompanyService } from 'src/app/service/company/company.service';

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Title } from '@angular/platform-browser';
import { Router, ActivatedRoute } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';

import { YesNoDialogComponent } from '../../common/dialog/yes-no-dialog/yes-no-dialog.component';

@Component({
  selector: 'app-company-detail',
  templateUrl: './company-detail.component.html',
  styleUrls: ['./company-detail.component.css']
})

export class CompanyDetailComponent implements OnInit {
  public companySeq: bigint;
  public companyName = new FormControl('', [Validators.required]);
  public companyKana = new FormControl('', [Validators.required]);
  public companyPostalCode = new FormControl('', [Validators.required]);
  public companyAddress1 = new FormControl('', [Validators.required]);
  public companyAddress2 = new FormControl('', [Validators.required]);
  public companyAddress3 = new FormControl('', []);
  public companyPhoneNumber = new FormControl('', []);
  public personInChargeFirstName = new FormControl('', []);
  public personInChargeLastName = new FormControl('', []);
  public departmentInCharge1 = new FormControl('', []);
  public departmentInCharge2 = new FormControl('', []);
  public departmentInCharge3 = new FormControl('', []);
  public personInChargeEmailAddress = new FormControl('', [Validators.email]);
  public deleted = new FormControl('', []);
  public deletedDay = new FormControl('', []);

  public isLoadingResults = false;

  public mainForm = this.formBuilder.group({
    companyName: this.companyName,
    companyKana: this.companyKana,
    companyPostalCode: this.companyPostalCode,
    companyAddress1: this.companyAddress1,
    companyAddress2: this.companyAddress2,
    companyAddress3: this.companyAddress3,
    companyPhoneNumber: this.companyPhoneNumber,
    personInChargeFirstName: this.personInChargeFirstName,
    personInChargeLastName: this.personInChargeLastName,
    departmentInCharge1: this.departmentInCharge1,
    departmentInCharge2: this.departmentInCharge2,
    departmentInCharge3: this.departmentInCharge3,
    personInChargeEmailAddress: this.personInChargeEmailAddress,
    deleted: this.deleted,
    deletedDay: this.deletedDay
  });

  constructor(
    private formBuilder: FormBuilder,
    private companyService: CompanyService,
    private readonly translateService: TranslateService,
    private dialog: MatDialog,
    private title: Title,
    private router: Router,
    private route: ActivatedRoute,
  ) { }

  ngOnInit() {
    this.setUpBrowserTitle();
    const companySeq = +this.route.snapshot.paramMap.get('companySeq');
    this.getCompany(companySeq);
  }

  private setUpBrowserTitle() {
    this.title.setTitle(AppConst.APP_TITLE + AppConst.APP_SUB_TITLE_COMPANY_Detail);
  }

  getCompany(companySeq: number) {
    this.isLoadingResults = true;

    this.companyService.getCompany(companySeq)
      .pipe(
        catchError(() => {
          this.isLoadingResults = false;
          return of(null as any);
        })

      ).subscribe(data => {
        this.isLoadingResults = false;
        this.extractFromCompanyDto(data);
      });

  }

  private onBack() {
    this.router.navigate(['/company-list']);
  }

  private onUpdate() {
    const dialogData: YesNoDialogData = {
      title: this.translateService.instant('companyDetailScreen.saveYesNoDialog.title'),
      message: this.translateService.instant('companyDetailScreen.saveYesNoDialog.message'),
      captionYes: this.translateService.instant('companyDetailScreen.saveYesNoDialog.captionYes'),
      captionNo: this.translateService.instant('companyDetailScreen.saveYesNoDialog.captionNo')
    };

    const dialogRef = this.dialog.open(YesNoDialogComponent, {
      width: '250px',
      height: '200px',
      data: dialogData
    });

    dialogRef.afterClosed().subscribe(result => {
      console.log('Dialog result: ${result}');
      if ('${result}') {
        this.update();
      }
    });
  }

  private update() {

    this.isLoadingResults = true;
    const companyDto: CompanyDto = this.createCompanyDto();

    this.companyService.createCompany(companyDto)
      .pipe(
        catchError(() => {
          this.isLoadingResults = false;
          return of(null as any);
        })

      ).subscribe(data => {
        this.isLoadingResults = false;
        this.extractFromCompanyDto(data);
      });

  }

  private createCompanyDto(): CompanyDto {

    const companyDto: CompanyDto = new CompanyDto();
    companyDto.companySeq = this.companySeq;
    companyDto.companyName = this.companyName.value;
    companyDto.companyKana = this.companyKana.value;
    companyDto.companyPostalCode = this.companyPostalCode.value;
    companyDto.companyAddress1 = this.companyAddress1.value;
    companyDto.companyAddress2 = this.companyAddress2.value;
    companyDto.companyAddress3 = this.companyAddress3.value;
    companyDto.companyPhoneNumber = this.companyPhoneNumber.value;
    companyDto.personInChargeLastName = this.personInChargeLastName.value;
    companyDto.personInChargeFirstName = this.personInChargeFirstName.value;
    companyDto.departmentInCharge1 = this.departmentInCharge1.value;
    companyDto.departmentInCharge2 = this.departmentInCharge2.value;
    companyDto.departmentInCharge3 = this.departmentInCharge3.value;
    companyDto.personInChargeEmailAddress = this.personInChargeEmailAddress.value;
    companyDto.deleted = this.deleted.value;

    return companyDto;

  }

  private extractFromCompanyDto(companyDto: CompanyDto) {

    this.companySeq = companyDto.companySeq;
    this.companyName.setValue(companyDto.companyName);
    this.companyKana.setValue(companyDto.companyKana);
    this.companyPostalCode.setValue(companyDto.companyPostalCode);
    this.companyAddress1.setValue(companyDto.companyAddress1);
    this.companyAddress2.setValue(companyDto.companyAddress2);
    this.companyAddress3.setValue(companyDto.companyAddress3);
    this.companyPhoneNumber.setValue(companyDto.companyPhoneNumber);
    this.personInChargeLastName.setValue(companyDto.personInChargeLastName);
    this.personInChargeFirstName.setValue(companyDto.personInChargeFirstName);
    this.departmentInCharge1.setValue(companyDto.departmentInCharge1);
    this.departmentInCharge2.setValue(companyDto.departmentInCharge2);
    this.departmentInCharge3.setValue(companyDto.departmentInCharge3);
    this.personInChargeEmailAddress.setValue(companyDto.personInChargeEmailAddress);
    this.deleted.setValue(companyDto.deleted);

  }
}

company.service.ts

import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AppConst } from 'src/app/app-const';
import { SearchCompanyListDto } from 'src/app/entity/company/search-company-list-dto';
import { ErrorMessageService } from 'src/app/service/message/error-message.service';
import { environment } from 'src/environments/environment';

import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { CompanyDto } from 'src/app/entity/company/company-dto';

@Injectable({
  providedIn: 'root'
})
export class CompanyService {
  private server = environment.production ? AppConst.URL_PROD_SERVER : AppConst.URL_DEV_SERVER;

  constructor(
    private http: HttpClient,
    private errorMessageService: ErrorMessageService,
    private readonly translateService: TranslateService
  ) { }

  public getCompanyList(httpParams: HttpParams): Observable {
    const webApiUrl: String = 'company-list';
    return this.http.get(this.server + webApiUrl, { params: httpParams })
      .pipe(
        catchError(err => {
          this.errorMessageService.add(this.translateService.instant('errMessage.http'));
          return of(null as any);
        })
      );

  }

  public getCompany(companySeq: number): Observable {
    const webApiUrl: String = `company/?companySeq=${companySeq}`;

    return this.http.get(this.server + webApiUrl, {
      params: {
        companySeq: `${companySeq}`
      }
    }
    )
      .pipe(
        catchError(err => {
          this.errorMessageService.add(this.translateService.instant('errMessage.http'));
          return of(null as any);
        })
      );
  }

  public createCompany(companyDto: CompanyDto): Observable {
    const webApiUrl: String = 'company';

    return this.http.post(this.server + webApiUrl, companyDto)
      .pipe(
        catchError(err => {
          this.errorMessageService.add(this.translateService.instant('errMessage.http'));
          return of(null as any);
        })
      );

  }

  public updateCompany(companyDto: CompanyDto): Observable {
    const webApiUrl: String = 'company';

    return this.http.put(this.server + webApiUrl, companyDto)
      .pipe(
        catchError(err => {
          this.errorMessageService.add(this.translateService.instant('errMessage.http'));
          return of(null as any);
        })
      );

  }

}

今日の感想

今日はマスタメンテ画面について書いてみました。
作ったばかりでまだ修正すべきと思うところは多々あります。
例えば一覧で使用したlocaletimezoneはいまは画面の変数として持っているだけですが、共通で使えるようサービスにしたほうが良い気もします。
styleを直で指定しているところもあります、すみません… しかし、とりあえずは一通りの機能ができたので、次回はこれらにユニットテストを書いていきたいと思います。
サーバ側のJavaのMockitoなどでなら経験があるのですがjsのテストコードは個人的には書いたことがありませんので楽しみです。

では、今日もお疲れ様でした。