Next2D FrameworkのViewとViewModel

設定ファイルは準備できましたので、実際の表示をコントロールする部分を紹介したいと思います。

設定ファイルは以下の記事を参考にしていただければと
- [Next2D Framework [config設定]](https://qiita.com/ienaga/items/2e96a9c588bae1bacc24)
- [Next2D Frameworkのrouting設定[導入編]](https://qiita.com/ienaga/items/3c2d9175d3797621ee2e)
- [Next2D Frameworkのrouting設定[カスタム設定編]](https://qiita.com/ienaga/items/0c5eabedc8e329e033bb)
- [Next2D Framework routing設定[NoCode Toolの連携編]](https://qiita.com/ienaga/items/99511ee77765cf6ea9ac)

viewの管理は1シーンに1View、1ViewModelが基本スタイルです。
ディレクトリ構成はキャメルケースの最初のブロックで作成するのが基本スタイルです。

# シーン作成
サンプルとして、Topページを作成したいと思います。

## 事前技術共有
localhostで起動する場合、`mock`ディレクトリがサーバーの役割を担います。
`mock`ディレクトリにjsonやapiのレスポンスデータを格納する事で擬似的なAPI通信を行う事ができます。

## routing.json

```json:routing.json
{
  "top": {
    "requests": [
      {
        "type": "json",
        "path": "{{ api.endPoint }}api/top.json",
        "name": "TopText"
      },
      {
        "type": "content",
        "path": "{{ content.endPoint }}content/sample.json",
      }
    ]
  }
}
```

この場合、`src/view`に`top`という名前でディレクトリを作成し、`TopView.js`と`TopViewModel.js`を作成します。

## コマンドで作成
複数のViewクラスやViewModelクラスを生成する際は、以下のコマンドで自動生成する事をお勧めします。
このコマンドは対象のディレクトリがなければディレクトリを作成し、ViewとViewModelが存在しない場合は新規でクラスを生成します。

```linux
npm run generate
```

# Viewクラス
メインコンテキストにアタッチされるコンテナの役割を担うのがViewクラスの役割です。
その為、記述は至ってシンプルで、routingで設定した値のキャメルケースでファイルを作成し、`next2d.fw.View`を継承するのが基本のスタイルです。特殊な用件がない限り、Viewでロジックを組む事はありません。

```javascript:TopView.js
/**
 * @class
 * @extends {next2d.fw.View}
 */
export class TopView extends next2d.fw.View
{
    /**
     * @constructor
     * @public
     */
    constructor ()
    {
        super();
    }
}
```

# ViewModelクラス
表示の際は、`bind`関数がコールされ、ページ遷移する際は`unbind`関数がコールされます。
Viewに任意のDisplayObjectをbindするのが、ViewModelの役割です。
コアなロジックなどは`src/model`で管理し、UIなどのは`src/component`で管理する事を推奨していますが、こちらのデザインパターンはまだ模索中です。

```javascript:TopViewModel.js
import { TopContent } from "../../content/top/TopContent";

/**
 * @class
 * @extends {next2d.fw.ViewModel}
 */
export class TopViewModel extends next2d.fw.ViewModel
{
    /**
     * @param {next2d.fw.View} view
     * @constructor
     * @public
     */
    constructor (view)
    {
        super(view);
    }

    /**
     * @param  {next2d.fw.View} view
     * @return {void}
     * @abstract
     */
    bind (view)
    {
        new Promise((resolve) =>
        {
            const { Event } = next2d.events;

            // NoCode Toolで作成したJSONから動的に生成する
            const topContent = view.addChild(new TopContent());

            // フレーム移動を監視する
            topContent.addEventListener(Event.ENTER_FRAME, (event) =>
            {
                const content = event.currentTarget;
                if (content.currentFrame === content.totalFrames) {
                    view.button.visible = true;
                    content.removeEventListener(Event.ENTER_FRAME, event.listener);
                }
            });

            // 中央表示
            topContent.x = this.config.stage.width  / 2;
            topContent.y = this.config.stage.height / 2;

            resolve(topContent);
        })
            .then((top_content) =>
            {
                return new Promise((resolve) =>
                {
                    const { MouseEvent } = next2d.events;
                    const { MovieClip } = next2d.display;
                    const { TextField, TextFieldAutoSize } = next2d.text;

                    // click button
                    const button = view.addChild(new MovieClip());

                    // 名前をつけて、非表示にする
                    button.name       = "button";
                    button.visible    = false;
                    button.buttonMode = true;

                    button.addEventListener(MouseEvent.MOUSE_UP, () =>
                    {
                        this.app.gotoView("home");
                    });

                    // APIで取得したテキスト情報をTextFieldクラスにセットする
                    const textField    = button.addChild(new TextField());
                    textField.autoSize = TextFieldAutoSize.CENTER;
                    textField.text     = this.response.get("TopText").word;

                    // 中央表示
                    textField.x = this.config.stage.width / 2 - textField.width / 2;
                    textField.y = top_content.y + top_content.height / 2 + textField.height;

                    resolve();
                });
            });
    }
}
```

[Next2D Framework routing設定[NoCode Toolの連携編]](https://qiita.com/ienaga/items/99511ee77765cf6ea9ac)の記事で作成したJSONファイルを読み込み、ViewModelで動的に生成します。今回は、`TopContent`を生成してTopViewにアタッチします。
`TopContent`を生成し、マイフレームのフレーム移動を監視し、最終フレームに移動したタイミングで、別のAPIから取得したテキスト情報の非表示を解除します。

これでTOPシーンは完成です。
テキストをクリックもしくはタップすると次のシーンに移動します。
明日は、シーン遷移のついて記事を書きたいと思います。