AngularでKonvaを使ったら、Buildで警告がでていました。

こんにちは。石川さんです。最近更新できていなかったので、中途半端ですが、最近の成果について記載します。AngularでKonvaを使ってTMのツールを作っているのですが、何やら警告が出ていたので、調べました!

出てきた警告は

「Warning: C:\……\xxx.component.ts depends on ‘konva’. CommonJS or AMD dependencies can cause optimization bailouts.」
なんか出ました。「ng serve」を実行すると、勝手にビルドが始まって、開発用のサーバーが起動するのですけど、ビルド終了時に上記のメッセージが出てまいりました。結構前から出てきていたように思うのですけど、まあこんなものなのかな、と、放置していました。ただ、システム開発をとことん極めます、と言っている人は、これを放置しちゃいけませんよねぇ、という気持ちになってきたので、調べます!

すぐ下に続きのメッセージが出ていましたので、まずはこちらを参照します。
「For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies」
なるほど、AngularaはCommonJSに依存することをおすすめしてないのですね。最適化されなくて、サイズが小さくならなくなることがあるので、ECMAScript(ES)モジュールにした方がよい、ということだそうです。

警告を消すだけなら

なるほど、angular.jsonファイルの中の「build」オプションの「allowedCommonJsDependencies」に「konva」を追加すれば、警告は消せそうですね。ちょっとやってみます。

既に「”allowedCommonJsDependencies”: [“dialog-polyfill”],」となっていたので追加してみます。「”allowedCommonJsDependencies”: [“dialog-polyfill”,”konva”],」に変更して保存、「ng serve」実行中だったのでCtrl+cでいったん終了して、再度「ng serve」を実行してみます。
お、警告が消えましたね。

もう少し調べます

警告が消えましたが、本当はKonvaをESモジュールにした方がよいのでしょうね。と、いうことで調査しました。しかし、Konvaのバージョン8.0.0で、「Full migration to ES modules package (!), commonjs code is removed.」と、ESモジュール対応されているようでした。と、いうことは、よくわかりませんが、とりあえずは警告を消しておけば大丈夫、かな?

まとめ

もっと突っ込んで調べようとしましたが、挫折しました。本来は、KonvaがESモジュールとして使えるはずなのに、Angularのビルダーが、CommonJSモジュールじゃないのかなぁ、と警告を出しているように思います。と、いうことでいったん終了です。

デプロイしたら挙動が違う!?どうしよう?

 今日も見に来て下さってありがとうございます。石川さんです。ここのところTMのツール開発しているのですが、ローカルで作成したときに確認した挙動と、サーバーにデプロイしたプログラムの挙動が違っています。どうしてなのか、調べてみました。
結論を言うと、原因は突き止めて解決しましたが、理由までは分かりませんでしたよ。。。

違いは…

まずは、ローカル側の挙動です。箱のデータはデータベースに書けるようになったので、線を引こうとまずは箱を探して、一つ目の箱から二つ目の箱へ1:Nのゼロありの線を引くようにしました。まだ作りかけなので、線は青いです。

ローカル側 想定通り動いています

次に、サーバー側の挙動です。一つ目の箱は右のサイドから、二つ目の箱の左のサイドへ線を引く、という設定になっているはずなのですが、なぜか両方共の箱の上のサイドになってしまいます。プログラムはまったく同じ状態のはずなので、謎です。

サーバー側 想定と少し異なります

そして、この接続点は移動できるように作っているので、サーバー側のプログラムで同じ位置になるように移動してみたところ、変な位置になってしまいました。なんてこったい。

サーバ側 接続を移動して見たところ やはり想定と異なります

いやいや、これ解決できるのかなぁ。。。

使える武器は…

Web開発の実務経験もセミナーなどで教わったこともなくて、すべて独学なので、こういうときに使える武器が全くないのですよね。ブラウザ(Chrome)にデバッガはついているのですけど、サーバー側にデプロイした状態だと、Typescriptのソースコードはjavascriptに変換されてしまっていて確認できなくなっているのですよね。と、いうことで、console.log()しか思いつきません。ま、あるものでやるしかないですよね。と、いうことで、まずは、Sideの情報と、1:NのときのNの情報がうまく処理できていないようですので、どうなっているのかログを出力してデプロイすることにします。

と、ソースコードを見始めましたが、使っているHTML5 Canvas JavaScript フレームワーク(KONVA)の使用方法が原因のようです。表示する図形、この場合は、接続点ですが、それぞれSideと1:Nの設定値のバリデータを手作りしていましたので、ログ出力する前に、ここのバリデータを使用しないように変更して、ビルド、デプロイしてみました。

