注:編集#1:ng-initを削除する

序章

今日は、MEAN(Mongo、Express、Angular、Node)スタックを使用して非常に単純なTodoアプリケーションを作成します。 作成します:

  • ToDoを作成して終了するシングルページアプリケーション
  • Mongooseを使用してMongoDBにtodoを保存する
  • Expressフレームワークの使用
  • RESTfulノードAPIの作成
  • フロントエンドとAPIへのアクセスにAngularを使用する

この記事はExpressJS4.0用に更新されました]。

アプリケーションはシンプルで、それ自体が初級から中級レベルですが、ここでの概念は、はるかに高度なアプリに適用できます。 私たちが焦点を当てるべき最大のことは、ノードをAPIとして使用し、Angularをフロントエンドとして使用することです。 それらを一緒に機能させることは少し混乱する可能性があるので、このチュートリアルはいくつかの混乱を軽減するのに役立つはずです。 それらのシートベルトを締めます。 これは長いものになる可能性があります。

基本設定

ファイル構造

ファイル構造を非常にシンプルに保ち、Nodeアプリケーションのほとんどのコードをserver.jsファイルに配置します。 大規模なアプリケーションでは、これをさらに細かく分けて職務を分離する必要があります。 Mean.io は、ベストプラクティスとファイル構造を分離する方法を確認するための優れた定型文です。 先に進んで、より単純なファイル構造を作成し、ファイルを編集していきましょう。

        - public            <!-- holds all our files for our frontend angular application -->
        ----- core.js       <!-- all angular code for our app -->
        ----- index.html    <!-- main view -->
        - package.json      <!-- npm configuration to install dependencies/modules -->
        - server.js         <!-- Node configuration -->

モジュールのインストール

Nodeでは、package.jsonファイルにアプリの構成が保持されます。 ノードのパッケージマネージャー(npm)はこれを使用して、使用する依存関係またはモジュールをインストールします。 この例では、 Express (人気のあるノードフレームワーク)と Mongoose (MongoDBのオブジェクトモデリング)を使用します。

    {
      "name"         : "node-todo",
      "version"      : "0.0.0",
      "description"  : "Simple todo application.",
      "main"         : "server.js",
      "author"       : "Scotch",
      "dependencies" : {
        "express"    : "~4.7.2",
        "mongoose"   : "~3.6.2",
        "morgan"     : "~1.2.2",
        "body-parser": "~1.5.2",
        "method-override": "~2.1.2"
        }
    }

npm installを実行すると、npmはこのファイルを調べて、ExpressとMongooseをインストールします。

ノード構成

package.jsonファイルで、メインファイルはserver.jsになると伝えました。 これはノードアプリのメインファイルであり、アプリケーション全体を構成します。

これは、次のファイルです。

  • アプリケーションを構成する
  • 私たちのデータベースに接続します
  • マングースモデルを作成する
  • RESTfulAPIのルートを定義する
  • フロントエンドAngularアプリケーションのルートを定義する
  • ブラウザで表示できるように、ポートでリッスンするようにアプリを設定します

今のところ、Express、MongoDBデータベース、およびポートでリッスンするようにアプリを構成します。

    // server.js

        // set up ========================
        var express  = require('express');
        var app      = express();                               // create our app w/ express
        var mongoose = require('mongoose');                     // mongoose for mongodb
        var morgan = require('morgan');             // log requests to the console (express4)
        var bodyParser = require('body-parser');    // pull information from HTML POST (express4)
        var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)

        // configuration =================

        mongoose.connect('mongodb://user:password@mongo.onmodulus.net:port/database');     // connect to mongoDB database on modulus.io

        app.use(express.static(__dirname + '/public'));                 // set the static files location /public/img will be /img for users
        app.use(morgan('dev'));                                         // log every request to the console
        app.use(bodyParser.urlencoded({'extended':'true'}));            // parse application/x-www-form-urlencoded
        app.use(bodyParser.json());                                     // parse application/json
        app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
        app.use(methodOverride());

        // listen (start app with node server.js) ======================================
        app.listen(8080);
        console.log("App listening on port 8080");

