読者です 読者をやめる 読者になる 読者になる

AngularJS でモデルとコントローラーをモジュール化する

先日 AngularJS のルーティング機能を試したけど、サンプルではモデルをコントローラーごとに作成していた。でも、プロダクトコードだとコントローラーやモデルは再利用しやすいように、AngularJS のモジュールにするはず。特にモデルは。

そういうわけで、モデルとコントローラーをモジュール化してみる。

まず、モデルの定義は factory メソッドを使う。

angular.module("services", ["ngResource"]).
  factory("Guest", function($resource) {
    // API の URL を指定してモデルを作成
    var Guest = $resource("/guests/:id");
    return Guest;
  });

次に、コントローラーの定義は controller メソッドを使う。

angular.module("controllers", ["services"]).
  controller("MainCtrl", function($scope, Guest) {
    // query でコレクションを取得
    $scope.guests = Guest.query();
  }).
  controller("NewCtrl", function($scope, $location, Guest) {
    $scope.guestName = "";

    $scope.addGuest = function() {
      Guest.save({ name: $scope.guestName }, function(guest) {
        // 作成に成功したら一覧に戻る
        $location.path("/");
      });

      $scope.guestName = "";
    };
  });

最後にルーティング。コントローラーの名前を文字列で指定しないと、上手く動いてくれない。

angular.module("app", ["controllers"]).
  config(["$routeProvider", function($routeProvider) {
    $routeProvider.
      when("/", { templateUrl: "/index.html", controller: "MainCtrl" }).
      when("/new", { templateUrl: "/new.html", controller: "NewCtrl" });
  }]);

Web API と HTML テンプレートを含んだコード全体は次の通り。

# coding: utf-8
require "sinatra"
require "json"

# データはメモリ上に保存
GUESTS = []

get "/" do
  erb :home
end

# ゲスト一覧を JSON で取得する API
get "/guests" do
  content_type :json, :charset => "utf-8"
  GUESTS.to_json
end

# ゲストを追加する API
post "/guests" do
  # POST したデータを params から取得できなかったので
  # body を JSON にパース
  @guest = JSON.parse(request.body.read.to_s)
  GUESTS << @guest

  content_type :json, :charset => "utf-8"
  @guest.to_json
end

# 一覧ページ用のテンプレートを返す
get "/new.html" do
  erb :new
end

# 作成ページ用のテンプレートを返す
get "/index.html" do
  erb :index
end

__END__
@@ home
<!DOCTPE html>
<html ng-app="app">
  <head>
    <meta charset="utf-8">
    <title>Angular Sample</title>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular-resource.min.js"></script>
    <script type="text/javascript">
    angular.module("services", ["ngResource"]).
      factory("Guest", function($resource) {
        // API の URL を指定してモデルを作成
        var Guest = $resource("/guests/:id");
        return Guest;
      });
    </script>
    <script type="text/javascript">
    angular.module("controllers", ["services"]).
      controller("MainCtrl", function($scope, Guest) {
        // query でコレクションを取得
        $scope.guests = Guest.query();
      }).
      controller("NewCtrl", function($scope, $location, Guest) {
        $scope.guestName = "";

        $scope.addGuest = function() {
          Guest.save({ name: $scope.guestName }, function(guest) {
            // 作成に成功したら一覧に戻る
            $location.path("/");
          });

          $scope.guestName = "";
        };
      });
    </script>
    <script type="text/javascript">
    angular.module("app", ["controllers"]).
      config(["$routeProvider", function($routeProvider) {
        $routeProvider.
          when("/", { templateUrl: "/index.html", controller: "MainCtrl" }).
          when("/new", { templateUrl: "/new.html", controller: "NewCtrl" });
      }]);
    </script>
  </head>
  <body>
    <div ng-view></div>
  </body>
</html>

@@ index
<a href="#/new">New</a>
<ul ng-controller="MainCtrl">
  <li ng-repeat="guest in guests">{{ guest.name }}</li>
</ul>

@@ new
<a href="#/">Back</a>
<div ng-controller="NewCtrl">
  <input type="text" ng-model="guestName" placeholder="Input name"/>
  <button ng-click="addGuest()">Add</button>
</div>