すると、、、なんと、正しく動作しました!
ズバッと解決、、、は、しましたが、、、気持ち悪いですねぇ。

ソースコードの違い

原因となった個所の、修正前のプログラムと修正後のプログラムを参考までに載せておきます。接続点の最大値を、Infinityにしたのですが、KONVAではInfinityは数値ではないようでバリデータがなかったため、KONVAのValidatorを参考にして、以下のように手作りしてみました。

-- 修正前(ローカルは動くが、サーバ側は正しく動かない)
Factory.addGetterSetter(TMConnectionPoint, 'maximum', "",
  function(val:any,attr:any){
      if (Konva.isUnminified) {
          if (!(Util._isNumber(val) || val === Infinity)) {
              Util.warn('[' + val.toString() + ']' +
                  ' is a not valid value for "' +
                  attr +
                  '" attribute. The value should be a number or an Infinity.');
          }
          return val;
      };
  });

ビルドしてデプロイしたらローカルと挙動が変わってしまったので、KONVAのソースコードと同様となるように作り直しました。get~Validatorというファンクションをつくって、それをセットする、というやり方でしたのでそちらに習ってみました。違いとしては、Konva.isUnminifiedのフラグをチェックするタイミングです。タイミングの違いによって、挙動が変わったのかもしれません。JavaScript初心者なので、これ以上追っていくと時間がいくらあっても足りないので諦めます。(あ、AngularはTypeScriptでしたね。どっちにしても初心者でした。。。)上記のスクリプトではundefinedを戻していないよ、という、エラーは出ていなかったのですが、ファンクションを切り出したところでエラーが出てきたので追記しました。もしかしたらこの程度の些細な差、なのかも知れません。

-- 修正後(ローカルもサーバ側も正しく動作する)
function getNumberOrInfinityValidator(){
  if (Konva.isUnminified) {
    return function(val:any,attr:any){
          if (!(Util._isNumber(val) || val === Infinity)) {
              Util.warn('[' + val.toString() + ']' +
                  ' is a not valid value for "' +
                  attr +
                  '" attribute. The value should be a number or an Infinity.');
          }
          return val;
      };
  }
  return undefined
}
Factory.addGetterSetter(TMConnectionPoint, 'maximum',"", getNumberOrInfinityValidator());

まとめ

Web開発では、ローカルでうまくいったからと言って、サーバにデプロイしてもうまく動くとは限らない、ということを知りました。そして、そのような事態になったときは、とても心細いですね。

Angular始めました – リアクティブフォームを使ってみる。そして、stackblitz.comのプログラムを組み込んでみる

 今日も見に来てくださってありがとうございます。石川さんです。
先日に引き続き、Angularを勉強しています。今回はリアクティブフォームを使って、サンプルプログラムを作ってみました。

出来上がりイメージ

  今回は、たまたまニュートン-ラフソン法について質問があったので、Angularで実装してみました。こんな感じになります。ルートを求めたい値「k」を入力して、Calcボタンをクリックすると、漸化式を繰り返します。二乗した結果が「k」との誤差0.01未満になったら終了します。

ニュートン-ラフソン法の実行結果

 今回も、statsblitz.comを利用してみました。作業中に、デフォルトで用意されているコンポーネントの「hello.component.ts」を削除してみたところ、以下のエラーがでました。しばらく解決できなくて、何かの設定があるのかと、ウロウロしてしまったので、回避策をメモしておきます。

Error in src/app/hello.component.ts (1:1)
File '/~/src/app/hello.component.ngtypecheck.ts' not found.

 何のことはありません、おかしな状態になっているだけ、ということのようでした。画面のプロジェクトエクスプローラーの左下の「DEPENDENCIES」をポイントすると、くるりとなった矢印が登場するので、そちらをクリックしたところ、しばらく待って、エラーが消えました。

DEPENDENCIESの解決

ソースコード

ソースコードは以下のとおりです。

