Rails:当数据库中没有元素时,一种优雅的消息显示方式


67

我意识到我正在写很多与此相似的代码:

<% 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 %>

Answers:


63

如果使用:collection参数渲染,例如render :partial => 'message', :collection => @messagesnil如果集合为空,则将返回渲染调用。然后可以将其合并到||中。表达例如

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

如果您以前从未遇到过,render:collection会为每个元素使用相同的局部渲染渲染集合,使每个元素在建立完整响应时都@messages可以通过局部变量使用message。您还可以使用以下方式指定要在每个元素之间呈现的分隔线:spacer_template => "message_divider"


1
太好了……收集之前和之后如何?说您要在部分渲染之前和之后拥有<ul>和</ ul>或<tr> </ tr>标记对,但前提是@messages不为空。examples-> <p> <ul> <li> message1 </ li> <message2> </ ul> <p>是@messages!= nil或<p> <ul>没有消息!<p>
mataal

3
我认为应该将Fernando Allen的解决方案添加到此答案中,作为可能的替代方案,因为人们可能会跳过它,因为这不是“最佳答案”。
arikfr 2011年

1
简要说明一下可能会有所帮助。为了使此语法起作用,必须在partial赋值周围使用括号,如上所示。没有它们,部分渲染将正确呈现,但条件消息不会显示
pruett

162

您也可以这样写:

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

6
我喜欢这个答案。我自己动用了这个,超级干净而且非常容易理解。
James F

1
很棒的答案,非常简洁。
Shane 2014年

7
您如何在haml / slim中做这样的事情?
DickieBoy 2014年

谢谢!可以接受的答案更多是Rails-y(也许正是出于这个原因,应该是可以接受的答案),但是在我构建原型时,这将为我节省很多时间。
user435779 2014年

这个功能叫什么?
onmyway133

18

我很惊讶我最喜欢的答案不在这里。答案很接近,但是我不喜欢裸露的文字,并且使用content_for太笨拙了。试穿一件这样的尺码:

  <%= render(@user.recipes) || content_tag("p") do %>
    This user hasn't added any recipes yet!
  <% end %>

1
到目前为止,这是我最喜欢的答案。
dkubb

7
重要提示:不要忘记为render加上括号,否则||将会不适用于的结果render,而是适用于集合本身。我本人也写过同样的东西,但由于这个原因(一开始)没有用。
D端

1
这是我一直在寻找的优雅解决方案:)
markquezada


6

您可以创建一些自定义帮助器。以下只是一个示例。

# 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 %>

3

需要注意的是,如果您想提高表达效率,也可以只遍历一个空数组:

<% @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

1

这个话题很老,但是我真的不喜欢这些,所以在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 /视图逻辑来设置“无消息”部分的样式,并使其易于阅读。


0

该代码可以简化为:

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

评论:

formatted_msg()-将消息添加格式的帮助程序方法

msg_delimiter-包含分隔符(如“ \ n”或“ <br />”)的变量

顺便说一句,我建议使用空?方法而不是空白?用于检查数组,因为a)其名称更简洁:)和b)空白?是一种ActiveSupport扩展方法,在Rails外部无法使用。


感谢您的建议,但我更喜欢使用空白?因为我不必检查对象是否不为零,并且在这种情况下,仅使用特定于rails的扩展名不会令我感到困扰。
2009年

0

您可以将两种情况拆分为不同的模板:一种是如果存在消息,另一种是如果不存在消息。在controller动作中(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”模板。使用局部函数而不是动作模板也应该很容易。


0

下面的解决方案为我工作,因为我想要trtd具有colspan属性:

<%= render(@audit_logs) || content_tag(:tr, content_tag(:td, 'No records',colspan: "11"))%>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.