<<前 [TOP] 次>>
支払い方法はそれぞれ入力する内容に違いがあります。
これを実装するために「Webpacker」というアプリケーションを利用します。
「Webpacker」は作成するJavaScriptファイルを管理するためのツールです。
「Webpacker」をインストールするにはコマンドプロンプトで「bin」フォルダに移動し、「rails webpacker:install:react
」と入力いします。
「Webpacker」を使用するためには「javascript_pack_tag()」の記述が必要です。
「C:\Rails6\work\shop\app\views\orders」フォルダの「new.html.erb」ファイルに「<%= javascript_pack_tag("hello_react") %>」という記述を追加してみます。
【C:\Rails6\work\shop\app\views\orders\new.html.erb】
<section class="shop_form"> <h1>お客様情報を入力してください。</h1> <%= render 'form', order: @order %> </section> <%= javascript_pack_tag("hello_react") %>
ページ下部に「Hello React!」と表示されれば「Webpacker」が使用できているということになります。
既存のドロップダウンをReactとWebpackerが管理するJavascriptを利用したものに置き換えます。
ReactはJavaScriptのビューライブラリです。
ReactはブラウザーでHTMLを動的にレンダリングします。
選択した支払いタイプをページのごく一部にのみ影響するようにし、サーバー全体に再レンダリングしないようにできます。
Webpackを利用してドロップダウンを作成します。
ReactとWebpackの操作方法を理解するためにRalisによってレンダリングされている既存のものをReactによってレンダリングされるものに置き換えます。
それには次の手順で行います。
- 実装のルートとなる新しいパック「pay_type」を作成します。
- 既存の支払いタイプドロップダウンを置き換えるために使用する「PayTypeSelector」コンポーネントを作成します。
- 「javascript_pack_tag()」とReactがコンポーネントをレンダリングできるのを使用して、コンポーネントをチェックアウトビューに入れます。
では最初に新しいパック「pay_type」を作成します。 「C:\Rails6\work\shop\app\javascript\packs」フォルダに「pay_type.jsx」ファイルを新規作成します。
import React from 'react' import ReactDOM from 'react-dom' import PayTypeSelector from 'PayTypeSelector' document.addEventListener('turbolinks:load', function() { var element = document.getElementById("pay-type-component"); ReactDOM.render(<PayTypeSelector />, element); });
「import React from 'react'」の部分はメインのReactライブラリにアクセスする方法です。
インポートはRubyのreuire()に似ていて、他のファイルにあるコードにアクセスできるようにしています。
ブラウザはそれをサポートしていません。
この行を処理するとファイル「react.js」を見つけようとします。
「import ReactDOM from 'react-dom'」の行ではreactDOMオブジェクトが組み込まれます。
このオブジェクトにはReactコンポーネントを使用するために必要なrender()関数があります。
「import PayTypeSelector from 'PayTypeSelector'」の行では次に作成するコンポーネントであるPayTypeSelectorをインポートしています。
「document.addEventListener('turbolinks:load', function() {」の行はドキュメントで使用可能な標準関数「addEventListener()」を使用して、実行しようとしているコードがDOM全体がロードされた後にのみ実行されるようにします。
Turbolinkの仕組みによりページがリロードされるたびにそのイベントが発生するわけではありません。
Turbolinkはページ読み込みイベントを管理し、代わりに「turbolinks:load」イベントを発生させます。
DOMContentLoadedを使用してページから移動して「戻る」ボタンを使用するとページはReactを設定せず何も機能しません。
「turbolinks:load」を使用するとページがレンダリングされるたびにReactがセットアップされます。
「 var element = document.getElementById("pay-type-component");」の行は「pay-type-component」を持つ要素を検索しています。
「ReactDOM.render(
JSXファイルでは「PayTypeSelector」を介して行われます。
しかしWebpackがJSXファイルをコンパイルするときにブラウザーで機能するJavaScriptを生成します。
上記のインポート行で「PaTypeSelector」を使用したため機能します。
実際にPayTypeSelectorコンポーネントの作成を行います。
「C:\Rails6\work\shop\app\javascript」フォルダに「PayTypeSelector」フォルダを新規作成し、以下のような「index.jsx」という名前のファイルを作成します。
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/index.jsx】
import React from 'react' class PayTypeSelector extends React.Component { render() { return ( <div className="field"> <label htmlFor="order_pay_type">支払い方法</label> <select id="order_pay_type" onChange={this.onPayTypeSelected} name="order[pay_type]"> <option value="">支払い方法を選択してください。</option> <option value="Check">現金</option> <option value="Credit card">クレジットカード</option> <option value="Purchase order">着払い</option> </select> </div> ); } } export default PayTypeSelector
PayTypeSelectorコンポーネントをRailsビューに取り込みます。
「C:\Rails6\work\shop\app\views\orders」フォルダにある「new.html.erb」の記述を「<%= javascript_pack_tag("pay_type") %>」に変更します。
【C:\Rails6\work\shop\app\views\orders\new.html.erb】
<section class="shop_form"> <h1>お客様情報を入力してください。</h1> <%= render 'form', order: @order %> </section> <%= javascript_pack_tag("pay_type") %>
あわせて「_form.html.erb」ファイルの記述も変更します。
「
【C:\Rails6\work\shop\app\views\orders\_form.html.erb】
<%= form_with(model: order, local: true) do |form| %> <% if order.errors.any? %> <div id="error_explanation2"> <h2><%= pluralize(order.errors.count, "error") %> この注文の保存ができません。</h2> <ul> <% order.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :氏名 %> <%= form.text_field :name, size: 40 %> </div> <div class="field"> <%= form.label :住所 %> <%= form.text_area :address, rows: 3, cols: 40 %> </div> <div class="field"> <%= form.label :email %> <%= form.text_field :email, size: 40 %> </div> <div id='pay-type-component'></div> <div class="actions"> <%= form.submit '注文を確定する' %> </div> <% end %>
先程作成した「javascript\PayTypeSelector/index.jsx」ファイルに「 onPayTypeSelected(event)」の記述を追加します。
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/index.jsx】
import React from 'react' class PayTypeSelector extends React.Component { onPayTypeSelected(event) { this.setState({ selectedPayType: event.target.value }); } render() { return ( <div className="field"> <label htmlFor="order_pay_type">支払い方法</label> <select id="order_pay_type" onChange={this.onPayTypeSelected} name="order[pay_type]"> <option value="">支払い方法を選択してください。</option> <option value="Check">現金</option> <option value="Credit card">クレジットカード</option> <option value="Purchase order">着払い</option> </select> </div> ); } } export default PayTypeSelector
これで「支払い方法選択フォーム」がReactとWebpackerが管理するJavascriptを利用したものに置き換わりました。
まだ「orders_controller.rb」に「pay_type_params()」メソッドの記述をしていないので注文の保存はできません。
支払い方法を選択した時に詳細入力ができるように記述を変更していきます。
「javascript\PayTypeSelector/index.jsx」ファイルに「constructor()」メソッド、「let PayTypeCustomComponent」の記述、「
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/index.jsx】
import React from 'react' import NoPayType from './NoPayType'; import CreditCardPayType from './CreditCardPayType'; import CheckPayType from './CheckPayType'; import PurchaseOrderPayType from './PurchaseOrderPayType'; class PayTypeSelector extends React.Component { constructor(props) { super(props); this.onPayTypeSelected = this.onPayTypeSelected.bind(this); this.state = { selectedPayType: null }; } onPayTypeSelected(event) { this.setState({ selectedPayType: event.target.value }); } render() { let PayTypeCustomComponent = NoPayType; if (this.state.selectedPayType == "現金") { PayTypeCustomComponent = CreditCardPayType; } else if (this.state.selectedPayType == "クレジットカード") { PayTypeCustomComponent = CheckPayType; } else if (this.state.selectedPayType == "着払い") { PayTypeCustomComponent = PurchaseOrderPayType; } return ( <div> <div className="field"> <label htmlFor="order_pay_type">支払い方法</label> <select id="order_pay_type" onChange={this.onPayTypeSelected} name="order[pay_type]"> <option value="">支払い方法を選択してください。</option> <option value="現金">現金</option> <option value="クレジットカード">クレジットカード</option> <option value="着払い">着払い</option> </select> </div> <PayTypeCustomComponent /> </div> ); } } export default PayTypeSelector
同じく「C:\Rails6\work\shop\app\javascript\PayTypeSelector/」フォルダに以下のような「NoPayType.jsx」「CheckPayType.jsx」「CreditCardPayType.jsx」「PurchaseOrderPayType.jsx」の4つのファイルを新規作成します。
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/NoPayType.jsx(新規作成)】
import React from 'react' class NoPayType extends React.Component { render() { return (<div></div>); } } export default NoPayType
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/CheckPayType.jsx(新規作成)】
import React from 'react' class CheckPayType extends React.Component { render() { return ( <div> <div className="field"> <label htmlFor="order_routing_number">支払時期</label> <select id="order_routing_number" name="order[routing_number]"> <option value="">支払い時期を選択してください。</option> <option value="前払い">前払い</option> <option value="後払い">後払い</option> </select> </div> </div> ); } } export default CheckPayType
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/CreditCardPayType.jsx(新規作成)】
import React from 'react' class CreditCardPayType extends React.Component { render() { return ( <div> <div className="field"> <label htmlFor="order_credit_card_number">カード番号</label> <input type="password" name="order[credit_card_number]" id="order_credit_card_number" /> </div> <div className="field"> <label htmlFor="order_expiration_date">有効期限</label> <input type="text" name="order[expiration_date]" id="order_expiration_date" size="9" placeholder="月/年" /> </div> </div> ); } } export default CreditCardPayType
【C:\Rails6\work\shop\app\javascript\PayTypeSelector/PurchaseOrderPayType.jsx(新規作成)】
import React from 'react' class PurchaseOrderPayType extends React.Component { render() { return ( <div> <div className="field"> <label htmlFor="order_po_number">希望宅配業者</label> <input type="text" name="order[po_number]" id="order_po_number" /> </div> </div> ); } } export default PurchaseOrderPayType
これで支払い方法を選択した時にそれぞれに対応した入力フォームが表示されます。
最後にコントローラに「pay_type_params」メソッドの記述を行います。
【C:\Rails6\work\shop\app\controllers\orders_controller.rb】
class OrdersController < ApplicationController include CurrentCart before_action :set_cart, only: [:new, :create] before_action :ensure_cart_isnt_empty, only: :new before_action :set_order, only: [:show, :edit, :update, :destroy] # GET /orders # GET /orders.json def index @orders = Order.all end # GET /orders/1 # GET /orders/1.json def show end # GET /orders/new def new @order = Order.new end # GET /orders/1/edit def edit end # POST /orders # POST /orders.json def create @order = Order.new(order_params) @order.add_line_items_from_cart(@cart) respond_to do |format| if @order.save Cart.destroy(session[:cart_id]) session[:cart_id] = nil format.html { redirect_to market_index_url, notice: 'ご注文ありがとうございました。' } format.json { render :show, status: :created, location: @order } else format.html { render :new } format.json { render json: @order.errors, status: :unprocessable_entity } end end end # PATCH/PUT /orders/1 # PATCH/PUT /orders/1.json def update respond_to do |format| if @order.update(order_params) format.html { redirect_to @order, notice: 'Order was successfully updated.' } format.json { render :show, status: :ok, location: @order } else format.html { render :edit } format.json { render json: @order.errors, status: :unprocessable_entity } end end end # DELETE /orders/1 # DELETE /orders/1.json def destroy @order.destroy respond_to do |format| format.html { redirect_to orders_url, notice: 'Order was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_order @order = Order.find(params[:id]) end # Only allow a list of trusted parameters through. def order_params params.require(:order).permit(:name, :address, :email, :pay_type) end def ensure_cart_isnt_empty if @cart.line_items.empty? redirect_to market_index_url, notice: 'カートは空です。' end end def pay_type_params if order_params[:pay_type] == "クレジットカード" params.require(:order).permit(:credit_card_number, :expiration_date) elsif order_params[:pay_type] == "現金" params.require(:order).permit(:routing_number, :account_number) elsif order_params[:pay_type] == "着払い" params.require(:order).permit(:po_number) else {} end end end
これで「注文を確定する」ボタンが機能するようになりました。
↓↓クリックして頂けると励みになります。