BanditHijo.dev

Membuat Form's Output dengan AJAX

Created at: 2020-12-01
Author by: BanditHijo

Latar Belakang

Tujuannya menggunakan AJAX untuk menampilkan hasil tanpa perlu mereload tempate berulang kali. Cukup sekali panggil dan yang berubah adalah pada bagian hasilnya saja.

Gambar 1

Gambar 1. Kotak merah adalah satu-satunya bagian yang berubah, sedangkan bagian lain tidak

Cara yang umum, untuk menampilkan output seperti di atas, adalah seperti ini.

app/controllers/stocks_controller.rb
1class StocksController < ApplicationController
2 def index
3 @tracked_stocks = current_user.stocks
4 end
5
6 def search
7 if params[:stock].present?
8 @stock = Stock.new_lookup(params[:stock].upcase)
9 if @stock
10 render 'users/my_portfolio'
11 else
12 flash[:alert] = 'Please enter a VALID symbol to search'
13 redirect_to my_portfolio_path
14 end
15 else
16 flash[:alert] = 'Please enter a symbol to search'
17 redirect_to my_portfolio_path
18 end
19 end
20end
config/routes.rb
1Rails.application.routes.draw do
2 root 'welcome#index'
3 devise_for :users
4
5 get 'my_portfolio', to: 'stocks#index'
6 get 'search_stock', to: 'stocks#search'
7end
app/views/stocks/index.html.erb
1<h3>Search Stocks</h3>
2<%= form_tag search_stock_path, method: :get do %>
3 <div class="input-group">
4 <%= text_field_tag :stock, params[:stock],
5 class: "form-control form-control-lg",
6 placeholder: "Stock ticker symbol",
7 onkeyup: "this.value = this.value.toUpperCase();",
8 autofocus: true %>
9 <div class="input-group-append">
10 <%= button_tag type: :submit, class: "btn btn-success" do %>
11 <%= fa_icon "search 2x" %>
12 <% end %>
13 </div>
14 </div>
15<% end %>
16
17<% if @stock %>
18 <div class="alert alert-success">
19 <div class="row d-flex justify-content-between">
20 <div class="col-sm-9 align-self-center">
21 <strong>Symbol: </strong><%= @stock.ticker %>
22 <strong>Name: </strong><%= @stock.name %>
23 <strong>Price: </strong><%= @stock.last_price %>
24 </div>
25 <div class="col-sm-3">
26 <%= link_to "Add to Portfolio", stocks_path(user: current_user, ticker: @stock.ticker),
27 method: :post,
28 class: "btn btn-success btn-block mx-2" %>
29 </div>
30 </div>
31 </div>
32<% end %>
33
34<% unless @tracked_stocks.empty? %>
35 <table class="table table-borderless table-hover my-3">
36 <thead>
37 <tr>
38 <th>Ticker</th>
39 <th>Name</th>
40 <th>Price</th>
41 <th>Action</th>
42 </tr>
43 </thead>
44 <tbody>
45 <% @tracked_stocks.each do |stock| %>
46 <tr>
47 <td><%= stock.ticker %></td>
48 <td><%= stock.name %></td>
49 <td><%= stock.last_price %></td>
50 <td>'Action Placeholder'</td>
51 </tr>
52 <% end %>
53 </tbody>
54 </table>
55<% end %>

Setelah form diinputkan, dan hasil ditampilkan, akan mendapatkan URL seperti ini.

http://localhost:3000/search_stock?stock=AMZN&button=

Apabila kita menginputkan nilai yang lain, maka template akan ikut dirender untuk menampilkan hasil pencarian yang baru.

http://localhost:3000/search_stock?stock=GOOG&button=

Nah, pada catatan kali ini, saya akan membuat template dirender sekali saja dan hanya pada bagian yang menampilkan hasil pencarian yang dirender berkali-kali.

Pemecahan Masalah

Pertama-tama, saya akan merubah output di stocks_controller pada action search menjadi format Javascript.

app/controllers/stocks_controller.rb
1class StocksController < ApplicationController
2 def index
3 @tracked_stocks = current_user.stocks
4 end
5
6 def search
7 if params[:stock].present?
8 @stock = Stock.new_lookup(params[:stock].upcase)
9 if @stock
10 respond_to do |format|
11 format.js { render partial: 'users/result' }
12 end
13 else
14 respond_to do |format|
15 flash.now[:alert] = 'Please enter a VALID symbol to search'
16 format.js { render partial: 'users/result' }
17 end
18 end
19 else
20 respond_to do |format|
21 flash.now[:alert] = 'Please enter a symbol to search'
22 format.js { render partial: 'users/result' }
23 end
24 end
25 end
26end

Kemudian, pada bagian view, pisahkan bagian result, menjadi render partial, saya beri nama _result.html.erb.

app/views/stocks/_result.html.erb
1<% if stock %>
2 <div class="alert alert-success">
3 <div class="row d-flex justify-content-between">
4 <div class="col-sm-9 align-self-center">
5 <strong>Symbol: </strong><%= stock.ticker %>
6 <strong>Name: </strong><%= stock.name %>
7 <strong>Price: </strong><%= stock.last_price %>
8 </div>
9 <div class="col-sm-3">
10 <%= link_to "Add to Portfolio", stocks_path(user: current_user, ticker: stock.ticker),
11 method: :post,
12 class: "btn btn-success btn-block mx-2" %>
13 </div>
14 </div>
15 </div>
16<% end %>

Pada bagian yang kita pindahkan (kode di atas) di view stocks/index.html.erb, kita ganti dengan <div id=results>.

app/views/stocks/index.html.erb
1...
2...
3
4<div id="results"></div>

Kita akan meletakkan hasil yang diberikan oleh controller pada div denga id=result tersebut.

Kita akan atur di dalam file javascript, buat file _result.js.erb.

app/views/stocks/_result.js.erb
1document.querySelector('#results').innerHTML = "<%= escape_javascript(render 'users/result.html', stock: @stock) %>"

Terakhir, tinggal menambahkan asynchronous form pada form pencarian dengan remote: true.

app/views/stocks/index.html.erb
1<h3>Search Stocks</h3>
2<%= form_tag search_stock_path, remote:true, method: :get do %>
3 <div class="input-group">
4 <%= text_field_tag :stock, params[:stock],
5 class: "form-control form-control-lg",
6 placeholder: "Stock ticker symbol",
7 onkeyup: "this.value = this.value.toUpperCase();",
8 autofocus: true %>
9 <div class="input-group-append">
10 <%= button_tag type: :submit, class: "btn btn-success" do %>
11 <%= fa_icon "search 2x" %>
12 <% end %>
13 </div>
14 </div>
15<% end %>
16
17...
18...

Hasilnya,

Gambar 2

Gambar 2. Hasil jadinya

Kalau diperhatikan pada bagian address bar, alamat tidak berubah. Karena kita tidak merender template lagi untuk menampilkan hasil, namun hanya merubah bagian yang memiliki <div id=results></div>.

Pesan Penulis

Sepertinya, segini dulu yang dapat saya tuliskan.

Mudah-mudahan dapat bermanfaat.

Terima kasih.

(^_^)