# JavaScript 応用編4(学習日:10/13,14 ToDOアプリの作成) ## ToDoアプリの作成 ### 1. エントリーポイント エントリーポイントはモジュールスコープ(Script要素ごとに持つスコープ)で行う。別スクリプトで別のJavaScriptを読み込むと、モジュール同士の連携はできなくなる。 <実習> 1. todoappフォルダにindex.htmlとindex.jsを作成する。 2. todoappフォルダ内にsrcフォルダを作成し、フォルダ内にApp.jsを作成する。 3. App.jsの内容を、index.jsでインポートし読み込む。 ### 2. アプリの構成要素 今回は以下の4機能を作成する。 * Todoアイテムを追加する。 * Todoアイテムを更新する。 * Todoアイテムを削除する。 * Todoアイテム数を表示する。 また、今回はCSSを利用して、デザイン性も意識する。 <実習> 1. index.htmlにCSSの読み込み設定を記述する。 2. 同様にクラス属性class=todoappを設定する。(CSSの装飾の目印として利用する。)→.todoapp{}でCSSの属性値を指定できる。 3. 同様にid属性をCSSやJavaScript等からの参照先として設定する。 4. メイン処理部分にid=js-todo-listをdiv要素で作成する。 5. Todoアイテム数表示部分にid=js-todo-countをspan要素で作成する。 ### 3. Todoアイテムを追加する 入力欄にTodoアイテム名を入力し、Todoアイテムを追加する処理を実装する。 <実習> 1. form要素のsubmit(送信)をチェックし、キャッチする。 イベントsubmitの発生は、addEventListenerメソッドでキャッチできる。 メソッド内には、イベントリスナーと呼ばれるコールバック関数を呼び出す。 コールバック関数内には、URLにsubmitが送信される動作を止めるevent.preventDefault()を使用する。(ページがリロードされるのを防ぐため) 2. 入力内容をTodoリストに表示する。 HTML文字列からHTML要素を作成するユーティリティモジュールを作成する。 srcフォルダ内にviewフォルダを作成し、html-util.jsを作成する。 HTML文字列からの変換部分は、template要素を用いてHTML要素(element)に変換する。その後、elementをDOMの形式に変換する。 コンテナ要素の中身のリセットは、関数renderを用いて子要素をすべて消し、改めてappendChildで子要素として追加する。 App.jsを更新し、html-util.jsからインポートした内容を使用できるようにする。 ### 4. イベントとモデル #### 3.で作成したアプリの問題点 Todoアイテムを更新するためには、Todoアイテムを識別する必要がある。 3.ではDOMで直接HTMLを記述するため、アイテムの個数や状態がDOM上の表記でしか分からない。 したがって、HTML記述時にTodoアイテムの情報(タイトルやid(識別子)等)を埋め込む必要がある。しかし、HTMLは文字列型のみという制約があり、また、リストとカウント数を両方更新しなければならないため、処理が複雑化しやすい。 #### JavaScriptクラスを作成する そこで、TodoアイテムをJavaScriptクラスとしてモデル化を行う。クラスからインスタンスを参照すれば、HTMLの文字列制約を無くせる。また、更新はフォームが送信されたタイミングではなく、リストのモデルが変化した際に行う事で、処理が簡潔化する。 この場合、モデルの変化というイベントに対応する必要がある。リストが変更を表すchangeイベントを自分自身に向けて発生(ディスパッチ)させることで、そのイベントをキャッチし更新を実行する。この仕組みは更新機能にも応用できる。 **☆具体的な処理の流れ(追加機能・修正版)☆** 1. Todoアイテムを入力し、Enterで送信(submit)する。 2. submitイベントをキャッチし、JavaScriptクラスでリストの追加処理を行う。 3. 追加処理に対しchangeイベントが発生し、キャッチしたら、DOMでHTML文を作成し更新する。 #### EventEmitterクラスを作成する イベント処理を一括でまとめたEventEmitterクラスを作成し、更新機能でも同様の処理ができるようにする。TodoリストモデルがEventEmitterを継承することで、イベントの仕組みが実現する。 EventEmitterでは、ディスパッチ側(発生側)とリッスン側(キャッチ側)の機能を併せ持つ。したがって、イベントが発生した際にこのEventEmitterを呼び出すことで、処理がクラスを通じて行われる。 <実習> 1. EventEmitter.jsをsrcフォルダ内に作成する。 2. EventEmitterクラスを定義する。(ディスパッチ側→emitメソッドで、登録したコールバック関数を呼び出す。リッスン側→addEventListenerで任意のコールバック関数を登録できる。リスナー関数の登録はSetで、イベントとリスナー関数の管理はMapで行う。これらの登録・削除機能も備える) #### TodoListModelクラスを作成する EventEmitterを継承した、TodoListModelクラスと、各Todoアイテムを表現するTodoItemModelクラスを作成する。 <実習> 1. modelフォルダをsrcフォルダ内に作成する。 2. TodoItemModel.jsをmodelフォルダ内に作成する。 3. TodoItemModelクラスには、タイトル、アイテムの完了状態、ユニークな識別子(id)を持たせる。 #### モデルを使って表示を更新する 以上2つのモデルを用いて、以下の通りApp.jsを変更する。 <実習> 1. コンストラクタでTodoListを初期化する。 2. TodoListModelの更新があれば表示を更新する。 3. フォームを送信後に、新しいTodoItemを追加する。 ### 5. Todoアイテムの更新と削除を実装する #### Todoアイテムの更新 Todoが実施されたか確認するために、チェックボックスをつける。但し、このままではチェックボックスの更新が反映されないため、changeイベントのディスパッチをリッスン、表を更新する。 <実習> 1. リストのHTML文作成部分に、input要素でチェックボックスをつける。また、完了済みの方はs要素で横線を入れる。 2. input要素からディスパッチされるchangeイベントをリッスンするために、TodoListModelにTodoアイテムの更新処理を追加する。 3. changeイベントのリスナー関数でアイテムの完了状態を更新する。 #### Todoアイテムの削除 削除ボタンを追加し、削除ボタンを押すと発生するclickイベントのディスパッチをリッスンし、Todoアイテムを削除する。 <実習> 1. Todoアイテムを削除するために、TodoListModelに指定したTodoアイテムの削除処理を追加する。 2. clickイベントのリスナー関数でTodoアイテムを削除する。 ### 6.Todoアプリのリファクタリング ここまではHTML要素をDOMで追加することで実装していた。 しかし、リストの分量が多くなるほどHTMLが長くなり、メンテナンス性が低下する。 App.jsはHTMLとモデルの間で発生するイベントを中継する役割を持つ。 そこで、Appをイベントの管理者に特化させ、HTML要素作成処理を別のクラスへ切り出すリファクタリングを行う。 #### Viewコンポーネント Viewコンポーネントでは、TodoリストをTodoアイテムViewコンポーネントと、TodoリストViewコンポーネントに分け、各コンポーネントに対応するモジュールを作成する。 1. TodoItemViewの作成 TodoアイテムViewコンポーネントのモジュールを作成する。 2. TodoListViewの作成 TodoリストViewコンポーネントのモジュールを作成する。 3. Addのリファクタリング 2つのモジュールをもとに、リファクタリングを行う。 #### Appのリスナーの整理 リスナー関数のリスナーがバラバラであるため、Appにリスナーメソッドを定義する。 リスナー関数handleメソッドを定義し、それを呼び出すようにする。 ###### tags: `JavaScript`