Rails5でAngular2を動かすまでの設定
丹内です。 掲題のとおり、BetaになっているRails5とAngular2を連携させるまでの手順をまとめました。
バージョン
- ruby 2.3.0
- node 4.2.4
- npm 3.5.3
Rails5アプリの準備
bundle initしてできたGemファイルにRails5を指定します。
source 'https://rubygems.org' gem 'rails', '5.0.0.beta1'
bundle installしたらアプリを生成します。
$ rbenv exec bundle install $ bundle exec rails new . -d mysql --skip-bundle -T exist create README.md create Rakefile create config.ru create .gitignore conflict Gemfile Overwrite /Users/tannai.yuki/dev/sample/Gemfile? (enter "h" for help) [Ynaqdh] force Gemfile create app (省略) $ bundle exec rails db:create $ bundle exec rails db:migrate $ bundle exec rails s => Booting Puma => Rails 5.0.0.beta1 application starting in development on http://localhost:3000 => Run `rails server -h` for more startup options => Ctrl-C to shutdown server I, [2016-01-11T11:38:22.205313 #7174] INFO -- : Celluloid 0.17.2 is running in BACKPORTED mode. [ http://git.io/vJf3J ] Puma 2.15.3 starting... * Min threads: 0, max threads: 16 * Environment: development * Listening on tcp://localhost:3000
webrickではなくpumaが使われています。 welcome aboardでRails5になっていることを確認したら、次のステップです。
フロントエンド環境の準備
npm initでもtouchでも良いので、アプリケーションルートにpackage.jsonを生成し、以下のように編集します。
{ "name": "sample-client", "version": "1.0.0", "engines": { "node": ">= 4.2.3 < 5", "npm": ">= 3" }, "scripts": { "start": "webpack -w" }, "license": "ISC", "dependencies": { "angular2": "2.0.0-beta.1", "es6-promise": "^3.0.2", "es6-shim": "^0.33.3", "es7-reflect-metadata": "^1.4.0", "rxjs": "5.0.0-beta.0", "zone.js": "0.5.10" }, "devDependencies": { "concurrently": "^1.0.0", "exports-loader": "^0.6.2", "expose-loader": "^0.7.1", "imports-loader": "^0.6.5", "ts-loader": "^0.7.2", "typescript": "^1.7.3", "webpack": "^1.12.11" } }
同じくアプリケーションルートに、webpack.config.jsを用意します。
const path = require('path'); const webpack = require('webpack'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; module.exports = { devtool: 'source-map', debug: true, entry: { 'vendor': './client/vendor.ts', 'app': './client/bootstrap.ts' }, output: { filename: 'bundle.js', path: './app/assets/javascripts/generated' }, resolve: { root: [path.join(__dirname, 'client/assets/scripts')], extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js'] }, module: { loaders: [ { test: /\.ts?$/, exclude: /node_modules/, loader: 'ts-loader' } ] }, plugins: [ new CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.js', minChunks: Infinity }), new CommonsChunkPlugin({ name: 'common', filename: 'common.js', minChunks: 2, chunks: ['app', 'vendor'] }) ] }
Typescriptのコンパイラ、TSCの設定ファイルであるtsconfig.jsonも用意します。
{ "compilerOptions": { "target": "ES5", "module": "commonjs", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false }, "files": [ "./client/vendor.ts", "./client/bootstrap.ts", "./client/components/AppComponent.ts" ], "exclude": [ "node_modules" ], "compileOnSave": false, "buildOnSave": false, "atom": { "rewriteTsconfig": true } }
これらアプリケーションルートに設置する設定ファイルに書かれているように、以後/client
以下にAngular2のファイルを配置していきます。
Angular2の実装
まずは/client/bootstrap.tsにAngular2の設定を書きます。
import {bootstrap} from 'angular2/platform/browser'; import {App} from './app'; import {provide} from 'angular2/core'; import { ROUTER_PRIMARY_COMPONENT, APP_BASE_HREF, ROUTER_PROVIDERS as NG2_ROUTER_PROVIDERS } from 'angular2/router'; const ROUTER_PROVIDERS: Array<any> = [ NG2_ROUTER_PROVIDERS, provide(ROUTER_PRIMARY_COMPONENT, { useValue: App }), provide(APP_BASE_HREF, { useValue: '/' }) ]; const APP_PROVIDERS: Array<any> = [ ROUTER_PROVIDERS ]; window.addEventListener("load", (e) => { bootstrap(App, APP_PROVIDERS); });
この中でRouteConfigをアノテートしている、Rootになるコンポーネントを作成します。
import {ViewEncapsulation, Component} from 'angular2/core'; import {CORE_DIRECTIVES} from 'angular2/common'; import {ROUTER_DIRECTIVES, Router, RouteConfig, Location} from 'angular2/router'; import {AppComponent} from './components/AppComponent'; @Component({ moduleId: module.id, selector: 'app', encapsulation: ViewEncapsulation.Emulated, template: ` <h1>This is Root Component</h1> <router-outlet></router-outlet> `, directives: [ CORE_DIRECTIVES, ROUTER_DIRECTIVES ] }) @RouteConfig([ { path: '/', name: 'AppComponent', component: AppComponent } ]) export class App { router: Router; location: Location; constructor(router: Router, location: Location) { console.log('app.ts'); this.router = router; this.location = location; } }
/client/components以下に、AppComponent.tsを作成します。
import {ViewEncapsulation, Component} from 'angular2/core'; @Component({ template: '<h1>This is AppComponent</h1>' }) export class AppComponent { constructor() { console.log('AppComponent.ts') } }
npm startでjavascriptが生成されることを確認します。
$ npm start > sample-client@1.0.0 start /Users/tannai.yuki/dev/sample > webpack -w ts-loader: Using typescript@1.7.5 and /Users/tannai.yuki/dev/sample/tsconfig.json Hash: f7e52fb9698e7e970aaa Version: webpack 1.12.11 Time: 5065ms Asset Size Chunks Chunk Names bundle.js 3.92 kB 0 [emitted] app vendor.js 766 kB 1 [emitted] vendor common.js 4.52 MB 2 [emitted] common bundle.js.map 3.61 kB 0 [emitted] app vendor.js.map 889 kB 1 [emitted] vendor common.js.map 4.81 MB 2 [emitted] common + 510 hidden modules
Railsの設定
エントリポイントになるHomeControllerを作成します。
$ bundle exec rails g controller HomeController index
/app/views/layouts/application.html.erbに、ルートコンポーネントで指定したセレクタを追加します。
<!DOCTYPE html> <html> <head> <title>Sample</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <%= csrf_meta_tags %> <%= action_cable_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all' %> <%= javascript_include_tag 'application' %> </head> <body> <app>Loading...</app> <!-- この行です --> <%= yield %> </body> </html>
app/assets/javascripts/application.jsで生成されるjavascriptの読み込み順を設定します。 今回の設定ではcommon、vendor、bundleの3つが生成されますが、この読み込み順によってはエラーが発生してしまうことへの対処です。
//= require jquery //= require jquery_ujs //= require turbolinks //= require generated/common //= require generated/vendor //= require generated/bundle //= require_tree .
表示の確認
rails sでアプリを起動してlocalhost:3000が以下のようになっていれば成功です!
このとき、javascript consoleには以下のように表示されます。
まとめ
Rails5でAngular2を動かす設定をご紹介しました。 ActionCableとAngular2の組み合わせはとても強力だと思うので、今後も継続して追っていきたいです。