ほんの少しのコードで、NodeのおかげでHTTPサーバーができました。 Expressを使用してアプリも作成し、その多くのメリットを利用できるようになりました。 app.configureセクションでは、エクスプレスモジュールを使用して、アプリケーションに機能を追加しています。

データベースの設定

Modulus.ioでホストされているリモートデータベースを使用します。 彼らは素晴らしいサービスを提供し、あなたが適切と思うように使用するためにあなたに前もって15ドルを与えます。 これは、その場でデータベースのテストと作成を行うのに最適です。

Modulusは必要なデータベースURLを提供し、mongoose.connectを使用してそれに接続できます。 それでおしまい。

アプリを起動してください!

package.jsonserver.jsが起動したので、サーバーを起動して何が起こっているかを確認できます。 コンソールに移動して、次のコマンドを使用するだけです。

node server.jsこれで、サーバーがポート8080でリッスンします。 http://localhost:8080では、何も出力するようにアプリケーションを構成していないため、ブラウザーに何も表示されません。 しかし、それは始まりです!

ファイルが変更されたときにサーバーを自動的に再起動します:デフォルトでは、サーバーの起動後、ノードはファイルの変更を監視しません。 つまり、ファイルを変更するたびにサーバーをシャットダウンして起動する必要があります。 これはnodemonで修正できます。 使用するには:nodemonをグローバルにインストールしますnpm install -g nodemon。 今すぐnodemon server.jsでサーバーを起動します。 そこからスムーズな航海。

アプリケーションフロー

次に、すべての可動部品がどのように連携するかについて簡単に説明します。 このアプリケーションにはさまざまなアイデアやテクノロジーが含まれているため、それらすべてと簡単に混同される可能性があります。

Angularはそれ自体がフロントエンドにあります。 NodeAPIを介して必要なすべてのデータにアクセスします。 ノードはデータベースにアクセスし、RESTfulルーティングに基づいてJSON情報をAngularに返します。

このようにして、フロントエンドアプリケーションを実際のAPIから分離できます。 APIを拡張したい場合は、フロントエンドのAngularアプリケーションに影響を与えることなく、いつでもより多くのルートと関数をAPIに組み込むことができます。 このように、APIをヒットするだけでよいため、最終的にはさまざまなプラットフォームでさまざまなアプリを構築できます。

ノードAPIの作成

フロントエンドアプリケーションに到達する前に、RESTfulAPIを作成する必要があります。 これにより、すべてのtodos を取得し、 todo を作成し、completeしてtodoを削除するAPIを使用できるようになります。 このすべての情報をJSON形式で返します。

Todoモデル

Todoのモデルを定義する必要があります。 これは単純にしておきます。 構成セクションの後、リッスンセクションの前に、モデルを追加します。

        // define model =================
        var Todo = mongoose.model('Todo', {
            text : String
        });

それが私たちが望むすべてです。 ToDoのテキストだけです。 MongoDBは、作成するToDoごとに_idを自動的に生成します。

RESTfulAPIルート

API呼び出しを処理するためのExpressルートを生成しましょう。

    // server.js
    ...

    // routes ======================================================================

        // api ---------------------------------------------------------------------
        // get all todos
        app.get('/api/todos', function(req, res) {

            // use mongoose to get all todos in the database
            Todo.find(function(err, todos) {

                // if there is an error retrieving, send the error. nothing after res.send(err) will execute
                if (err)
                    res.send(err)

                res.json(todos); // return all todos in JSON format
            });
        });

        // create todo and send back all todos after creation
        app.post('/api/todos', function(req, res) {

            // create a todo, information comes from AJAX request from Angular
            Todo.create({
                text : req.body.text,
                done : false
            }, function(err, todo) {
                if (err)
                    res.send(err);

                // get and return all the todos after you create another
                Todo.find(function(err, todos) {
                    if (err)
                        res.send(err)
                    res.json(todos);
                });
            });

        });

        // delete a todo
        app.delete('/api/todos/:todo_id', function(req, res) {
            Todo.remove({
                _id : req.params.todo_id
            }, function(err, todo) {
                if (err)
                    res.send(err);

                // get and return all the todos after you create another
                Todo.find(function(err, todos) {
                    if (err)
                        res.send(err)
                    res.json(todos);
                });
            });
        });

    ...

