Rails: элегантный способ отображения сообщения, когда в базе данных нет элементов.

Я понял, что пишу много кода, похожего на этот:

<% unless @messages.blank? %>
  <% @messages.each do |message|  %>
    <%# code or partial to display the message %>
  <% end %>
<% else %>
  You have no messages.
<% end %>

Есть ли в Ruby и/или Rails какая-нибудь конструкция, которая позволила бы мне пропустить это первое условие? Так что это будет выполнено, когда итератор/цикл не войдет ни разу? Например:

<% @messages.each do |message| %>
  <%# code or partial to display the message %>
<% and_if_it_was_blank %>
  You have no messages.
<% end %>

person Jakub Troszok    schedule 22.06.2009    source источник


Ответы (10)


Если вы используете параметр :collection для рендеринга, например. render :partial => 'message', :collection => @messages, то вызов render вернет nil, если коллекция пуста. Затем это можно включить в || выражение напр.

<%= render(:partial => 'message', :collection => @messages) || 'You have no messages' %>

Если вы еще не сталкивались с этим, рендеринг :collection отображает коллекцию, используя один и тот же фрагмент для каждого элемента, делая каждый элемент @messages доступным через локальную переменную message по мере создания полного ответа. Вы также можете указать разделитель, который будет отображаться между каждым элементом, используя :spacer_template => "message_divider"

person mikej    schedule 22.06.2009
comment
отлично... как насчет до сбора и после сбора? скажем, вы хотите иметь пары тегов ‹ul› и ‹/ul› или ‹tr›/tr› до и после частичного рендеринга, но только если @messages не пусты. примеры- › ‹p›‹ul›‹li›message1‹/li›‹message2›‹/ul›‹p› is @messages!=nil ИЛИ ‹p›‹ul›нет сообщений!‹p› - person mataal; 23.06.2009
comment
Я думаю, что решение Фернандо Аллена следует добавить к этому ответу как возможную альтернативу, поскольку люди могут пропустить его, потому что это не лучший ответ. - person arikfr; 13.02.2011
comment
просто краткое примечание, которое может быть полезным. чтобы этот синтаксис работал, вы должны использовать круглые скобки вокруг присваивания partial, как показано выше. без них частичное отображается правильно, но условное сообщение не - person pruett; 18.07.2012

Вы также можете написать что-то вроде этого:

<% if @messages.each do |message| %>
  <%# code or partial to display the message %>
<% end.empty? %>
  You have no messages.
<% end %>
person Fernando Allen    schedule 04.06.2010
comment
Мне нравится этот ответ. Я использовал этот из всех сам, супер чистый и очень понятный. - person James F; 17.01.2012
comment
Отличный ответ, очень лаконичный. - person Shane; 17.02.2014
comment
Как бы вы сделали что-то подобное в haml/slim? - person DickieBoy; 04.11.2014
comment
Спасибо! Принятый ответ больше похож на Rails (и, вероятно, должен быть принятым ответом именно по этой причине), но этот сэкономит мне много времени, пока я создаю прототип. - person user435779; 10.11.2014
comment
Как называется эта функция? - person onmyway133; 08.02.2015
comment
это именно то, что я искал! - person mArtinko5MB; 26.08.2020

Я удивлен, что моего любимого ответа здесь нет. Есть ответ, который близок, но мне не нравится голый текст, а использование content_for неуклюже. Примерьте это на размер:

  <%= render(@user.recipes) || content_tag("p") do %>
    This user hasn't added any recipes yet!
  <% end %>
person David Bock    schedule 08.04.2013
comment
Это мой любимый ответ на данный момент. - person dkubb; 06.11.2013
comment
Важно: не забывайте круглые скобки для render, иначе || будет применяться не к результату render, а к самой коллекции. Я сам только что написал то же самое, но это не сработало (сначала) из-за этого. - person D-side; 30.12.2014
comment
Это было элегантное решение, которое я искал :) - person markquezada; 28.10.2015

Один из способов - сделать что-то вроде:

<%= render(:partial => @messages) || render('no_messages') %>

Редактировать:

Если я правильно помню, это стало возможным благодаря этой фиксации:

http://github.com/rails/rails/commit/a8ece12fe2ac7838407954453e0d31af6186a5db

person jonnii    schedule 22.06.2009

Вы можете создать собственный помощник. Следующий пример — это всего лишь пример.

