Angular.js入門 (2)コントローラ

## コントローラの導入

前回: [Angular.js入門 (1)導入とデータバインディング](http://qiita.com/_rdtr/items/fd61b24213016974b8d9)
次回: [Angular.js入門 (3)フィルタ](http://qiita.com/_rdtr/items/5bf69a29e33f668168cc)
 
コントローラはAngularにおいてモデルとビューを対応付けて操作するためのコンポーネントです。具体的に説明をするために前回のhtmlを例に使います。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="">
    <input type="text" ng-model="hello">
    <div class="{{hello}}">ここが装飾される</div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
</html>

```

ここのフォーム部分にコントローラを適用してみましょう。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="hello">
        <div class="{{hello}}">ここが装飾される</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
</html>
```

これでng-controller属性を記述したdiv内部は, FirstCtrlというコントローラのスコープ内に置かれることになります。ここでコントローラの処理を外部ファイルに切り出していきましょう。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="">
    <div ng-controller="FirstCtrl">
        <div class="{{data.message}}">ここが装飾される</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
```

```main.js
function FirstCtrl ($scope) {
    $scope.data = {message: "panel"}
}
```

コントローラはAngularのscopeオブジェクトを引数に取ります。main.jsで記述したFirstCtrlのscopeのプロパティとして定義したdata.message = panelがclass属性として適用されていることが確認できます。

![panelによる装飾](https://qiita-image-store.s3.amazonaws.com/0/20470/47710f5d-d115-c22c-b969-f1a0a0620207.png)

## 複数のコントローラ
今度はもうひとつコントローラを入れてみます。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="data.message">
        <div class="{{data.message}}">装飾1</div>
    </div>

    <div ng-controller="SecondCtrl">
        <input type="text" ng-model="data.message">
        <div class="{{data.message}}">装飾2</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
```

```main.js
function FirstCtrl ($scope) {
}

function SecondCtrl ($scope) {
}
```

コントローラには何も処理を記載していませんが, それぞれのコントローラごとにスコープが定義されるので同じdata.messageを定義していてもそれぞれの入力欄に記載した内容が個別に反映されることに注意してください。

![2つのコントローラ](https://qiita-image-store.s3.amazonaws.com/0/20470/daa1ea36-ffd7-0081-46f6-3e2930d7e4cc.png)

では逆に, 2つのコントローラ間で値を共有したい場合はどうすればいいのでしょうか？ここでついにng-appの登場です。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="data.message">
        <div>{{data.message}}</div>
    </div>

    <div ng-controller="SecondCtrl">
        <input type="text" ng-model="data.message">
        <div>{{data.message}}</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
```

```main.js
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
    return {message: "共有されるメッセージ"}
})

function FirstCtrl ($scope, Data) {
    $scope.data = Data;
}

function SecondCtrl ($scope, Data) {
    $scope.data = Data;
}
```

ng-app名に"myApp"を指定して, myAppのfactoryメソッドを通じてDataというサービスを定義しています. このサービスをそれぞれのコントローラで引数に含めることで異なるコントローラ間で同じデータを共有することができるようになるというわけです。

![データの共有](https://qiita-image-store.s3.amazonaws.com/0/20470/5aa63189-4696-9ec1-1ca0-5743302388b9.png)

同じ値が共有されていること, 入力欄も相互に連動することが確認できます。

## スコープ内での関数定義
コントローラ内で独自に関数を定義してやることもできます。

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="data.message">
        <div>{{data.message}}</div>
    </div>

    <div ng-controller="SecondCtrl">
        <input type="text" ng-model="data.message">
        <div>{{reversedMessage()}}</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
```

```main.js
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
    return {message: "これはテストです"}
})

function FirstCtrl ($scope, Data) {
    $scope.data = Data;
}

function SecondCtrl ($scope, Data) {
    $scope.data = Data;

    $scope.reversedMessage = function () {
        return $scope.data.message.split("").reverse().join("");
    }
}
```

単純にdataの文字列を反転させているだけです。$scopeに生やしたメソッドは自由にhtmlのng-controller内で呼ぶことができるということですね。

![メソッド定義](https://qiita-image-store.s3.amazonaws.com/0/20470/ba3d0166-b9ab-5763-5742-06f618b73994.png)

ただし上の例だと関数reversedMessage内で$scopeを使っているので,

```index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Angular.jsのテスト</title>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/foundation/5.2.2/css/foundation.css">
</head>
<body>

<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="data.message">
        <div>{{data.message}}</div>
    </div>

    <div ng-controller="SecondCtrl">
        <input type="text" ng-model="data.message">
        <div>{{reversedMessage(data.message)}}</div>
    </div>
</div>

</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.6/angular.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
```

```main.js
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
    return {message: "これはテストです"}
})

function FirstCtrl ($scope, Data) {
    $scope.data = Data;
}

function SecondCtrl ($scope, Data) {
    $scope.data = Data;

    $scope.reversedMessage = function (text) {
        return text.split("").reverse().join("");
    }
}
```

のように, コントローラ内の関数定義ではパラメータをもたせておいて, html上で実際のデータを引数として入れてやる方がキレイでしょう。