对Rails 3来说相当新,并且谷歌搜索每一种方式无法解决以下问题,大多数教程都没有处理错误。
我创建了一个包含多种内容类型/模型的Rails 3项目,例如文章,博客等。每种内容类型都有注释,所有内容都存储在一个注释表中作为嵌套资源并具有多态关联。只有一个评论操作,即“创建”操作,因为它不需要显示等,因为它属于父内容类型,应该只是在提交时重新显示该页面。
现在我有大部分工作和评论提交和发布就好了,但最后一个问题是当用户没有填写必填字段时显示错误。如果字段未填写,则应返回到父页面并显示验证错误,如Rails通常与MVC一起使用。
我的评论控制器的创建动作看起来像这样,这是我第一次尝试...
def create
@commentable = find_commentable
@comment = @commentable.comments.build(params[:comment])
respond_to do |format|
if @comment.save
format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
else
format.html { redirect_to @commentable }
format.xml { render :xml => @commentable.errors, :status => :unprocessable_entity }
end
end
end
如果您没有填写任何内容并提交评论表单,该页面会重定向回适当的父级,但不会显示任何闪存或任何内容。现在我想出了为什么,根据我的理解,闪存不会在redirect_to上持久存在,只能在渲染上。现在,麻烦就在这里。
评论控制器中只有'创建'动作,所以我需要将渲染指向'blogs / show'(注意:我知道这不是多态的,但是一旦我开始工作,我会担心关于那个)。我在上面代码的“else”块中尝试了这个...
else
format.html { render 'blogs/show' }
format.xml { render :xml => @commentable.errors, :status => :unprocessable_entity }
end
无论如何,当我尝试在博客上提交无效评论时,我收到一条错误消息“显示[...] / app / views / blogs / show.html.erb,其中第1行引发:undefined method 'title'代表nil:NilClass。“
查看网址,我想我知道为什么......而不是直接访问/ blogs / the-title-of-my-article(我正在使用friendly_id),它会转到/ blogs / the-title-的-MY-文章/评论。我认为额外的“评论”会抛出查询并将其返回为零。
那么我怎样才能让页面呈现而不用在那里抛出额外的“评论”?或者有更好的方法来解决这个问题吗?
不确定是否重要或有帮助,但评论/博客的route.rb看起来像这样......
resources :blogs, :only => [:show] do
resources :comments, :only => [:create]
end
答案 0 :(得分:3)
在过去的几周里,我一直在关注这个问题,我认为我最终将其拉下来,错误/正确的渲染方向,填写的字段仍然填满。我确实考虑过AJAX,但是如果可能的话,我宁愿以优雅的降级来做这件事。
此外,我承认我不得不采用一种非常麻烦的方式,包括引入一种方式来复制父模型以呈现适当的内容类型的show动作,并且在这个阶段我需要简单的代码工作,不一定看起来很漂亮。
我知道它可以更好地重构,我希望在Rails变得更好的时候这样做。或者,任何其他认为可以改善这一点的人都会受到欢迎。无论如何,这是我的所有代码,只是想分享回来,并希望这可以帮助同一场景中的某些人。
comments_controller.rb
class CommentsController < ApplicationController
# this include will bring all the Text Helper methods into your Controller
include ActionView::Helpers::TextHelper
def create
@commentable = find_commentable
@comment = @commentable.comments.build(params[:comment])
respond_to do |format|
if @comment.save
format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
else
# Transform class of commentable into pluralized content type
content_type = find_commentable.class.to_s.downcase.pluralize
# Choose appropriate instance variable based on @commentable, rendered page won't work without it
if content_type == 'blogs'
@blog = @commentable
elsif content_type == 'articles'
@article = @commentable
end
format.html { render "#{content_type}/show" }
format.xml { render :xml => @commentable.errors, :status => :unprocessable_entity }
end
end
end
private
# Gets the ID/type of parent model, see Comment#create in controller
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.where(:status => 1).find_by_cached_slug(params[:id])
@comment = Comment.new
# On another content type like blogs_controller.rb, replace with appropriate instance variable
@content = @article
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @article }
end
end
end
show.html.erb for articles(更改适当的博客或其他变量)
<h1><%= @article.title %></h1>
<%= @article.body.html_safe %>
<%= render :partial => 'shared/comments', :locals => { :commentable => @article } %>
shared / _comments.html.erb(为了简化,我在这里省略了发布评论的显示,只是显示了提交它们的表单)
<%= form_for([commentable, @comment]) do |f| %>
<h3>Post a new comment</h3>
<%= render :partial => 'shared/errors', :locals => { :content => @comment } %>
<div class="field">
<%= f.label :name, :value => params[:name] %>
<%= f.text_field :name, :class => 'textfield' %>
</div>
<div class="field">
<%= f.label :mail, :value => params[:mail] %>
<%= f.text_field :mail, :class => 'textfield' %>
</div>
<div class="field">
<%= f.text_area :body, :rows => 10, :class => 'textarea full', :value => params[:body] %>
</div>
<%= f.submit :class => 'button blue' %>
<% end %>
shared / _errors.html.erb(我将其重构为部分重用文章,博客,评论等,但这只是一个标准的错误代码)
<% if content.errors.any? %>
<div class="flash error">
<p><strong><%= pluralize(content.errors.count, "error") %> prohibited this page from being saved:</strong></p>
<ul>
<% content.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
答案 1 :(得分:1)
我稍微重构了@Shannon的答案,使其更具活力。在我的'find_parent'方法中,我抓住了url路径并获取了控制器名称。在'create'方法中,我创建了一个'instance_variable_set',它为Articles(@article)或Blogs(@blog)创建了一个动态变量,或者它可能是什么。
希望你会喜欢我做过的事情?如果您有任何疑问或有什么可以改进的地方,请告诉我?
def create
@comment = @commentable.comments.new(params[:comment])
if @comment.save
redirect_to @commentable, notice: "Comment created."
else
content_type = find_parent
instance_variable_set "@#{content_type.singularize}".to_sym, @commentable
@comments = @commentable.comments
render "#{content_type}/show"
end
end
def find_parent
resource = request.path.split('/')[1]
return resource.downcase
end
答案 2 :(得分:0)
您收到错误,因为blogs/show
视图可能引用@blog
对象,当您在评论控制器中呈现它时,该对象不存在。
你应该回到使用redirect_to
而不是渲染。在您发出无效评论时,它没有显示闪光,因为如果没有保存评论,您没有告诉它设置闪光。闪光将持续到下一个请求。