まずは、コンポーネントのスクリプトです。リアクティブフォームのポイントは「FormGroup」ですね。TypeScript側で「myGroup」として定義しています。また、今回はFormBuilderを使って「k」を定義しました。また、Validatorsを使って、正の数をチェックするようにしました。
onSubmit()呼び出し時、漸化式で計算を実行して、二乗した結果がkに近づいて差が0.01を下回ったところで処理を完了します。

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-x2',
  templateUrl: './x2.component.html',
  styleUrls: ['./x2.component.css'],
})
export class X2Component implements OnInit {
  myGroup: FormGroup;
  results: string[];
  guess: number;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myGroup = this.fb.group({
      k: [24, Validators.min(0)],
    });
  }

  get k() { return this.myGroup.get('k'); }

  onSubmit() {
    console.log('Executed!')
    this.results = [];
    let k: number = this.k.value;
    let epsilon: number = 0.01;
    let guess: number = k / 2;
    let newGuess: number;
    while (Math.abs(guess * guess - k) >= epsilon) {
      newGuess = guess - (guess ** 2 - k) / (2 * guess);
      if (this.results.length == 0) {
        this.results.push(guess.toString() + ' => ' + newGuess.toString());
      } else {
        this.results.push(' => ' + newGuess.toString());
      }
      guess = newGuess;
    }
    this.guess = guess;
  }
}

 そして、コンポーネントのHTMLファイルです。

<p>
ニュートン-ラフソン法でkの根を求めます。
</p>
<p>
f(x) = x<sup>2</sup> - k<br>
x<sub>n + 1</sub> = x<sub>n</sub> - f(x) / f'(x) = x<sub>n</sub> - (x<sub>n</sub><sup>2</sup> - k)/2x<sub>n</sub>
</p>
<form [formGroup]="myGroup" (submit)="onSubmit()">
  <table>
    <tr>
      <th>k</th>
      <td><input type="number" formControlName="k"></td>
    </tr>
    <tr>
      <th></th>
       <td *ngIf="k.invalid" [style.color]="'red'">正の数を入力してください。</td>
    </tr>
    <tr>
      <th></th>
      <td>
        <input type="submit" value="Calc" [disabled]="myGroup.invalid">
      </td>
    </tr>
  </table>
</form>
<ul>
  <li>まず、guess = k / 2 = {{k.value}} / 2 = {{k.value / 2}} とします。<br>guess - (guess<sup>2</sup> - {{k.value}})/ (2 × guess)がより近い値になるので、計算結果の誤差が0.01になるまで繰り返します。<br>
    <p *ngIf="!results">Calcを押して続行します。</p></li>
  <li *ngFor="let ans of results">{{ans}}</li>
</ul>
<p *ngIf="guess">{{guess}} * {{guess}} = {{guess * guess}}</p>

WordPressへの組み込み

 stackblitz.comを見ていると、「Share」メニューがあったので、クリックして見ると、「Embed」タブが出てきましたので、WordPressに組み込めるのではないか、と、調べて組み込んでみました。

 組み込みブロックはあるのですが、stackblitz.com用のものがなく、調べているとここに書いてありました。iframeタグを使って、ということだったので、実験してみましたところ、以下のとおり、実行できました!

 ただ、編集のプレビュー画面では「Calc」ボタンは動きませんでした。iframeではsubmitが無効になっているようです。ただ、確認画面では動作しました。こちらの原因はまた機会があったら調べてみます。公開された後に実行できるかどうかわかりませんので、stackblitz.comのソースコードはこちら、実行結果はこちらです。

まとめ

 Angularのリアクティブフォームを使って、ページを作ってみました。

Angular始めました – stackblitz.comを使ったAngularの簡単な紹介

 今日も見に来てくださって、ありがとうございます。石川さんです。

 先日「Angularはじめました。」ということで、「AngularによるモダンWeb開発 基礎編 第2版」を読み込んで簡単な仕組みを作ってみよう、と、意気込んでいましたがずいぶん経ってしまいました。まずは、この書籍のサンプルをダウンロードして動作確認しようと取り組んでみたところ、もろもろ躓きまして。この本、初学者にはおすすめできませんね。あ、ぼくはすぐに何とか解決しましたよ。プロですからね。エヘン!でも、ちょっと初心者には難しかったかも知れない、と、追加の書籍を二冊購入して色々と勉強していました。学習効果が高いのはアウトプット、ということは分かっているのですが、どうにも本が好きなのでしょうねぇ。すぐに次の本、と買ってしまい割とコレクターみたいなところがあります。で、今日は、Angularってどんなものなの、ということを簡単に紹介したいと思います。そうそう、アウトプットです!

