狛ログ

2020年11月27日金曜日

NgRx 8にて発生した「@ngrx/store: runtime checks are currently opt-in but ...」の警告の解決方法。

11月 27, 2020

オフィス狛 技術部のmmm(むー)です。

Angular8 + ngrx8を使用している担当プロジェクトで、ngrx/storeの警告がでているのが気になったので対処しました。

1. エラーメッセージの確認

@ngrx/store: runtime checks are currently opt-in but will be the default in the next major version with the possibility to opt-out, see https://ngrx.io/guide/migration/v8 for more information.
直訳:runtime checksは現在オプトイン形式がデフォルトですが、次のメジャーバージョンでオプトアウト形式になる可能性があります。詳細はサイトで確認してください。

これだけではさっぱりわからなかったので、メッセージに従いサイトを確認します。

2. ngrx バージョン8のアップデートガイド確認

色々書かれていますが、今回関連があるのは下記のようです。
A migration is provided to remove the usage ngrx-store-freeze, remove it from the package.json, and to enable the built-in runtime checks strictStateImmutability and strictActionImmutability.
直訳:ngrx-store-freezeの使用を取り除くマイグレーションが提供されます。package.jsonから削除して、ビルトインのruntime checks(strictStateImmutability とstrictActionImmutability)を有効にしてください。

要するに、ngrx7まではngrx-store-freezeを使用してStoreをimmutable(不変)にする必要があります。
しかし、ngrx8以上ではライブラリ不要になるので、runtime checksの値を設定してくださいとのことです。
弊社ではngrx-store-freezeを使用していませんが、同様にエラーが出ていました... とにかく設定しないといけないみたいです。

3. RuntimeChecksのページ確認

ページ内の記述方法を参考にstrictStateImmutabilitystrictActionImmutabilityの値を設定します。
ngrx-store-freezeを使用している場合は、設定している2つの項目はtrueにしてください。
// app.module.ts

StoreModule.forRoot(reducers),

  // ↓

StoreModule.forRoot(reducers, {
  runtimeChecks: {
    strictStateImmutability: false,
    strictActionImmutability: false
  }
}),

以上となります。
サイトに書いてあることをそのまま実行しただけですが、ngrxについて日本語で書かれているページがあまりないので、よければ参考にしてみてください。🍭

2020年11月25日水曜日

WindowsでDockerを利用してMySQLサーバーを立てる場合の注意点。

11月 25, 2020
オフィス狛 技術部のHammarです。

最近はnode.jsを触る機会がまた多くなってきたのですが、あるプロジェクトでAPIはnode.js、DBはMysqlを使うことになったので、Dockerでそれらのローカル環境を作り開発することにしました。
そこでWindowsとDockerの絡みでハマりポイントがあったので、ご紹介したいと思います。

※今回はDockerがWindows10環境にインストールされている前提で以下進めていきます。

まずDockerの起動にはdocker-compose.ymlを利用していきます。
そのdocker-compose.ymlには今回開発につかうAPIとDBの設定を下記のように記載します。
※今回DBの記述がメインとなるので、API側の記述は割愛します。
version: '3'
services:
  api:
	build:・・・
	・・・
  db:
    image: mysql:8.0
    command: mysqld --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - './docker/dev/mysql/data:/var/lib/mysql'
      - './docker/dev/mysql/my.cnf:/etc/mysql/conf.d/my.cnf'
      - './docker/dev/mysql/sql:/docker-entrypoint-initdb.d'

上記のように書いて、あとは通常通り上記ymlを使ってDocker起動するだけです。
しかしながら、上記方法でMacでは上手くいくのですが、Windowsではなぜかうまくいきません。

これはWindowsはMacとは違い、WindowsはdockerをVirtualBox経由で起動させているためのようです。
こちら参考にさせていただきました。
https://qiita.com/waterada/items/1dbf6a977611e0e8f5c8

※ちなみにWindowsは上記が原因で、作りたい環境内容によっては他の問題も発生するようで、実際自分も別環境作成時にまた問題があったのですが、またそのあたり別の投稿で記載しようと思います。

