Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Xử lý collection rỗng với .each trong Rails

Tôi yêu each, nhưng có vấn đề với nó. Collection rỗng thì sao?

Nếu gọi [].each, không gì xảy ra và trả về []. Đôi khi đó là thứ bạn muốn. Nhưng thường hơn, đặc biệt khi xây UI, bạn muốn xử lý list rỗng đặc biệt. Ví dụ, tôi thường muốn hiện message khác khi không có data.

Vì [].each trả về [], không phải nil, bạn buộc viết kiểu này:

app/views/users/index.html.erb
<% if @users.empty? -%>
  <p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>
<% else -%>
  <% @users.each do |user| -%>
    <p><%= user.name %></p>
  <% end -%>
<% end -%>

Được, nhưng cảm giác cồng kềnh. Tôi muốn nói: "Làm điều này cho mỗi item trong collection, trừ khi rỗng, thì làm điều khác hoàn toàn." Tôi muốn kiểu each, có "or else".

Render Rails Collections

Trong view, Rails giúp ích. Nó render collection cực tốt:

app/views/users/index.html.erb
<%= render @users %>
app/views/users/_user.html.erb
<p><%= user.name %></p>

Khi render nhận @users, nó render _user.html.erb lần lượt cho mỗi user trong @users. Bỏ qua each hoàn toàn.

Làm thêm, nếu @users rỗng, render trả về nil, đúng như mong muốn! Vậy viết thế này, ra output giống bản đầu:

app/views/users/index.html.erb
<%= render(@users) || render('empty') %>
app/views/users/_user.html.erb
<p><%= user.name %></p>
app/views/users/_empty.html.erb
<p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>

Rất trực tiếp, khi hiểu convention của Rails.

Ngoài view thì sao?

Nếu theo Rails, render với collection là cách nhanh, mạnh render collection object.

Nhưng đôi khi không muốn extra partial render mỗi item. Hoặc render không hỗ trợ design. Hoặc bạn even không ở trong view.

Giải pháp tốt nhất tôi tìm thấy là presence.

list.presence tương đương:

if list.present?
  list
else
  nil
end

Tức là, lấy list nếu có phần tử, nil nếu rỗng.

Với presence, bạn viết:

@users.each do |user|
  puts user.name
end.presence || puts("You don't have any users.")

In từng tên nếu @users có user, hoặc You don't have any users nếu không.

Vẫn, tôi muốn bỏ presence. Cảm giác như hack, vì nó là hack. Nếu được support, kiểu này có thể tốt hơn:

@users.each do |user|
  puts user.name
end || puts("You don't have any users.")

hoặc kiểu Smalltalk:

@users.each(if_empty: lambda { puts "You don't have any users." }) do |user|
  puts user.name
end

hoặc thậm chí (gasp!):

for user in @users
  puts user.name
else
  puts "You don't have any users."
end

Hiện tại, tôi thường dùng presence hoặc pattern if blank?; else each cơ bản.

Bạn nghĩ cách nào tốt hơn? Bạn xử lý list rỗng như thế nào? Tìm cách tốt hơn? Cho tôi biết!