Angularとは何か、そのセットアップについて

 一言でAngularとは何か、というと、ステキなWebアプリを開発するためのフレームワーク、と言ってよいのかな、と、思います。ステキなWebアプリと書きましたが、流行の専門用語ではこれをPWA(Progressive Web Application)と呼んでいて、デスクトップアプリケーションのような体験ができるWebアプリ、ということのようです。これらはGoogleが開発を進めているオープンソースのフロントエンドフレームワークで、半年に一度はメジャーバージョンが更新される、という活発な開発状況です。書籍の数が比較的少ないのは、この頻繁なバージョンアップのせいではないか、ということをぼくは勝手に疑っています。

 ローカルで開発するためには、node.jsというjavascriptを実行するための環境を用意する必要があります。なので、まずはnode.jsをインストールします。node.jsのインストールが終わったら、そのnode.jsの中のインストールコマンドで、Angularをインストールするということになります。コマンドプロンプト、またはターミナルから以下のコマンドを実行してください。

npm install -g @angular/cli

これで、Angularがインストールされたことになります。その後、プロジェクトを作るために、任意のフォルダで以下のコマンドを実行することで、新しいプロジェクトのためのフォルダ(myProject)を作成します。

ng new myProject

開発は、ここから「ng serve」を実行して、作られた結果をブラウザで確認しながら、という感じで進められていくことになります。結構面倒ですよね。もっとお手軽にどんなものか知りたい、というAngular初心者の方々のために、stackblitz.comが便利なので紹介したいと思います。

stackblitz.comを使ってみましょう

 stackblitz.comはブラウザで利用できる統合開発環境(IDE)のクラウドサービスのひとつです。ホームページを見てみるとわかりますが、いくつかの開発に対応しています。今回はAngularですので、以下のアイコンを探してクリックしてみてください。

stackblitz.comのAngular開発環境入口

 すると、ブラウザの中に統合開発環境(IDE)がAngularの新規プロジェクトを開いた状態で開始します。左がフォルダとソースコードの一覧で、真ん中がソースコード、右がAngularを実行した結果、という感じです。

stackblits.comのAngular開発環境(初期画面)

 Angularでは基本的に、コンポーネントという部品の単位で開発することになります。初期表示では「app.component.ts」ファイルが開かれています。このファイルがコンポーネントの処理部分を担います。拡張子が「.html」のファイルが表示内容の骨格部分、「.css」が表示内容の飾りの部分、という感じで考えていただければ、と思います。

2022年2月3日 追記
 stackblitz.comにて本日再び新しいAngularプロジェクトを作成してみたところ、Angularのバージョンが13になっていました。保存してあるリポジトリもバージョンアップしましたので、以降の内容は12→13で読み替えるようお願いいたします。たしかこのバージョンは、半年に一回は更新されるんだよね。。。

 「Start editing to see some magic happen :)」と記載がありますので、ここの部分、さっそく直してみましょう。まずは、日本語使えるのかな、ということでこの部分を日本語にしてみます。左側の「app.component.html」ファイルをクリックして中央に開きます。英語部分を日本語にしてみましょう。

リアルタイムで修正が反映される

 HTMLファイルを編集すればわかりますが、修正と同時に右側のアプリケーション部分が更新されます。日本語もちゃんと表示されましたね。

画面遷移なしで使える機能を盛り込んでみた

 リアクティブフォーム以外で画面遷移しない範囲の簡単な項目をもろもろ盛り込んでみました。完全に自分用の備忘録です。忘れたときに参照しようと思って書いていますので、説明は省きたいと思います。出来上がりイメージは以下のとおりです。

できあがりイメージ

ソースコードは「app.components.ts」と「app.components.html」の二つだけ変更しました。以下の通りです。

import { Component, VERSION } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular ' + VERSION.major;
  today = new Date();
  clicked: boolean;
  message: string;
  count: number = 0;
 list: string[] = [
    "AngularによるモダンWeb開発",
    "Angular超入門",
    "Angularデベロッパーズガイド",
  ];
  ch1: boolean = false;
  ch2: boolean = false;
  ch3: boolean = true;
  opt: string;
  sel: string;
  mul: string;

  onClick() {
    this.clicked = !this.clicked;
  }

  keyup(val: string) {
    this.message = val;
  }

  countup() {
    this.count++;
  }

}
<hello name="{{ name }}"></hello>
<p>
  編集を開始して、魔法が起こるのを確認してください☺
