RailsAPI×AngularでかっこいいWebアプリを作（ろうと試み）る【API呼んでみる編】


[RailsAPI×AngularでかっこいいWebアプリを作（ろうと試み）る【環境構築編】](http://qiita.com/shitake/items/7b3811c36b3d6a66d194)の続きです。

## 前回のあらすじ

RailsAPIアプリ作る
↓
Angularアプリをwebpackして`/public`にぶっこむ
↓
RailsAPIアプリといいつつ`root`のみ画面返しちゃってSPA


というわけで今回はただただAngularからAPI呼んでみます。
大したことはしないのでつまらないです。
ここで帰るときもストックだけはしていってね。

[ソース](https://github.com/4take/rails-api-angular)も置いとくので参考にどうぞ。

## API作る

scaffoldコマンドを実行。

```
$rails g scaffold User
```

するとAPIモードなので勝手に判断して`migration`,`model`,`controller`のみを生成してくれます。賢すぎる。

```
Running via Spring preloader in process 4795
      invoke  active_record
      create    db/migrate/20161001061821_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
```

コントローラーもAPI仕様になっているのでもう特にやることはないです、ハイ。

```UserController.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :update, :destroy]

  # GET /users
  def index
    @users = User.all

    render json: @users
  end

  # GET /users/1
  def show
    render json: @user
  end

  # POST /users
  def create
    @user = User.new(user_params)

    if @user.save
      render json: @user, status: :created, location: @user
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /users/1
  def update
    if @user.update(user_params)
      render json: @user
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end

  # DELETE /users/1
  def destroy
    @user.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def user_params
      params.fetch(:user, {})
    end
end
```

ただサンプルでDB作るのもめんどうなので`GET /users`で固定値を返すようにしてしまいます。なんだかすいませんね。

```UserController.rb
def index
  @users = []
  (1..10).each{|i|
    @users.push({id: i, name: 'ユーザー'<<i.to_s})
  }

  render json: @users
end
```

migrationで怒られないようにconfigもちょっといじる

```config/environments/development.rb
config.active_record.migration_error = false
```

## API呼ぶ
AngularアプリからRestAPIを呼び出すためには`$resource`ファクトリーを使用します。
その場合別途`angular-resource`というモジュールのインストールが必要になりますが、前回記載したpackage.jsonを使用している場合は既にインストール済みのはずです。
ただしモジュールが違えばもちろん型定義ファイルも別になるのでこれはインストールする必要あり。

型定義ファイルをインストール

```
$cd app/angular/javascripts/
$dtsm install angularjs/angular-resource --save
```

APIを呼ぶための`UserResource`というResourceを定義します。

```
mkdir resources
touch resources/user-resource.ts
```

```user-resource.ts
/// <reference path="../typings/bundle.d.ts"/>

export interface User {
	id: number
	name: string
}

interface Resource extends ng.resource.IResource<User>, User {
}

export default class UserResource {

	get userResource(): ng.resource.IResourceClass<Resource>{
		return <ng.resource.IResourceClass<Resource>>this.resource('/users')
	}

	static $inject = ['$resource']
	constructor(private resource: ng.resource.IResourceService) {
	}

	all(success: (v: Array<User>) => void) {
		let res = this.userResource.query({
			},
			() => {
				success(res)
			})
	}
}

angular.module('App').service('userResource', UserResource)
```

resourceからユーザーの一覧を取得して表示するだけのComponentを定義します。

```
mkdir components
touch components/users.ts
```

```users.ts
///<reference path="../typings/bundle.d.ts"/>

import angular = require('angular')
import UserResource from '../resources/user-resource'
import {User} from '../resources/user-resource'

class Users {
	private users: Array<User>

	static $inject = ['userResource']
	constructor(private userResource: UserResource) {
		this.userResource.all((users)=> {
			this.users = users
		})
	}
}

angular.module('App').component('users', {
	controller: Users,
	bindings: {},
	template:
		`
	<md-list>
		<md-list-item ng-repeat="user in $ctrl.users">
			<p> {{ user.name }} </p>
			<md-checkbox class="md-secondary"></md-checkbox>
		</md-list-item>
	</md-list>
		`
})
```

エントリーポイントである`appliaction.ts`に追加した2つのクラスをrequireする。

```application.ts
/// <reference path="./typings/bundle.d.ts"/>

// angular
import angular = require('angular')
require('angular-material')
require('angular-cookies')
require('angular-resource')
require('angular-sanitize')
require('angular-route')
require('angular-animate')
require('angular-material-icons')
require('es5-shim')

let app = angular.module('App', [
	'ngMaterial',
	'ngCookies',
	'ngResource',
	'ngSanitize',
	'ngRoute',
	'ngMdIcons',
	'ngAnimate'
]);

//stylesheets
require('../stylesheets/application')

//components
require('./components/users')

//services
require('./resources/user-resource')

//angular-materialのTheme
app.config(($mdThemingProvider) => {
	$mdThemingProvider.theme('default')
					  .primaryPalette('grey', {
						  'default': '100'
					  })
					  .accentPalette('pink', {
						  'default': '700'
					  });
});

//Controllerも一緒に定義しちゃいました
export default class AppCtrl {
	private title: string = 'Hello RailsAPI × Angular！'
	static $inject = ['$rootScope', '$scope', '$cookies', '$window', '$timeout', '$location', 'userResource']
	constructor(
		private rootScope: ng.IRootScopeService,
		private scope: ng.IScope,
		private cookies: any,
		private window: ng.IWindowService,
		private timeout: ng.ITimeoutService,
		private location: ng.ILocationService
	) {
	}
}

app.controller('AppCtrl', AppCtrl)
```

そんで作ったComponentを使ってみる。

```index.erb
<html ng-app="App">
<head>
  <meta charset="UTF-8">
  <meta name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1">
  <title>rails-api-angular</title>
</head>
<body ng-controller="AppCtrl as appCtrl" ng-cloak>
  <header class="header md-whiteframe-3dp">
    <md-toolbar md-scroll-shrink>
      <div class="md-toolbar-tools">
        <h3>
          <span>rails-api-angular</span>
        </h3>
        <span flex></span>
        <md-button class="md-accent">Sign up</md-button>
        <md-button class="md-accent">Log in</md-button>
      </div>
    </md-toolbar>
  </header>

  <div layout="column" layout-align="center center">
    <h1>{{appCtrl.title}}</h1>
    <!--component-->
    <users></users>
  </div>

<%= stylesheet_link_tag webpack_asset_path('application.css') %>
<%= javascript_include_tag webpack_asset_path('application.js')%>
</body>
</html>
```

webpackとrails起動実行

```
$npm run dev
$rails s
```

きた！寂しいのでチェックボックスもつけた。

![Kobito.78ye0L.png](https://qiita-image-store.s3.amazonaws.com/0/127115/450e3cb5-2221-94f2-8c56-635ae33f23d4.png "Kobito.78ye0L.png")


次回はangular-materialをとっても便利にする話をしようと思います。