Railsでサンプルコードを探していると、renderというメソッドをよく見かけます。
しかし、
・ControllerとViewのどちらでも使えるけど、同じもの?
・オプションを指定するとJSONとかXMLを出力できるってホント?
など、様々な疑問が浮かんでくると思います。
そこで今回は、
・Controllerでrenderを使う方法
・Viewでrenderを使う方法
・renderの発展的な使い方として、JSONやXMLで出力する方法
について説明します。
それでは行ってみましょう!
renderとは
renderは、レンダリング(rendering)という単語から名付けられたメソッドです。
renderの主な役割は、テンプレートを表示すること(=レンダリングすること)です。
MVCのControllerとViewのどちらでも使用できますが、使い方には微妙な違いがあります。
また、renderには様々なオプションが用意されており、登録されたデータをJSONやXMLで出力することもできます。
Ruby on Railsガイドの以下のページでもrenderについて触れていますので、もっと詳しく知りたい方はこちらも参考にしてください。
参考:https://railsguides.jp/action_view_overview.html
準備
renderの動作を確認するために、Ruby on Railsをインストールして、Railsサーバーを起動しておきましょう。
詳しくは、以下の記事で紹介していますので、ぜひご覧ください。
なお、今回の記事では、app/samurai/render-demoディレクトリにRailsをインストールしました。
Railsサーバーが起動できたら、いったんRailsサーバーを終了してから、次の手順を行います。
(1)以下のコマンドを1行ずつ順番に入力します。
bin/rails generate controller pages index help bin/rails generate scaffold Post user_id:integer title:string month:integer bin/rails db:migrate bin/rails console
Controller(Pages Controller)とView(index View、help View)、Model(Posts Model)が作成され、Railsコンソールが起動します。
(2)以下のコードを1行ずつ順番に入力します。
Post.create(user_id:5,title:"楽しい休日の過ごし方" ,month:3) Post.create(user_id:1,title:"先日の旅行での話" ,month:2) Post.create(user_id:3,title:"昨日の出来事" ,month:12) Post.create(user_id:3,title:"山登りに行きました" ,month:8) Post.create(user_id:4,title:"友人が結婚しました" ,month:4) Post.create(user_id:2,title:"最近少し気になったこと" ,month:1) Post.create(user_id:4,title:"ランニングのコツ" ,month:9) exit
Postテーブルに必要なデータが入力されました。
「http://localhost:3000/」にアクセスしたときに、app/views/pages/index.html.erbテンプレート(indexビュー)がレンダリング(表示)されるようにします。
(3)config/routes.rbを編集します。
変更前:
Rails.application.routes.draw do get 'pages/index' get 'pages/help' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
変更後:
Rails.application.routes.draw do get 'pages/index' get 'pages/help' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html root 'pages#index' end
(4)以下のコマンドを入力します。
[code]
bin/rails server
[/code]
Railsサーバーが起動します。
これで準備完了です。
(5)ブラウザで「http://localhost:3000/」にアクセスしてみましょう。
以下のように、app/views/pages/index.html.erbテンプレートがレンダリング(表示)されていれば、次に進みましょう。
Controllerでテンプレートを使う
Controllerでは、他のアクションのテンプレートをそのまま利用する場合に、renderを使います。
「準備」でconfig/routes.rb(ルーター)を編集して、「http://localhost:3000/」にアクセスしたときに、Pages Controllerのindexアクションによって、app/views/pages/index.html.erbテンプレート(indexビュー)がレンダリング(表示)されるように変更しました。
今度は、app/controllers/pages_controller.rb(Pages Controller)のindexアクションでrenderを使い、indexビューの代わりに、app/views/pages/help.html.erbテンプレート(helpビュー)がレンダリング(表示)されるように変更します。
(1)app/controllers/pages_controller.rbを編集します。
変更前:
class PagesController < ApplicationController def index end def help end end
変更後:
class PagesController < ApplicationController def index render "help" end def help end end
(2)ブラウザで「http://localhost:3000/」にアクセスします。
URLは変わりませんが、以下のように、app/views/pages/help.html.erbテンプレート(help)がレンダリング(表示)されていますね。
(3)app/controllers/pages_controller.rbを元に戻します。
変更前:
class PagesController < ApplicationController def index render "help" end def help end end
変更後:
class PagesController < ApplicationController def index end def help end end
これで、再びapp/views/pages/index.html.erbテンプレート(index)が呼び出されるようになりました。
以上が、Controllerでのrenderの基本的な使い方です。
Controllerで部分テンプレートを使う
次に、Controllerで部分テンプレートと呼ばれるモノを使ってみましょう。
複数の投稿を1ページで表示する場合は、以下のように同じフォーマットを繰り返し適用しますね。
このような場合に、部分テンプレートを使います。
ただ、今回の例は、Controllerから部分テンプレートを無理矢理使っている感じになりました。
部分テンプレートを使うのはViewのほうが良さそうです。
(1)app/views/pages/_post.html.erbを以下の内容で作成します。
<%= post.title %>
ユーザーID:<%= post.user_id %>
投稿月:<%= post.month %>
このファイルが部分テンプレートです。
部分テンプレートのファイル名は、必ず_(アンダーバー)で始めます。
(2)app/controllers/pages_controller.rbを編集します。
変更前:
class PagesController < ApplicationController def index end def help end end
変更後:
class PagesController < ApplicationController def index render :partial => "post.html.erb", :collection => Post.all end def help end end
(3)ブラウザで「http://localhost:3000/」にアクセスします。
手順の前に紹介したように、app/views/pages/_post.html.erbテンプレートがPostの数だけレンダリング(表示)されていることを確認してください。
変数を渡す
Controllerで部分テンプレートをレンダリングするときは、「:collection => Post.all」という書きかたでPostテーブルのデータを渡しています。
ここではさらに、モデルとは無関係のローカル変数を渡す方法を紹介します。
(1)app/views/pages/_post.html.erbを以下の内容に変更します。
変更前:
<%= post.title %>
ユーザーID:<%= post.user_id %>
投稿月:<%= post.month %>
変更後:
<%= post.title %>
ユーザーID:<%= post.user_id %>
投稿月:<%= post.month %>
「#90ee90」を「<%= bgcolor %>」に変更しました。
ここに変数bgcolorの値が入ることになります。
(2)app/controllers/pages_controller.rbを編集します。
変更前:
class PagesController < ApplicationController def index end def help end end
変更後:
class PagesController < ApplicationController def index render :partial => "post.html.erb", :collection => Post.all, :locals => { :bgcolor => "#ffe6e6" } end def help end end
「:locals => { :bgcolor => "#ffe6e6" }」で、変数bgcolor(値は”#ffe6e6”)を渡しています。
(3)ブラウザで「http://localhost:3000/」にアクセスします。
以下のように、色が変わりますね。
「#ffe6e6」を「lightsteelblue」に変更しても色を変えられます。
この後、Viewで部分テンプレートを使う方法を説明するために、app/controllers/pages_controller.rbを次のように戻しておきましょう。
(4)app/controllers/pages_controller.rbを編集します。
変更前:
class PagesController < ApplicationController def index render :partial => "post.html.erb", :collection => Post.all, :locals => { :bgcolor => "#ffe6e6" } end def help end end
変更後:
class PagesController < ApplicationController def index end def help end end
これで、「http://localhost:3000/」にアクセスしたときの表示が元に戻りました。
Viewで部分テンプレートを使う
ヘッダーやフッター、メニューなど、複数のページで共通の内容を表示するためには、Viewでrenderを使って部分テンプレートを呼び出します。
今回は、app/views/pages/index.html.erbテンプレート(indexビュー)にヘッダーとフッターを追加してみましょう。
各ファイル名は、以下のようにします。
項目 | ファイル名 |
---|---|
ヘッダー | app/views/pages/_header.html.erb |
フッター | app/views/pages/_footer.html.erb |
(1)app/views/pages/_header.html.erbを以下の内容で作成します。
☆ヘッダー☆
(2)app/views/pages/_footer.html.erbを以下の内容で作成します。
★フッター★
(3)app/views/pages/index.html.erbを編集します。
変更前:
Pages#index
Find me in app/views/pages/index.html.erb
変更後:
<%= render 'header'%>Pages#index
Find me in app/views/pages/index.html.erb
<%= render 'footer'%>
(4)ブラウザで「http://localhost:3000/」にアクセスします。
以下のように、「☆ヘッダー☆」や「★フッター★」が表示されていますね。
Viewでもrenderを使って、部分テンプレートを呼び出せましたね。
変数を渡す
Viewで部分テンプレートを使う場合も、Controllerで部分テンプレートを使う場合と同じように変数を渡せます。
先ほど作成した_post.html.erbをそのまま利用して、ViewでもPostテーブルのデータと、モデルと無関係のローカル変数を渡せることを確認しましょう。
(1)app/views/pages/index.html.erbを編集します。
変更前:
<%= render 'header'%>Pages#index
Find me in app/views/pages/index.html.erb
<%= render 'footer'%>
変更後:
<%= render 'header'%>Pages#index
Find me in app/views/pages/index.html.erb
<% @posts.each do |post| %> <%= render partial: "post", :locals => {:bgcolor => "lightsteelblue", :post => post} %> <% end %> <%= render 'footer'%>
(2)app/controllers/pages_controller.rbを編集します。
変更前:
class PagesController < ApplicationController def index end def help end end
変更後:
class PagesController < ApplicationController def index @posts = Post.all end def help end end
このように書くと、app/views/pages/index.html.erb(indexビュー)で必要な「@posts」をControllerから渡せます。
(3)ブラウザで「http://localhost:3000/」にアクセスします。
先ほどの例から色が変わっただけでなく、ヘッダーやフッターも付いたままですね!
JSONやXMLを返す
renderは、さまざまな方式でデータを出力できますので、その一例としてJSONやXMLを出力する方法も紹介しましょう。
今度はPosts Controllerを編集していますので、注意してくださいね!
(1)app/controllers/posts_controller.rbを以下のように編集します。
変更前:
class PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] # GET /posts # GET /posts.json def index @posts = Post.all end (省略)
変更後:
class PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] # GET /posts # GET /posts.json def index @posts = Post.all respond_to do |format| format.html format.json {render :json => @posts} format.xml {render :xml => @posts} end end (省略)
(2)ブラウザで「http://localhost:3000/posts.json」にアクセスします。
以下のように表示されます。
これは読みにくいので、整形すると以下のようになります。
[ { "id": 1, "user_id": 5, "title": "楽しい休日の過ごし方", "month": 3, "created_at": "2018-06-22T06:38:30.335Z", "updated_at": "2018-06-22T06:38:30.335Z" }, { "id": 2, "user_id": 1, "title": "先日の旅行での話", "month": 2, "created_at": "2018-06-22T06:38:30.405Z", "updated_at": "2018-06-22T06:38:30.405Z" }, (省略) ]
(3)続けてブラウザで「http://localhost:3000/posts.xml」にアクセスします。
以下のように表示されます。
確かにXMLではありますので文句は言えませんが、コレジャナイ感が半端ないです。
JSONと同じようなXMLを出力するには、activemodel-serializers-xmlが必要でした。
(4)Gemfileの最終行に以下の内容を追加します。
gem 'activemodel-serializers-xml'
(5)Railsサーバーを起動した端末でCtrlキーを押しながらCキーを押し、以下のコマンドを入力します。
bundle install
(6)gemをインストールしたら、以下のコマンドを入力します。
bin/rails server
Railsサーバーが起動します。
(7)もう一度ブラウザで「http://localhost:3000/posts.xml」にアクセスします。
これだ!
実は、app/controllers/posts_controller.rbを変更しなくても、JSONだけなら表示できます。
ただ、JSONだけでなくXMLも出力するなら、この手順で説明したようにposts_controller.rbを編集します。
まとめ
今回はRailsのrenderについて解説していきました。
ControllerでもViewでも、renderを使って部分テンプレートを使ったり、変数を使って値を渡したりできました。
renderは、さまざまな方式でデータを出力できますので、その一例としてJSONやXMLを出力する方法も確認しました。
Viewに同じようなHTMLを書くことになったら、renderで(主観的ですが)キレイにまとめることを検討します。
しかし、複雑なコードを書いてようやくまとめられる!というレベルのときは、Viewのコードをキレイにすることを目指して、Controllerでrenderを使うことも検討しましょう。
ControllerでもViewでも書けるのは便利な反面、役割分担がないがしろにされる一因にもなってしまいますので、十分に検討して、動作を追いかけやすいコードを書くように注意してください。
それでは!