ということでいろいろと調べた結果、mysqlの設定ファイルであるmy.cnfファイルを別途作成し、それをdocker起動時にマウントさせることによって本事象を解消できるということのようでした。

1.docker-compose.ymlを修正する

まずはdocker-compose.ymlを下記のように書き換えます。
version: '3'
services:
  api:
	build:・・・
	・・・
  db:
    image: mysql:8.0
    command: mysqld --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - './docker/dev/mysql/sql:/docker-entrypoint-initdb.d'

2.my.cnfファイルを作成する

もともとDBの文字コードの設定もymlファイルのcommand部分に記載していたのですが、このあたりをmy.cnfに別途下記のように作成します。
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4

3.Dockerfileを作成する

最後に上記のmy.cnfファイルをマウントさせるために、下記のようにDockerfileを作成し、そこでADDします。
またファイルの権限もデフォルトだと777となってしまい、mysqlは権限777のcnfファイルは読み込まないということなので、ADDしたあとに権限も変更するように記述します。
FROM mysql:8.0

ADD ./docker/dev/mysql/my.cnf /etc/mysql/conf.d/my.cnf

RUN chmod 644 /etc/mysql/conf.d/my.cnf

上記3つが整った状態で、ymlを使ってDocker起動すると、なんとかうまく起動されました。
上記手順書いてみると、なるほどなーと感じるのですが、これを全く知らないところから調べていったので、かなりハマって環境構築だけで結構時間がかかってしまいました。。。
そもそもやっぱりVirtualBox経由でDockerを利用することがいろいろな弊害を生んでいるようなので、このあたりWindowsユーザーは不利だなーと感じたのでした。

Illustrator2021のオブジェクトを再配色を試してみる。

11月 25, 2020


こんにちは、オフィス狛 デザイン部のSatoです。

先日アップデートされましたIllustrator2021で、以前ブログでご紹介した「オブジェクトを再配色」という機能がさらに便利になったようなのでご紹介します。

↓以前アップしたオブジェクトを再配色の記事です。

実は元々の機能の場所などが少し変わっていますので、「オブジェクトを再配色」画面の表示方法から説明していきます。 


色を変えたいオブジェクト全体を選択し、Illustratorメニューバーの「編集」の「カラーの編集」から「オブジェクトを再配色」を選ぶかツールバーの「オブジェクトを再配色アイコン」をクリックします。

以前は「オブジェクトを再配色パレット」が表示……されていたのですが、今回のアップデートから新しい「オブジェクトを再配色」画面が表示されるようになりました。

今まで最初に表示されていた「オブジェクトを再配色パレット」は右下に表示される「詳細オプション」ボタンをクリックすることで表示されるようになっています。
今まで通り再配色パレットを最初に表示したい場合ははオブジェクトを再配色パレットの左下に表示される「起動時に詳細オブジェクトを再配置ダイアログを開く」にチェックを入れましょう。
(新しい「オブジェクトを再配色」のダイアログが表示しづらくなるのであまりおすすめはできません)

新しくなったオブジェクトを再配色には「カラーテーマピッカー」や「目立つカラー」パレットなどの機能が追加されたのでご紹介したいと思います。 


最初にご紹介したいのはカラーテーマピッカーツールです。
なんとこの機能、選択したオブジェクトではない別の画像やオブジェクトの全体的な配色をつかって再配色してくれる機能です。
「カラーテーマピッカー」ボタンを押すとマウスカーソルがスポイトマークに変化するので参考にしたい画像をクリックしたり、Shift キーを押しながらクリックして複数選択したり、ドラッグして選択範囲を選択します。

画像を使い、Illustratorで描いたイラストの色を変えました

選択した画像でよく使われている色に変わりましたが、なんだか配色にメリハリがない配色になってしまいました。
こういう時にもう一つの新機能 「目立つカラー」パレットが役立ちます「目立つカラー」パレットの目立たせたい明度の高い色の端を横にドラッグして広げます。

