بسم الله الرحمن الرحيم

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.

FILEapp/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
FILEconfig/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
FILEapp/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.

FILEapp/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.

FILEapp/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>.

FILEapp/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.

FILEapp/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.

FILEapp/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,

gambar_2

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.

(^_^)


Penulis

bandithijo

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

d98d8237fef8f1017d0be931b6e291341cbe6ca8