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.
Cara yang umum, untuk menampilkan output seperti di atas, adalah seperti ini.
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
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
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.
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
.
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>
.
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.
1
document.querySelector('#results').innerHTML = "<%= escape_javascript(render 'users/result.html', stock: @stock) %>"
Terakhir, tinggal menambahkan asynchronous form pada form pencarian dengan remote: true
.
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