「目立つカラー」パレットを調整してイラストがメリハリのある配色になりました!


オブジェクトを再配色を駆使し色をさらに調整してイラストを完成させました。

「カラーテーマピッカー」も「目立つカラー」も、よく色の参考に使っている「カラーライブラリ機能」よりもなかなか普段使わないような配色のデザインが作れるので、普段似たような色ばかり使ってしまうなあ……と悩んだ時に使ってみたい機能だと思います。


みなさんも配色に悩んだ際に「オブジェクトを再配色」の新しい機能を使ってみてください。

2020年10月29日木曜日

Angular・【Simple Modal Module】モーダルを手軽に実装する。

10月 29, 2020

こんにちは、オフィス狛 技術部のpinoです。

今回は、AngularのSimple Modal Moduleの使い方についてご紹介したいと思います。

Simple Modal Moduleは、とても簡単にモーダルが実装できるプラグインです。
bootstrapなどでモーダルを作成されていた方も、この機会にぜひ目を通していただきたい内容です。

作れるもの

Simple Modal Moduleで実装できるモーダルは、以下のデモで試せます。
https://ngx-simple-modal-demo.stackblitz.io/

準備

まずは、以下のコマンドでインストールします。
npm install ngx-simple-modal

基本となるCSSはすでに用意されていますので、angular.jsonに以下を定義します。
angular.json
"styles": [
   "styles.css",
   "../node_modules/ngx-simple-modal/styles/simple-modal.css"
],
これで、モーダル表示時のスタイルやアニメーションはバッチリです。

HTMLは、上記のCSSに定義されているスタイルを使用するような形でテンプレートがある程度決まっていますので、以下の形から派生させていくことになります。
HTML
<div class="modal-content">
    <div class="modal-header">
      <!-- モーダルのタイトル -->
    </div>
    <div class="modal-body">
      <!-- モーダルの本文 -->
    </div>
    <div class="modal-footer">
      <!--
        フッターはbutton要素を置く
        ex.: <button (click)="close()">Cancel</button>
      -->
    </div>
</div>

1. AppModuleにImport

Simple Modal Moduleをimportします。
app.module.ts
import { NgModule} from '@angular/core';
import { CommonModule } from "@angular/common";
import { BrowserModule } from '@angular/platform-browser';
import { SimpleModalModule } from 'ngx-simple-modal'; // 追加🍨
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    CommonModule,
    BrowserModule,
    SimpleModalModule // 追加🍨
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

また、冒頭のデモでは、モーダル以外をクリックしたり、escキーを押すことでモーダルを閉じたりできたかと思います。
そのようにオプションを変更したい場合は、以下のようにも定義できます。

imports: [
    ...
    SimpleModalModule.forRoot({container: 'modal-container'}, {...defaultSimpleModalOptions, ...{
      closeOnEscape: true,
      closeOnClickOutside: true,
      wrapperDefaultClasses: 'o-modal o-modal--fade',
      wrapperClass: 'o-modal--fade-in',
      animationDuration: 300,
      autoFocus: true
    }})
  ]

・項目名 ... 説明(型, デフォルト値)
・closeOnEscape ... escapeキー押下でモーダルを閉じる(boolean, false)
・closeOnClickOutside ... モーダル以外をクリックしてモーダルを閉じる(boolean, false)
・bodyClass ... モーダルが開いている間、bodyタグに付与するクラス(string, 'modal-open')
・wrapperDefaultClasses ... <simple-modal-wrapper>に付与するクラス(string, 'modal fade-anim')
・wrapperClass ... モーダルが開いている間、<simple-modal-wrapper>に付与するクラス(string, 'in')
・animationDuration ... モーダルの開閉にかける時間(number, 300)
・autoFocus ... モーダルを閉じた後に、フォーカスをモーダルが開く直前の状態に戻す(boolean, false)

* <simple-modal-wrapper>に関して
デフォルトのラッパーとして、モーダルはこのタグの配下に配置されます。