これらのルートに基づいて、フロントエンドアプリケーションがAPIからデータを要求する方法を説明するテーブルを作成しました。

HTTP動詞 URL 説明
得る /api/todos すべてのToDoを取得する
役職 /api/todos 単一のToDoを作成する
消去 /api/todos/:todo_id 1つのToDoを削除する

各APIルート内で、Mongooseアクションを使用してデータベースとの対話を支援します。 以前にvar Todo = mongoose.modelを使用してモデルを作成しましたが、これを使用して find create 、およびremoveを実行できます。 できることは他にもたくさんあります。詳細については、公式のドキュメントをご覧になることをお勧めします。

APIが完成しました! 喜んで! アプリケーションを起動すると、localhost:8080/api/todosでアプリケーションを操作して、すべてのタスクを取得できます。 何も追加していないので、現在は何もありません。

Angularを使用したフロントエンドアプリケーション

ノードアプリケーションの作成データベースの構成 APIルートの生成サーバーの起動を行いました。 すでに多くのことが行われていますが、まだ少し長くなります!

これまでに行ってきた作業は、アプリケーションとして自立することができます。 これは、アプリケーションとユーザーがコンテンツに接続できるようにするために使用するAPIにすることができます。

作成したばかりの新しいAPIを最初に使用したいと考えています。 これは、先月私が学んだ私のお気に入りの用語の1つです。私たちはdogfoodingになります。 新しいAPIを使用する最初のクライアントであるため、これを扱うことができます。 これを単純にするため、フロントエンドを定義するのはindex.htmlcore.jsだけです。

フロントエンドルートの定義

APIルートはすでに定義されています。 アプリケーションのAPIには/api/todosからアクセスできますが、フロントエンドについてはどうでしょうか。 ホームページにindex.htmlファイルを表示するにはどうすればよいですか?

フロントエンドアプリケーションのserver.jsファイルに1つのルートを追加します。 Angularはシングルページアプリケーションを作成し、ルーティングを処理するため、これが必要なすべてです。

APIルートの後、app.listenの前に、次のルートを追加します。

    // server.js
    ...
        // application -------------------------------------------------------------
        app.get('*', function(req, res) {
            res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
        });
    ...

これにより、localhost:8080を押すと、単一のindex.htmlファイルが読み込まれます。

Angularcore.jsの設定