# application_helper.html.erb
def unless_empty(collection, message = "You have no messages", &block)
  if collection.empty?
    concat(message)
  else
    concat(capture(&block))
  end
end

# view.html.erb
<% unless_empty @messages do %>
  <%# code or partial to dispaly the message %>
<% end %>
person Simone Carletti    schedule 22.06.2009

В качестве примечания вы можете просто перебирать пустой массив, если вам нужна эффективность выражения:

<% @messages.each do |message|  %>
  <%# code or partial to dispaly the message %>
<% end %>
<% if (@messages.blank?) %>
  You have no messages.
<% end %>

Хотя это не обрабатывает @messages как nil, это должно работать в большинстве ситуаций. Внедрение нестандартных расширений в то, что должно быть рутинным представлением, вероятно, усложняет в остальном простую вещь.

Что может быть лучшим подходом, так это определить партиал и помощник для рендеринга «пустых» разделов, если они достаточно сложны:

<% render_each(:message) do |message|  %>
  <%# code or partial to dispaly the message %>
<% end %>

# common/empty/_messages.erb
You have no messages.

Где вы можете определить это как:

def render_each(item, &block)
  plural = "#{item.to_s.pluralize}"
  items = instance_variable_get("@#{plural}")
  if (items.blank?)
    render(:partial => "common/empty/#{plural}")
  else
    items.each(&block)
  end
end
person tadman    schedule 22.06.2009

Старая тема, но мне не очень нравилась ни одна из них, поэтому, поигравшись с Rails 3.2, я придумал эту альтернативу:

<% content_for :no_messages do %>
  <p>
    <strong>No Messages Found</strong>
  </p>
<% end %>

<%= render @messages || content_for(:no_messages) %>

Или, если вам нужен более подробный рендеринг с частичным путем, как я:

<%= render(:partial => 'messages', 
     :collection => @user.messages) || content_for(:no_messages) %>

Таким образом, вы можете стилизовать часть «без сообщений» с любой логикой HTML/представления, которую вы хотите, и сохранить ее удобной для чтения.

person Kansha    schedule 23.01.2013

Этот код можно сократить до:

<%= @messages.empty? ? 'You have no messages.' : @messages.collect { |msg| formatted_msg(msg) }.join(msg_delimiter) %>

Комментарии:

formatted_msg() — вспомогательный метод, который добавляет форматирование к сообщению

msg_delimiter - переменная, содержащая разделитель, например "\n" или "<br />"

Кстати, я бы предложил использовать пустой? метод вместо пустого? для проверки массива, т.к. а) его имя лаконичнее :) и б) пусто? — это метод расширения ActiveSupport, который не будет работать вне Rails.

person Lukas Stejskal    schedule 22.06.2009
comment
Спасибо за предложение, но я предпочитаю использовать пустое? потому что мне не нужно проверять, не является ли объект нулевым, и в этом случае меня не сильно беспокоит то, что это только расширение, специфичное для рельсов. - person Jakub Troszok; 22.06.2009

Вы можете разделить два случая на разные шаблоны: один, если сообщения существуют, и один, если сообщений не существует. В действии контроллера (возможно, MessagesController#index) добавьте в качестве последнего оператора:

render :action => 'index_empty' if @messages.blank?

Если сообщений нет, будет отображаться app/views/messages/index_empty.html.erb. Если есть сообщения, они не пройдут и отобразят app/views/messages/index.html.erb как обычно.

Если вам это нужно более чем в одном действии, вы можете красиво реорганизовать его во вспомогательный метод, например следующий (непроверенный):

def render_action_or_empty (collection, options = {})
    template = params[:template] || "#{params[:controller]}/#{params[:action]}"
    template << '_empty' if collection.blank?
    render options.reverse_merge { :template => template }
end

При этом вам просто нужно поставить render_action_or_empty(@var) в конце любого действия контроллера, и он отобразит либо шаблон «действие», либо шаблон «action_empty», если ваша коллекция пуста. Также должно быть легко сделать эту работу с частичными вместо шаблонов действий.

person Zargony    schedule 22.06.2009

Ниже решение работает для меня, потому что я хочу tr и td со свойством colspan:

<%= render(@audit_logs) || content_tag(:tr, content_tag(:td, 'No records',colspan: "11"))%>
person Jigar Bhatt    schedule 28.06.2019