* wrapperDefaultClasses、wrapperClassに関して
前述で紹介した基本のCSSではデフォルト値が使用される前提でスタイリングされているので、通常はあまり変更する必要がないと思います。
以降で紹介するaddModal(), close()といったモーダルの仕組みだけ利用し、スタイリングは1から行いたい場合などに活用できそうです。

2. モーダルコンポーネントを作成

モーダルのコンポーネントを作成していきます。
HTML
<div class="modal-content">
    <div class="modal-header">
      <h4>{{title || 'Confirm'}}</h4>
    </div>
    <div class="modal-body">
      <p>{{message || 'Are you sure?'}}</p>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-outline-danger" (click)="close()">Cancel</button>
      <button type="button" class="btn btn-primary" (click)="confirm()">OK</button>
    </div>
</div>
TS
import { Component } from '@angular/core';
import { SimpleModalComponent } from "ngx-simple-modal";
export interface ConfirmModel {
  title:string;
  message:string;
}

@Component({
    selector: 'confirm',
    template: './confirm.component.html'
})
export class ConfirmComponent extends SimpleModalComponent<ConfirmModel, boolean> implements ConfirmModel {
  title: string;
  message: string;

  constructor() {
    super();
  }

  confirm() {
    this.result = true; //🍀
    this.close();
  }
}

ポイントは🍀マークのthis.result = true;です。
理由は後述します。

また、close()でモーダルを閉じることができます。こちらはモーダルコンポーネント自らで呼び出すことができるため、TS上だけでなくHTML上でclickイベントに直接割り当てるのもOKです。

3. 作成したコンポーネントをモジュールに登録

App.module.tsに作成したコンポーネントをimportします。