</p>
<ol>
  <li>変数が利用できます。name = 「{{name}}」</li>
  <li>計算もできます。3 + 3 = {{3 + 3}}</li>
  <li>日時を表示できます。{{today | date: "YYYY-MM-dd hh:mm:ss"}}</li>
  <li *ngIf="clicked" (click)="onClick()" [style.background-color]="'cyan'">クリックに反応します。A</li>
  <li *ngIf="!clicked" (click)="onClick()" [style.background-color]="'lightblue'">クリックに反応します。B</li>
  <li><input type="text" #f1 (keyup)="keyup(f1.value)">入力を下に反映します。</li>
  <li>↑の入力をここ「{{message}}」に瞬時に反映させます。</li>
  <li>{{count}}回クリックしました。<button (click)="countup()">Click</button></li>
  <li>ngForで繰り返し処理ができます。</li>
  <ol>
    <li *ngFor="let item of list;let i = index">{{item + ":i = " + i}}</li>
  </ol>
  <li>チェックボックスが使えます。
    <input type="checkbox" [(ngModel)]="ch1">
    <input type="checkbox" [(ngModel)]="ch2">
    <input type="checkbox" [(ngModel)]="ch3"></li>
  <ul>
  <li *ngIf="ch1">一つ目がチェックされています。</li>
  <li *ngIf="ch2">二つ目がチェックされています。</li>
  <li *ngIf="ch3">三つ目がチェックされています。</li>
  </ul>
  <li>ラジオボタンが使えます。
    <label><input type="radio" [(ngModel)]="opt" value="男">男</label>
    <label><input type="radio" [(ngModel)]="opt" value="女">女</label>
    <label><input type="radio" [(ngModel)]="opt" value="不明">不明</label></li>
  <ul>選択値は「{{opt}}」です。</ul>
  <li>プルダウンが使えます。
  <select [(ngModel)]="sel">
    <option *ngFor="let item of list">{{item}}</option>
  </select></li>
  <ul>選択値は「{{sel}}」です。</ul>
  <li>複数選択リストが使えます。
    <select [(ngModel)]="mul" multiple size=3>
      <option *ngFor="let item of list">{{item}}</option>
    </select>
  </li>
  <ul>選択値は「{{mul}}」です。</ul>
</ol>

stackblitz.comとgithub.comで共有できます

  stackblitz.comは、github.comと連携することでソースコードを保存することができます。そして、保存すると他の人と共有することが可能になります。今回はこちらがソースコードの共有です。そして、こちらが実行結果の共有です。

まとめ

 Angularを使うと、これまで簡単にできなかったことが、そこそこ簡単にできるようになりました。

Angular始めました – モダンWeb開発 PWA(Progressive Web Application)始めますよ~!

 今日も見に来てくださって、ありがとうございます。石川さんです。

 ご縁があって、近々Webシステムの開発をすることになるかも知れない、ということになりましたので、諸々検討して、Angularの勉強を始めることにしました。

Angularによる モダンWeb開発

 Webシステムが出始めた当時、4GLの開発をしていたこともあって、あの複雑なだけで開発生産性が高くもなく、操作性の少しも良くない仕組みがどうしても好きになれず、ずっと避けてきていたのですよね。そう、もっさりとしていて遅いし、ユーザーインターフェースも雑で細やかさがなくて、嫌だったのですよね。アプリケーションのインストールがない、というメリット以外には良さを感じられなかったのです。様子が変わってきたのはGoogle Mapあたりからで、それまでのWebアプリケーションらしからぬ動きはかなりの衝撃でしたね。そして、GmailにGoogleドキュメントと、かつてのWebアプリケーションとは違う感じになってきているなぁ、というのは知っていましたが、とうとうWebアプリケーションに手を出す決意をしました。

 と、いうのも、Angularのホームページのチュートリアルに感動して、これは本格的に取り組まねば、という気持ちになりました。まず、実行できるチュートリアルがすべてWebで完結しているのですよね。Visual Studio CodeのようなIDEが自動的に開始します。先日gitPodでも体験していましたがブラウザだけで開発できるのは素晴らしいですね!そして、コンポーネント指向開発という新しい開発方法もなかなか面白いですね。部品が多くなり過ぎたら再利用が難しくなるかも知れないなぁ、という気持ちになりましたが、もうちょっとやれば、何かつかめるかも知れませんね。

 それと、Comppassの「Angular日本ユーザー会のYoutubeライブ!」を視聴しました。予想通りですが、まったくついていけませんでした!まだ開発もしてないですからねぇ。Angularのバージョン13リリースに関して、追加、更新、削除された機能について、語られていました。現状がまったくわからないので、まあついていけないのは当然ですね。参加して、引っ掛かりができるようになれば、という感じです。

 Angularのホームページと、画像の書籍「AngularによるモダンWeb開発 基礎編 第2版」を読み込んだら、簡単な仕組みを作ってみようと思います。

 さあ、がんばるぞー!