RailsAPI×AngularでかっこいいWebアプリを作(ろうと試み)る【環境構築編】の続きです。
前回のあらすじ
RailsAPIアプリ作る
↓
Angularアプリをwebpackして/public
にぶっこむ
↓
RailsAPIアプリといいつつroot
のみ画面返しちゃってSPA
というわけで今回はただただAngularからAPI呼んでみます。
大したことはしないのでつまらないです。
ここで帰るときもストックだけはしていってね。
ソースも置いとくので参考にどうぞ。
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仕様になっているのでもう特にやることはないです、ハイ。
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
で固定値を返すようにしてしまいます。なんだかすいませんね。
def index
@users = []
(1..10).each{|i|
@users.push({id: i, name: 'ユーザー'<<i.to_s})
}
render json: @users
end
migrationで怒られないようにconfigもちょっといじる
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
/// <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
///<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する。
/// <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を使ってみる。
<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
きた!寂しいのでチェックボックスもつけた。
次回はangular-materialをとっても便利にする話をしようと思います。