Rails Render Views
Bài đăng này đã không được cập nhật trong 7 năm
Giới thiệu chung
Render partial thường được sử dụng nhằm mục đích hạn chế sự trùng lặp code ở phần view cũng như để thuận tiện hơn trong việc tái sử dụng. Partial (phần) hay còn gọi là Partial template, được hiểu như một cấu trúc có thể tách quá trình rendering một trang ra thành nhiều phần nhỏ dễ quản lí hơn. Với việc sử dụng Partial, lập trình viên có thể tùy ý sử dụng phần được render đó để gọi ở bất cứ đâu trên view theo ý muốn.
Đặt tên cho Partial
Để render ra một partial như một phần của view và không cần biến truyền vào, chúng ta có thể dùng cú pháp khá đơn giản:
<%= render "form" %>
Câu lệnh này rails sẽ mặc định hiểu sẽ render nội dung ở trong file có tên dạng _form.html.erb
ở trong phần view của controller tương ứng. Dấu gạch dưới trước tên partial là một cách để rails phân biệt giữa partial với các file view thông thường.
Tuy nhiên để render ra một partial nằm ở các thư mục khác trong views, thay vì render ra tên partial như trên, chúng ta thêm đường dẫn đến partial đó. Giả sử chúng ta cần render ra partial "form" nằm trong thư mục views/shared:
<%= render "shared/form" %>
Rails sẽ tự động hiểu và render partial ở đường dẫn app/views/shared/_form.html.erb
.
Dùng Partial để đơn giản hóa Views và tái sử dụng
Một trong những cách để áp dụng partial đó là chia bố cục ở view thành từng phần độc lập, mỗi 1 phần đó tương ứng với 1 partial, nhằm tối giản view để dễ kiểm soát hơn. Vd như chúng ta có một danh sách user. Xét thử 1 file views/users/index.html.erb
:
<% provide :title, "Users list") %>
<h1>All Users</h1>
<%= form_for users_path, method: :get do %>
<div class="container">
<div class="row">
<div class="col-md-6">
<div id="custom-search-input">
<div class="input-group col-md-12">
<%= text_field_tag :search, params[:search], placeholder: "search here",
class: "form-control" %>
<span class="input-group-btn">
<%= button_tag "search", class: "btn btn-primary" %>
</span>
</div>
</div>
</div>
</div>
</div>
<% end %>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.age %></td>
</tr>
<% end %>
</tbody>
</table>
Nhìn cũng không có gì phức tạp, chỉ có 2 phần chính là form search cơ bản và liệt kê ra danh sách user gồm tên và tuổi. Tuy nhiên nếu như trong trường hợp user cần show ra nhiều thông tin hơn, hoặc chức năng search cần phức tạp hơn, hay đơn giản là muốn view có giao diện đẹp hơn, thì số dòng code sẽ rất lớn và phức tạp hơn rất nhiều. Mọi chuyện sẽ đơn giản hơn nếu như chúng ta chia làm 2 partial, 1 là partial _search.html.erb
, và 2 là partial _user.html.erb
.
#_search.html.erb
<div class="container">
<div class="row">
<div class="col-md-6">
<div id="custom-search-input">
<div class="input-group col-md-12">
<%= text_field_tag :search, params[:search], placeholder: "search here",
class: "form-control" %>
<span class="input-group-btn">
<%= button_tag "search", class: "btn btn-primary" %>
</span>
</div>
</div>
</div>
</div>
</div>
và
#_user.html.erb
<tr>
<td><%= user.name %></td>
<td><%= user.age %></td>
</tr>
rồi gọi chúng ra ở index:
<% provide :title, "Users list" %>
<h1>All Users</h1>
<%= form_for users_path, method: :get do %>
<%= render "search" %>
<% end %>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
</tr>
</thead>
<tbody>
<%= render partial: "user", collection: @users, as: "user" %>
</tbody>
</table>
Nhìn đã gọn hơn rồi, và trong trường hợp chúng ta có thêm danh sách book, author, post..vv.. và mỗi file index lại cần đến chức năng search và show thông tin user thì render partial đã phát huy tác dụng của nó khi chúng ta chỉ việc render đến _search.html.erb
và _user.html.erb
ở bất cứ view nào cần dùng đến.
Có điều, ở trong file user index trên có đoạn render nhìn có vẻ phức tạp:
<%= render partial: "user", collection: @users, as: "user" %>
Đó chính là cách truyền biến vào partial trong render.
Passing Local Variables
Rails hỗ trợ render có thể truyền các biến local vào partial để chúng thêm mạnh mẽ và linh hoạt. Chúng ta có thể dùng object
để khai báo 1 variable vào partial, và as
để customize tên biến truyền vào. Ví dụ đơn giản nhất là trong trường hợp tạo mới và chỉnh sửa profile user:
ở views/users/new.html.erb
:
#new.html.erb
<h1>Sign Up</h1>
<%= render partial: "form", object: @user, as: "user" %>
ở views/user/edit.html.erb
:
#edit.html.erb
<h1>Editing Profile</h1>
<%= render partial: "form", object: @user, as: "user" %>
và ở partial _form.html.erb
chúng ta có thể dùng user
thay cho @user
<%= form_for user do |f| %>
<p>
<b>user name</b><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
option as
rất thú vị trong trường hợp render partial ở nhiều chỗ và mỗi chỗ lại có một tên object truyền vào khác nhau.
Đấy là trường hợp truyền 1 biến vào partial, còn trong trg hợp biến truyền vào là 1 tập hợp, thì chúng ta sẽ dùng collection
để khai báo. Cụ thể như vd đầu tiên, chúng ta cần gọi ra tất cả các users để lấy ra thông tin tên và tuối.:
<tbody>
<%= render partial: "user", collection: @users, as: "user" %>
</tbody>
Về cú pháp không có vấn đề gì, tuy nhiên có vẻ như hơi lặp nhiều từ "user", rails có hỗ trợ cú pháp đơn giản hơn cho trường hợp này:
<%= render @users %>
Đơn giản đến mức tối đa, nhưng mà làm sao để Rails hiểu được chúng ta muốn render ra partial _user.html.erb
với 1 loạt biến @users
và có tên biến chung là user
?
Thực ra thì trong Rails tất cả partial đều mặc định có 1 biến local variable với tên trùng với tên của partial, tức là partial _user.html.erb
sẽ luôn có sẵn local variable là user
. Và trong trường hợp trên, Rails sẽ xác định ra partial cần render thông qua việc tìm từng tên của model trong tập hợp các biến đó.
Trong trường hợp collection
trả về empty, render sẽ trả về giá trị nil, cho nên cũng khá đơn giản để đưa ra các nội dung thay thế:
#(Search) index.html.erb
<h1>Search Result</h1>
<%= render(@users) || "Khong co ket qua phu hop!" %>
Còn 1 điều khá thú vị nữa là Rails cũng cung cấp sẵn cho partial 1 biến đếm (counter variable) được gọi thông qua collection
và lấy tên theo tên tập hợp biến đi kèm với _counter
. Nếu như thông thường, để đánh stt index cho một danh sách, chúng ta có thể dùng each_with_index
kèm với một method helper riêng chứa biến index, thì giờ đây với biến đếm mà Rails cung cấp sẵn cho partial, chúng ta chỉ việc thêm vào [partial_name]_counter
là được:
#_user.html.erb
<tr>
<td><%= user_counter %></td>
<td><%= user.name %></td>
<td><%= user.age %></td>
</tr>
và
#index.html.erb
<table class="table">
<thead>
<tr>
<th>No.</th>
<th>name</th>
<th>age</th>
</tr>
</thead>
<tbody>
<%= render @users %>
</tbody>
</table>
Lời kết
Kỹ thuật Rails render nói chung và render view nói riêng khá đa dạng và hữu dụng, bài viết này chỉ là một phần rất nhỏ về kỹ thuật render view, còn nhiều hạn chế và thiếu sót. Bài viết được tham khảo từ nguồn: http://guides.rubyonrails.org/layouts_and_rendering.html
All rights reserved