2015/04/04

TypeScript+AngularJSでToDoアプリをつくってみた

Webアプリを作ってるときに、素のJavaScriptではどうもソースコードが汚くなってしまった。
「ソースコードには人格がでる」と思っているので、キレイに、かつ簡単に書きたい。

ということでAngularJSというMVC(MVW)フレームワークを使ってみた。


参考にしたTodoアプリは、ドットインストールの「AngularJSで作るToDoアプリ」を基にした。
詳細な説明については、ドットインストールを参照してほしい。



準備


私の場合、TypeScriptはVisualStudioで書いているので、NuGetから以下の型定義をインストールしておく。他のエディタを使っている場合は、GitHubなどからダウンロードしてください。

  • angularjs.TypeScript.DefinitelyTyped
  • jquery.TypeScript.DefinitelyTyped(※1)

※1:jQueryの型定義は、AngularJSの型定義をインストールしたら勝手にインストールされた。依存関係になっているらしい。

※2:AngularJSの本体はNuGetからインストールしてもよいが、私の環境ではうまく動かなかったので、ドットインストールのサンプル同様「http://code.angularjs.org/angular-1.0.1.min.js」を使用する。



View(HTML)

<!DOCTYPE html>

<html lang="ja" ng-app>
<head>
    <meta charset="utf-8" />
    <title>AngularJS + TypeScript</title>
    <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    <script src="app.js"></script>

    <style>
        .done-true{
            color:gray;
            text-decoration:line-through;
        }
    </style>

</head>
<body>
    <h1>AngularJS + TypeScript</h1>
    <div ng-controller="TaskCtrl">
        <p>Finished Task: {{getDoneCount()}} / {{tasks.length}}</p>
        <button ng-click="deleteDone()">Delete Finished</button>
        <ul>
            <li ng-repeat="task in tasks">
                <input type="checkbox" ng-model="task.done" />
                <span class="done-{{task.done}}">{{task.body}}</span>
                <a ng-click="deleteTask($index)">[x]</a>
            </li>
        </ul>
        <form ng-submit="addNew()">
            <input type="text" ng-model="newTaskBody" />
            <input type="submit" value="add" />
        </form>
    </div>
</body>
</html>

内容はほぼすべてドットインストールと同じ。
唯一違うのは、27行目のng-click=”deleteTask($index)”の部分。
これは他のメソッド同様に、TypeScript側で実装することにした。



Model + Controller (TypeScript)


タスク(内容と完了フラグ)をクラスに定義する。
class TaskItem {
    body: string;
    done: boolean;
}


次に、アプリ用スコープのインターフェースを作成する。
これを作ることで、TypeScriptでの強みである型チェックと自動補完が可能になる。

ちなみに継承している「ng.IScope」はAngularJSが持っているスコープ定義。
それを拡張するカタチで、ITaskScopeインターフェースを作成している。
interface ITaskScope extends ng.IScope {
    // taskが入った配列
    tasks: Array<taskitem>;

    // 画面のテキストボックス
    newTaskBody: string;

    // 定義するメソッド
    addNew: typeof TaskCtrl.prototype.addNew;
    deleteTask: typeof TaskCtrl.prototype.deleteTask;
    deleteDone: typeof TaskCtrl.prototype.deleteDone;
    getDoneCount: typeof TaskCtrl.prototype.getDoneCount;
}


最後にコントローラクラスを作成する。
class TaskCtrl {
    constructor(private $scope: ITaskScope) {
        // 初期値
        $scope.tasks = [
            { body: "do this 1", done: false },
            { body: "do this 2", done: false },
            { body: "do this 3", done: false },
            { body: "do this 4", done: false }
        ];

        // $scopeにメソッドをひもづける
        $scope.addNew = this.addNew.bind(this);
        $scope.deleteTask = this.deleteTask.bind(this);
        $scope.deleteDone = this.deleteDone.bind(this);
        $scope.getDoneCount = this.getDoneCount.bind(this);
    }

    /**
     * タスクの追加
     */
    addNew(): void {
        this.$scope.tasks.push({ body: this.$scope.newTaskBody, done: false });
        this.$scope.newTaskBody = '';   // テキストボックスのクリア
    }

    /**
     * タスクの削除
     * @param index 削除する行番号
     */
    deleteTask(index: number): void {
        this.$scope.tasks.splice(index, 1);
    }

    /**
     * 済タスクの一括削除
     */
    deleteDone(): void {
        var oldTask = this.$scope.tasks;
        this.$scope.tasks = [];

        oldTask.forEach(task => {
            if (!task.done) this.$scope.tasks.push(task);
        });
    }
   
    /**
     * 完了済み件数の取得
     * @return 完了済み件数
     */
    getDoneCount(): number {
        var count = 0;
        this.$scope.tasks.forEach(task => {
            count += task.done ? 1 : 0;
        });

        return count;
    }
}



さいごに


今回作成したToDoアプリをGitHubにあげた。
Controllerなどは、実際に動かしながら見たほうが理解が早いと思うので、是非ご活用ください。



ただ、ふたつほど疑問に残るところがある。

ひとつは、AngularJSってMVCのフレームワークなので、Model・View・Controllerの3つが存在するはずなのに、サンプルではModelとControllerがまとまってしまっている(というかModelがない)

まるでWindowsフォームのView(Form1.csみたいなの)に全部書いているような感覚だ。
どうやってM・V・Cで分ければよいのか…。


もうひとつは、HTMLにいろいろ書き込まなければならないため、大きなWebアプリだとゴチャゴチャにならないのかなと。素のJavaScript勉強してるときは、HTML側にメソッド名書かない方がよいとかあったのに、このサンプルではバリバリ書いてある。



AngularJSについては、まだ全然わからないことばかりなので、今後も勉強していきたい。
上記2つの疑問も、分かり次第まとめてブログに投稿しようと思う。



以上

written by @bc_rikko


追記:2015/04/08 18:30

フレームワークを使わず、素のJavaScriptだけでMVCモデルのToDoアプリを作ってみた。

AngularJSを使った場合とくらべて、コード量が倍以上に膨れ上がっていた。
やはりMVCフレームワークは偉大だ…。



追記:2015/05/27 18:30

次はVue.jsで同じToDoアプリを作ってみた。
Vue.jsは学習コストが低いといわれているが、たしかにAngularJSと比べるとわかりやすい気がする。

0 件のコメント :

コメントを投稿