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

Atribusi-NonKomersial-BerbagiSerupa 4.0 Internasional (CC BY-NC-SA 4.0)
Penulis

My journey kicks off from reading textbooks as a former Medical Student to digging bugs as a Software Engineer – a delightful rollercoaster of career twists. Embracing failure with the grace of a Cat avoiding water, I've seamlessly transitioned from Stethoscope to Keyboard. Armed with ability for learning and adapting faster than a Heart Beat, I'm on a mission to turn Code into a Product.
- Rizqi Nur Assyaufi