Dynamically Replacing Components Based on User Actions
Learn to add dynamic fields in our form using React.
Detecting elements using plain JavaScript
To detect events in plain JavaScript, we’d add the onchange
attribute in line 7 to our select element, setting its value to the JavaScript code we’d like to execute. This is exactly how it works in React as well, except that we use the attribute onChange
(note the capitalized “C”):
import React from 'react'class PayTypeSelector extends React.Component {render() {return (<div className="field"><label htmlFor="order_pay_type">Pay type</label><select onChange={this.onPayTypeSelected} name="order[pay_type]"><option value="">Select a payment method</option><option value="Check">Check</option><option value="Credit card">Credit card</option><option value="Purchase order">Purchase order</option></select></div>);}}
Note that we aren’t quoting the value to onChange
but are instead using curly
braces. This is another feature of JSX and is part of making the view
dynamic. Curly braces allow us to interpolate JavaScript, much like how #{...}
does in Ruby or <%= ... %>
does in ERB. React knows to put quotes in the
right places when the HTML is rendered.
Using React component
We can now define the method onPayTypeSelected()
in line 3, like so:
import React from 'react'class PayTypeSelector extends React.Component {onPayTypeSelected(event) {console.log(event.target.value);}
This implementation demonstrates how we can access the user’s selection.
The event
passed in is a synthetic event, which has a property target
that is a
DOMEventTarget
. This in turn has a property value that has the value
of the
selected payment type.
If we run the following application, open the JavaScript console, and select different payment types, we should see messages in the console.
#--- # Excerpted from "Agile Web Development with Rails 6", # published by The Pragmatic Bookshelf. # Copyrights apply to this code. It may not be used to create training material, # courses, books, articles, and the like. Contact us if you are in doubt. # We make no guarantees that this code is fit for any purpose. # Visit http://www.pragmaticprogrammer.com/titles/rails6 for more book information. #--- 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 store_index_url, notice: 'Thank you for your order.' } 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 #... private def ensure_cart_isnt_empty if @cart.line_items.empty? redirect_to store_index_url, notice: 'Your cart is empty' end end def pay_type_params if order_params[:pay_type] == "Credit card" params.require(:order).permit(:credit_card_number, :expiration_date) elsif order_params[:pay_type] == "Check" params.require(:order).permit(:routing_number, :account_number) elsif order_params[:pay_type] == "Purchase order" params.require(:order).permit(:po_number) else {} end end end
What do we do with this new method? If you recall, a React component is a
view and state. When the state changes, the view is rerendered by calling the
component’s render()
method. We want the view to be rerendered when the
user changes payment types, so we need to get the currently selected payment
type into the component’s state.
We can do this ...