import { NgModule } from '@angular/core';
import { CommonModule } from "@angular/common";
import { BrowserModule } from '@angular/platform-browser';
import { SimpleModalModule } from 'ngx-simple-modal';
import { ConfirmComponent } from './confirm.component'; // 追加🍨
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent,
    ConfirmComponent  // 追加🍨
  ],
  imports: [
    CommonModule,
    BrowserModule,
    SimpleModalModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

* 公式ではentryComponentsに定義するようになっていますが、ver.9から非推奨となっているようです。
弊社ではdeclarationsに定義するだけで問題なく使用しています。
参考: https://dev.to/angular-jp/entrycomponents-53mo

4. 実際に使ってみる

モーダルを表示させたいコンポーネントに組み込みます。
例なのでapp.component.tsに組み込んでみます。

import { Component } from '@angular/core';
import { ConfirmComponent } from './confirm.component';
import { SimpleModalService } from "ngx-simple-modal";

@Component({
  selector: 'app',
  template: './app.component.ts'
})
export class AppComponent {
    constructor(private simpleModalService:SimpleModalService) {}

    showConfirm() {
      this.simpleModalService.addModal(ConfirmComponent, { // 🍀①
        title: 'Confirm title',
        message: 'Confirm message'
      })
      .subscribe(isConfirmed =>{ // 🍀②
        if(isConfirmed) {
          alert('accepted');
        } else {
          alert('declined');
        }
      });
    }
}

🍀①のaddModal()でモーダルを表示させることができます。
モーダルを使用する側でモーダルを閉じたい場合は、removeModal()removeAll()が使用できます。

また、ポイントは🍀②部分です。
「2. モーダルコンポーネントを作成」 でresultにセットした値はここで取得することができます。
resultにはbool値以外も使用することができますので、
Submitされたか」「"はい"が押されたか」「処理に成功したか」など
様々なフラグをカスタマイズするのもいいですね。

注意点

これは実際に私がハマったことなのですが、いくらsubscribeしているからといってresultの変更を常々検知できるわけではありません。
ngOnDestroy時にObserverbleとしてresultが返される仕組みだそうなので、resultを取りたい場合はモーダルを閉じる必要があります。

今回記事を書くにあたり公式を確認したところ、その旨記載がありました。
ハマったとき、公式をちゃんと確認する癖をつけなければ...!


補足

公式にはBootstrapと一緒に使う方法も載っていますので、「Bootstrapと完全に縁を切るのは難しいけど、モーダルをコンポーネントとして管理したいんだよなぁ...。」といった場合にもおすすめだと思いました。
https://www.npmjs.com/package/ngx-simple-modal#what-if-i-want-to-use-bootstrap-3-or-4


今回は公式の内容を参考に、Simple Modal Moduleについて紹介しました。
参考になればうれしいです。

Node.jsを使用して、S3上のPDFをマージする方法。

10月 29, 2020

オフィス狛 技術部のmmm(むー)です。

先日、業務でPDFのマージ作業が必要でしたので、その備忘録を残します。
マージ作業には、pdf-libというライブラリを使用しました。

pdf-libとは
pdf-libとは、PDFを作成・マージするためのnpmパッケージです。
今回はNode.jsで作業を行いましたが、ブラウザ側のJavaScriptでも使用することができます。また、PDFのマージだけではなく、実際にコードベースでPDFの作成もできるようです。
参考:https://www.npmjs.com/package/pdf-lib (公式)

【前提条件】
Nodeとnpmがインストールされていること。

1. pdf-libのインストール

使用したいプロジェクト配下に移動し、下記を実行します。
$ cd ~/<プロジェクトパス>
$ npm install --save pdf-lib

package.json に下記が追加されました。
// package.json

"dependencies": {
    "pdf-lib": "^1.11.1",
  }

2. S3からファイルを読み込む

< ... > 内は可変ですので、ご自身の環境の値を設定してください。
   const aws = require('aws-sdk');

// S3にアクセスするための情報
const s3Client = new aws.S3({
  accessKeyId: <accessKeyId>,
  secretAccessKey: <secretAccessKey>,
  region: <region>,
});

// バケットの情報 
// (バケット内にディレクトリを作成していない場合は、Keyにファイル名のみ指定してください)
const params = {
      Bucket: <bucketName>,
      Key: <s3DirectoryName>/<file.pdf>,
    };

// S3にある対象ファイル情報取得
const dataInS3 = await s3Client.getObject(params).promise();

// bufferデータを取得
const bufferData = dataInS3.Body;

// 💡 S3ではなく実行環境上にファイルがある場合は、下記のみでファイルの情報を取得できます
const fs = require('fs');
const file1 = await PDFDocument.load(fs.readFileSync('./file1.pdf'));


3. 読み込んだPDFをマージする

本来であれば複数のPDFをS3から読み込むと思いますが、今回は同じPDFファイルでマージします。
const fs = require('fs');
const { PDFDocument } = require('pdf-lib');

// マージ用の空PDF空を作成
const mergedPdf = await PDFDocument.create();

// マージ対象のファイル情報取得
const targetPdf = await PDFDocument.load(bufferData);

// マージ対象のファイルの全てのページ情報取得
const targetPdfPages = await mergedPdf.copyPages(
  targetPdf,
  targetPdf.getPageIndices(),
);

// 最初に作成した空のマージ用PDFに、マージ対象の全てのページを追加
for (const page of targetPdfPages) {
  mergedPdf.addPage(page);
}

// 💡 1ページだけ取得したい場合はこちら (下記例は、最初の1ページのみ取得)
const [targetPdfOnePage] = await mergedPdf.copyPages(
  targetPdf,
  [0],
);

// マージ用PDFに、1ページ更に追加
mergedPdf.addPage(targetPdfOnePage);

4. マージしたファイルを書き出す

ディレクトリを指定してファイルを書き出す場合、ディレクトリが存在しないとエラーになるので注意してください。
// PDFファイル書き出し
fs.writeFileSync('./merge.pdf', await mergedPdf.save());

5. マージしたファイルをレスポンスする

下記コードは、Expressを使用しています。
const pdfBytes = await mergedPdf.save();
const pdfbuffer = Buffer.from(pdfBytes);

res
    .attachment('merge.pdf')
    .set('Content-Type', 'application/pdf')
    .set('isBase64Encoded', true)
    .send(pdfBuffer)
    .toString('base64');

以上となります。
PDFをマージする必要がある場合、参考にしてみてください🍭

2020年10月27日火曜日

AWS Lambdaの関数コードでzipファイルをアップロードする。(DeprecationWarningの対応)

10月 27, 2020
オフィス狛 技術部のJoeです。

弊社のあるプロジェクトで、AWSのLambda関数でPOSTリクエストする機能があるのですが、
ある日、ログを確認すると以下のようなメッセージが出力されていました。

DeprecationWarning: You are using the post() function from 'botocore.vendored.requests'.  This dependency was removed from Botocore and will be removed from Lambda after 2021/01/30. https://aws.amazon.com/blogs/developer/removing-the-vendored-version-of-requests-from-botocore/. Install the requests package, 'import requests' directly, and use the requests.post() function instead.

【直訳】
非推奨警告:「botocore.vendored.requests」のpost()関数を使用しています。 この依存関係はBotocoreから削除され、2021/01/30以降にLambdaから削除されます。 https://aws.amazon.com/blogs/developer/removing-the-vendored-version-of-requests-from-botocore/。 リクエストパッケージをインストールし、「リクエストをインポート」して、代わりにrequests.post()関数を使用します。

このメッセージの対応として、Lambda関数のコードでzipファイルをアップロードして、
「botocore.vendored.requests」からPythonのRequestsモジュールに変更した手順をご紹介します。
(Windowsでの手順になります)


1.Pythonをインストール

公式サイトから、Pythonをダウンロードして、インストールしてください。
※今回はVersion 3.9.0 を使用しました

環境変数(Path)の追加をお忘れなく。
[インストールフォルダ]\Python39
[インストールフォルダ]\Python39\Scripts

2.pipでRequestsを指定のフォルダにインストール

適当な場所に作業用のフォルダを作成し、
コマンドプロンプトを起動し、作成したフォルダに移動してください。

以下のコマンドで、インストールします。
pip install requests -t .

成功すると、作業用フォルダの中に、以下のフォルダが作成されます。
bin
certifi
certifi-2020.6.20.dist-info
chardet
chardet-3.0.4.dist-info
idna
idna-2.10.dist-info
requests
requests-2.24.0.dist-info
urllib3
urllib3-1.25.11.dist-info

3.zipファイルの作成

Lambda関数にアップロードするzipファイルを作成します。
上記2のフォルダをzipファイルにするだけなのですが、注意点があります。

①「.dist-info」のフォルダは不要なので削除します。

②今回のように、zipファイルをアップロードするLambda関数に、既に関数コードを記述している場合、
アップロードしたタイミングで、消えてしまいます。

既存のコードを使用する場合、「2」で作成したの作業フォルダの中に、
新規で「lambda_function.py」ファイルを作成して、
既存の関数コードをコピーしてください。

上記①、②の対応をした場合、zipファイルの中身は以下のようになります。
bin
certifi
chardet
idna
requests
urllib3
lambda_function.py

4.zipファイルのアップロード

Lambdaの関数コードのアクションで、「.zip ファイルをアップロード」を選択して、
「3」で作成したzipファイルをアップロードします。

Environmentにzipしたファイルとフォルダが表示されていると思いますので、
これでPythonのRequestsモジュールが使用できます。

最後にimportを修正して、「Deploy」すれば完了です。
修正前:from botocore.vendored import requests
修正後:import requests



上記の対応で、ログから警告メッセージが消えて、問題なく動作しました。


2020年10月23日金曜日

Angular・リアクティブフォームにおけるdisabled属性の扱いについて。

10月 23, 2020

はじめまして、オフィス狛 技術部のpinoと申します。

今回は、Angularのリアクティブフォームにおけるdisabled属性の扱いについて書いてみたいと思います。

先日、Angularの実装中に以下のWarningに出会いました。
It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true
when you set up this control in your component class, the disabled attribute will actually be set in the DOM for
you. We recommend using this approach to avoid 'changed after checked' errors.

Example:
form = new FormGroup({
     first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
     last: new FormControl('Drew', Validators.required)
});

(かなり砕いた感じに)意訳すると、リアクティブフォームを使っているのに、disabled属性をHTMLに直に設定していませんか?例の書き方ならdisabled属性をDOMで設定することができます!といった具合でしょうか。

Warningなので最悪このままにしておいてもいいかもしれませんが、できることなら解消しておきたいですよね。

上記のWarningは、文言に忠実に従うことで回避できます。
以下、応用も含めて例を紹介します!

Formの定義

修正前は、以下のFormを定義していることにします。
(わかりやすく、Warningの文中にあった例を流用したいと思います。)
HTML
... // 中略

  <form [formGroup]="form">
    <input type="text" disabled formControlName="first">
    <input type="text" formControlName="last">
  </form>

...
TS
... // 中略

  form = new FormGroup({
    first: new FormControl('Nancy', Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

...
FormBuilderを使用する場合
... // 中略

  form = this.fb.group({
    first: ['Nancy', Validators.required],
    last: ['Drew', Validators.required]
  });

  constructor(private fb: FormBuilder) { }

...
* FormBuilderを使用する場合は、DIが必要です。 以降は、記述を省略します。

Warningの出る書き方

HTMLで以下のようにdisabled属性を設定していると、上記のWarningが出ます。
例)
  // ①HTMLの書き方そのまま
  <input type="text" disabled>

  // ②バインディング構文使用、設定値は固定
  <input type="text" [disabled]="true">

  // ③バインディング構文使用、設定値は可変
  <input type="text" [disabled]="form.invalid">

③もダメなんですね...。


解消方法

1. 基本に従う
Warningの文言にある通りに書き換えます。
HTML
... // 中略

  <form [formGroup]="form">
    <input type="text" formControlName="first">
    <input type="text" formControlName="last">
  </form>

...
TS
... // 中略

  form = new FormGroup({
    first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

...

HTMLからdisabledの記述を無くし、
コンポーネント側に {value: 'Nancy', disabled: true} という記述を増やしました。
ちなみにdisabled: trueで非アクティブ、disabled: falseでアクティブとなります。

FormBuilderを使用する場合は、以下となります。
... // 中略

  form = this.fb.group({
    first: [{value: 'Nancy', disabled: true}, Validators.required],
    last: ['Drew', Validators.required]
  });

...
こちらも、{value: 'Nancy', disabled: true} を追記すればOKです。

2. 動的に設定する
動的に切り替えたい時の方法です。
HTML
... // 中略

  <input type="text" formControlName="first">
  <input type="text" formControlName="last">
  <input type="checkbox" (click)="switchDisabled()">チェックするとfirstが非アクティブになります

...
TS
... // 中略

  isDisabled = true;

  form = this.fb.group({
    first: [{value: 'Nancy', disabled: this.isDisabled}, Validators.required],
    last: ['Drew', Validators.required]
  });

...

  switchDisabled() {
    if (this.isDisabled) {
      this.form.controls.address.enable();
    } else {
      this.form.controls.address.disable();
    }
    this.isDisabled = !this.isDisabled;
  }

...

切り替え用のチェックボックスを置いてみました。
ポイントは、コンポーネント側のenable()disable()です。
これにより、アクティブ・非アクティブを切り替えることができます。

今回はチェックボックスを使用しましたが切り替えるためのトリガーはなんでもOKですし、
切り替えが必要ない場合は、ngOnInit()に処理を置いて初期状態の判断に使用するのも良さそうです!

補足

ここまで説明してきましたが、実はdisabledをこのように指定しなくてもWarningが出ない要素もあります。
今のところそのように把握しているのは、button要素とoption要素です。

さらに、前述したWarningの出る書き方の例でいうと

button要素 → ①, ②, ③ どの書き方をしても怒られない
option要素 → ①は怒られる。②と③なら怒られない

という結果になりました。

この辺りは、余裕ができたらもう少し詳しく調べてみたいですね。


今回はここまで。
簡単な内容だったと思いますが、参考になれば嬉しいです。