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

如果使用:collection参数来渲染,例如render :partial => 'message', :collection => @messages那么如果集合是空的,那么对render的调用将返回nil 。 这可以被合并到一个||中 expression例如

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

如果您之前没有遇到过,render :collection会为每个元素使用相同的部分来呈现一个集合,当它构build完整的响应时,通过局部variablesmessage使每个@messages元素可用。 您还可以使用以下命令指定要在每个元素之间呈现的分隔线:spacer_template => "message_divider"

你也可以这样写:

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

一种方法是做类似的事情:

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

编辑:

如果我没有记错的话,这个提交就成了可能:

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

我很惊讶,我最喜欢的答案不在这里。 有一个答案是closures的,但我不喜欢裸文本和使用content_for是klunky。 尝试这一个大小:

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

你可以创build一些自定义帮手。 下面的只是一个例子。

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

作为一个说明,如果你正在寻找expression效率,你可能只是遍历一个空的数组:

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

虽然这不处理@消息为零,它应该适用于大多数情况。 引入不规则扩展到常规视图可能会使一个简单的事情复杂化。

可能更好的方法是定义一个部分和一个帮助器来渲染“空白”部分,如果这些部分是相当复杂的:

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

老主题,但我真的不喜欢任何这些在Rails 3.2上玩耍我想出了这个替代scheme:

 <% content_for :no_messages do %> <p> <strong>No Messages Found</strong> </p> <% end %> <%= render @messages || content_for(:no_messages) %> 

或者如果你需要像我这样部分path的更详细的渲染:

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

通过这种方式,您可以使用任何HTML /视图逻辑来设置“无消息”部分的样式,并使其易于阅读。

该代码可以缩短为:

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

注释:

formatted_msg() – 为消息添加格式的辅助方法

msg_delimiter – 包含分隔符(如“\ n”或“ <br /> ”)的variables

顺便说一句我会build议使用空? 方法而不是空白? 检查一个数组,因为a)它的名字更简洁:)和b)空白? 是一个ActiveSupport扩展方法,它不会在Rails之外工作。

你可以将你的两个案例分成不同的模板:一个是消息存在,一个是消息不存在。 在控制器动作( MessagesController#index大概)中,添加为最后一个语句:

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

如果没有消息,它将显示app/views/messages/index_empty.html.erb 。 如果有消息,它会像往常一样通过并显示app/views/messages/index.html.erb

如果你不只需要一个动作就需要这个,你可以很好地将它重构成一个像下面这样的辅助方法(未经testing):

 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'模板或者'action_empty'模板。 使用partials而不是action模板也应该很容易。