なみひらブログ

学んだことを日々記録する。~ since 2012/06/24 ~

AngularJSのひと通り動作させるためにやったこと

AngularJSのひと通り動作させるためにやったことをメモっときます。

前提

この作業メモには以下の前提があります。

  • レイアウトにはTilesを使っています。
  • Springframework, mavenを使っています。
    • AngularJSを使うためにはこの辺の技術は関係ありませんが、自分用のメモのため記載が登場します。
  • 筆者はJavaScript初心者のため、有識者にとって見苦しい記載/コードがあるかもしれません(;´Д`)

必要な知識

とりあえず気にしておくこと

  • AngularJSはJavaScriptMVCを実現するためのフレームワーク
  • これまではサーバサイドでMVCが実現されることが多かったですが、それをブラウザ側(JavaScript)で実現できます。
    • ブラウザ側が高機能になってきており、それに伴いクライアントサイドが複雑化してきている。そのためモジュール化やMVC化が求められているというのが背景らしいです。
  • 各処理の単位をMVCで書けるため、ControllerとSericeモジュールは意識して定義する必要がある。

やりたいこと

  • サーバ側APIからユーザ一覧情報を取得して、ユーザ情報を画面に表示する。
  • 画面例

f:id:Namihira:20150801205748j:plain

全体的なの流れ

  1. モジュールを作成します。
  2. そのモジュールにServiceとControllerを登録していきます。
  3. 画面とControllerを紐付けます。

以下の記載には別途最初にレイアウト関連の記載があります。

作業メモ

Springやビルド関連

全てJavaScriptで閉じているため、特に変更なし(spring-context.xnkやpom.xml

レイアウト関連(Tiles)

ControllerモジュールとServiceモジュールをロードするために画面設定に修正する。

  • レイアウトファイル(layout.jsp
    • ng-appはAngularJSの機能を使うために必要な定義。定義したタグ内にAngularJSが適用される。とりあえずはhtmlタグに定義しておけば問題ない。名前について後述。
 <%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 <html ng-app="sampleApp">
 <head>
   <title><tiles:getAsString name="title"/></title>
   <tiles:insertAttribute name="header" />
   <tiles:insertAttribute name="commonScript" />
   <tiles:insertAttribute name="services" />   #Serviceモジュールを定義するための領域確保
 </head>

 <body>
   <tiles:insertAttribute name="body" />
   <tiles:insertAttribute name="footer" />
   <tiles:insertAttribute name="controllers" /> #Controllerモジュールを定義するための領域確保
 </body>
</html>

AngularJS関連

  • AngularJS取り込み
    • AngularJSを使うために関連モジュールを取り込むために以下追加
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>


  • Tiles定義ファイル(definition.xml
    • 上記で設定した属性に定義ファイルを割り当てる。
    • Controllerは各ページごとに違うと想定して、基底ページでは定義しない(空文字指定)。今のところ。
(略)
<tiles-definitions>
  <definition name="baseLayout" template="/WEB-INF/views/common/layout.jsp">
    <put-attribute name="header" value="/WEB-INF/views/common/header.jsp" />
    <put-attribute name="commonScript" value="/WEB-INF/views/common/script.jsp" /> #Module割り当て(名前が良くない気がする(;´Д`))
    <put-attribute name="services" value="/WEB-INF/views/common/services.jsp" />  #Serviceモジュール割り当て
    <put-attribute name="controllers" value="" />  #Controllerモジュール割り当て。形だけ。
    <put-attribute name="footer" value="/WEB-INF/views/common/footer.jsp" />
  </definition>


  <definition name="user.list" extends="baseLayout">
    <put-attribute name="title" value="User Management" />
    <put-attribute name="body" value="/WEB-INF/views/user/body.jsp" />
    <put-attribute name="controllers" value="/WEB-INF/views/user/controllers.jsp" /> #Controllerモジュール割り当て
  </definition>

(略)


  • モジュール作成
    • AngularJSは一番上位の概念としてModuleというものがあって、それを作成する必要がある。*1
    • 作成したModuleに対して、ControllerやServiceを登録していくイメージ(後述)
    • さっきのHTML内のng-appで使ったModuleがそのタグ内で利用することができる。1つのアプリケーションでModuleを分けたい場合はその定義に使うモジュール名を変える。
    • 定義例:script.jsp
      • 第2引数があると、新規にモジュール作成することになる。ちなみに第2引数を省略すると作成済みのモジュールが返ってくる(あとで使う)。