最初にAngularのセットアップを見てみましょう。 モジュールを作成コントローラーを作成todosを処理する関数を定義する必要があります。 次に、ビューに適用できます。

    // public/core.js
    var scotchTodo = angular.module('scotchTodo', []);

    function mainController($scope, $http) {
        $scope.formData = {};

        // when landing on the page, get all todos and show them
        $http.get('/api/todos')
            .success(function(data) {
                $scope.todos = data;
                console.log(data);
            })
            .error(function(data) {
                console.log('Error: ' + data);
            });

        // when submitting the add form, send the text to the node API
        $scope.createTodo = function() {
            $http.post('/api/todos', $scope.formData)
                .success(function(data) {
                    $scope.formData = {}; // clear the form so our user is ready to enter another
                    $scope.todos = data;
                    console.log(data);
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

        // delete a todo after checking it
        $scope.deleteTodo = function(id) {
            $http.delete('/api/todos/' + id)
                .success(function(data) {
                    $scope.todos = data;
                    console.log(data);
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

    }

Angularモジュール(scotchApp)とコントローラー(mainController)を作成します。

また、すべてのtodoを取得 todoを作成todoを削除する関数も作成します。 これらはすべて、作成したAPIにヒットします。 ページの読み込み時に、GET /api/todosを実行し、APIから受け取ったJSONを$scope.todosにバインドします。 次に、これらをビューでループして、ToDoを作成します。

作成と削除についても同様のパターンに従います。 アクションを実行し、ToDoリストを作り直します。

フロントエンドビューindex.html

ここでは、それを単純に保ちます。 これは、Angularと対話するために必要なHTMLです。 私達はします:

  • Angularモジュールとコントローラーを割り当てる
  • すべてのタスクを取得してページを初期化します
  • ToDoをループします
  • ToDoを作成するためのフォームがあります
  • チェックされたらtodoを削除します
    <!-- index.html -->
    <!doctype html>

    <!-- ASSIGN OUR ANGULAR MODULE -->
    <html ng-app="scotchTodo">
    <head>
        <!-- META -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

        <title>Node/Angular Todo App</title>

        <!-- SCROLLS -->
        <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"><!-- load bootstrap -->
        <style>
            html                    { overflow-y:scroll; }
            body                    { padding-top:50px; }
            #todo-list              { margin-bottom:30px; }
        </style>

        <!-- SPELLS -->
        <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script><!-- load jquery -->
        <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script><!-- load angular -->
        <script src="core.js"></script>

    </head>
    <!-- SET THE CONTROLLER AND GET ALL TODOS -->
    <body ng-controller="mainController">
        <div class="container">

            <!-- HEADER AND TODO COUNT -->
            <div class="jumbotron text-center">
                <h1>I'm a Todo-aholic <span class="label label-info">{{ todos.length }}</span></h1>
            </div>

            <!-- TODO LIST -->
            <div id="todo-list" class="row">
                <div class="col-sm-4 col-sm-offset-4">

                    <!-- LOOP OVER THE TODOS IN $scope.todos -->
                    <div class="checkbox" ng-repeat="todo in todos">
                        <label>
                            <input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}
                        </label>
                    </div>

                </div>
            </div>

            <!-- FORM TO CREATE TODOS -->
            <div id="todo-form" class="row">
                <div class="col-sm-8 col-sm-offset-2 text-center">
                    <form>
                        <div class="form-group">

                            <!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
                            <input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">
                        </div>

                        <!-- createToDo() WILL CREATE NEW TODOS -->
                        <button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>
                    </form>
                </div>
            </div>

        </div>

    </body>
    </html>

私たちが持っているものを見てください。

結論

これで、(作成した)APIを介してすべてのToDoを表示、作成、および削除する、完全に機能するアプリケーションができました。 それはかなりの日でした。 私たちはたくさんのことをしました。 私たちが達成したことの概要:

  • Expressを使用したRESTfulノードAPI
  • マングースを使用したMongoDBインタラクション
  • Angular AJAX$http呼び出し
  • 更新なしのシングルページアプリケーション
  • ドッグフーディング(申し訳ありませんが、私はその言葉が本当に好きです)

アプリケーションをテストする

先に進み、 GitHub にコードをダウンロードして、微調整またはテストします。 すべてを稼働させるには:

  1. Nodeとnpmがインストールされていることを確認してください
  2. リポジトリのクローンを作成します:git clone [email protected]:scotch-io/node-todo
  3. アプリケーションをインストールします:npm install
  4. サーバーを起動します:node server.js
  5. http://localhost:8080でブラウザに表示します

これが、多くの可動部品を連携させる方法について洞察に満ちたものであったことを願っています。 将来的には、server.jsファイルを分離することを検討します。これは、少しおかしくなったためです。

参考資料より多くのMEANスタックアプリケーションに興味がある場合は、独自のMEANスタック基盤の構築を開始するためのガイドを作成しました。

MEANスタックシングルページアプリケーションの設定

この記事は、Node and AngularTo-DoAppシリーズの一部です。

  1. NodeとAngularを使用したシングルページToDoアプリの作成
  2. ノードアプリケーションの構成と構造
  3. Angularモジュール:コントローラーとサービス