<script>
  angular.module('sampleApp', []);
</script>


  • Serviceモジュール作成
    • MVCのModelにあたる部分。汎用的な処理はここで定義。
    • 定義例:services.jsp
      • 内容は、「userService」というServiceを作成とModuleへの登録を同時にやっているイメージ
      • このときのModule作成は第2引数がないので、上記で作成した"sampleApp"が返ってくる。みんなそこにどんどん登録していくイメージ。
      • 定義しているメソッド(getUsers)は、サーバ側APIからユーザ一覧情報をとってきて結果をブロードキャストしている。
      • ブロードキャストは$rootScopeに対して行ったほうが一連の$scopeに投げられるので便利。
<script>
var app = angular.module('sampleApp')

app.service('userService', ['$rootScope', function ($rootScope) {
		this.getUsers = function () {
    	                             $.ajax({
        		                 type: 'GET',
            	                         url: '/sampleapp/api/users',
            	                         dataType: 'json',
            	                         success: function (json) {
                	                     $rootScope.$broadcast('getUsersCompleted', json)
            	                         }
        	                    });
	                        }
}]);
</script>


  • Controllerモジュール作成
    • 上記で定義したServiceを使うController
    • 定義例:controllers.jsp
      • ここでもModule作成は第2引数がないので、上記で作成した"sampleApp"が返ってくる。みんなそこにどんどん(ry
      • 引数に"userService"を指定しているが、AngularJSが同名をもつモジュールをDIしてくれる(ここでは上記で定義したuserServiceが注入される)。
      • 定義しているのは、変数(users)とか初期化用メソッド(init)、あとイベント検知のためのリスナー(?)登録。
      • 基本的に$scopeに対して定義します。
      • イベントを検知するためには、$onに対して行い、任意のキーワードを指定してListenします。
        • この場合だと上記でuserServiceが"getUsersCompleted"に対してブロードキャストするため、ここで同じキーワードを指定することで検知できます。*2
<script>
var app = angular.module('sampleApp')

app.controller('userManagementController', ['$scope', 'userService', function ($scope, userService) {
	$scope.users = []

	$scope.init = function () {
           userService.getUsers()
	}

        $scope.$on('getUsersCompleted', function (event, params) {
           $scope.$apply(function () {
             angular.copy(params, $scope.events);
           });
        });
}]);
</script>


  • Viewに対してモジュール割り当て
    • 今まで作成したモジュールをView(jspやhtml)に割り当てます。
      • 本来ServiceモジュールはControllerモジュールから使われるはずなので、Viewに割り当てるのは、Controllerモジュールだけのはず(今回もそうです)。
    • 定義例:body.jsp
      • AngularJSはHTML拡張の側面が強いため、いろいろなタグを利用できる(ディレクティブと呼ぶ)。
        • ng-controller:利用するControllerの宣言
        • ng-init:Controllerの初期化処理。ここではuserManagementControllerで定義したinitを呼ぶようにしている。
        • ng-repeat:for文に相当。ここでは、userManagementControllerで定義したusers変数を回している。
        • {{ }}:式評価はこの表現で出来る。ここでは、user情報のnameの値は表示される。
<div ng-controller="userManagementController"  ng-init="init()">
    <div class="well" ng-repeat="user in users">
       <li>ユーザ名:{{user.name}}<br>
    </div>
</div>

所感

  • いままでJavaScriptは関数たくさん書きまくって使いまわすようなC言語のような使い方しかしなかったので、今どきのやり方に触れられた気がします(*´Д`*)
  • MVCとかDIとかはSpringFrameworkで使っていたので特に概念的なギャップはなく、スッと頭に入ってきました。あとはJavaScript/AngularJSの作法を学ぶ必要がある感じ。
    • もっと使いこなしてきたら、新しい概念がどんどんでてきそう。
  • あと他のJavaScriptフレームワークを知らないので、AngulerJS自体の評価はわかりません(;´Д`)

参考

*1: アプリとモジュールという名前が混在する(;´Д`)

*2: こんな感じの仕組みはAndroidアプリのブロードキャストに似ている。もっと直接的なbindみたいなものはないのかしら(´Д`)