I bet others have had this problem, but I have not seen a good solution.
In a rails app you might want to do stuff with one model from different places.
Say, you have a Projects page where you list all the projects and have a form for making new ones. This form also serves as a update form if the (:id) is present.
Lets say you also have a custom开发者_运维知识库ers show page where you also list all the projects for a spesific customer. Here you also want to edit and create projects( for this customer)
When the controller gets this it will send the user to the page spesified in the respond_to block. But, sometimes you want to go back to the projects page and other times you want the customer show page.
When submiting with ajax. For the projects page you fill it with javascript and ERB, that does not necessarry fit the customers show page.
Is there an elegant way to handle this without a ton of if statements?
Edit
I have been experimenting a bit on a testapp with two scaffolds projecs and todos.
I don't think this is elegant. To many if statements and not very DRY.
How can I make this better?
here's the code
projects/show
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= @project.name %>
</p>
<% @todos.each do |todo| %>
<p>
<%= todo.name %><%= link_to 'Edit', show_path(:id => @project.id.to_s, :todo_id => todo.id.to_s)%>
</p>
<% end %>
<%= render 'todos/form' %>
<%= link_to 'Edit', edit_project_path(@project) %> |
<%= link_to 'Back', projects_path %>
todos/_form
<% if params[:todo_id].nil? %>
<%= form_for([@project,@todo], :url => project_make_todo_path( @project)) do |f| %>
<% if @todo.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
<ul>
<% @todo.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :project_id %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% else %>
<%= form_for([@project,@todo], :url => project_update_todo_path( @project, @todo)) do |f| %>
<% if @todo.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
<ul>
<% @todo.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :project_id %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
parts of the projects_controller.rb
def project_make_todo
@project = Project.find(params[:id])
@todo = @project.todos.build(params[:todo])
respond_to do |format|
if @todo.save
format.html { redirect_to( @project, :notice => 'Todo was successfully created.') }
format.xml { render :xml => @project, :status => :created, :location => @project }
else
format.html { render :action => "new" }
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
end
end
end
def project_update_todo
@todo = Todo.find(params[:todo_id])
@project = Project.find(params[:id])
respond_to do |format|
if @todo.update_attributes(params[:todo])
format.html { redirect_to(@project, :notice => 'Todo was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
end
end
end
parts of the routes.rb
match 'projects/:id/project_todo' => 'projects#project_make_todo', :as => :project_make_todo
match 'projects/:id/:todo_id/project_todo' => 'projects#project_update_todo', :as => :project_update_todo
The use case where a model can be updated from many different locations can be confusing for exactly the reasons you mention: which controller handles the update action; how do I format my form so that it fits all cases? Usually the best solution is to create a new view and a new controller to handle the special case. This keeps your code cleaner and does not require you to use many conditionals.
For example, if you start out with the Project model and a ProjectsController and a simple edit and new form you are in pretty good shape.
Once you add the Customer model you want to be able to modify a customer's projects. The best solution is to build a separate nested form for modifying projects in the customer's edit and new forms. That way you aren't messing with your old implementation and you are asking the Customer model to be responsible for updating its own projects.
What this allows you to do is that when you are updating a project from the project page, you are redirected back to the projects page, and when you are updating projects from the customer page you are redirected back to the customer page.
Be sure that you keep the logic of updating your projects in the Project model, that way you can use a simple @project.update_attributes call regardless of which controller you are in. You don't want to have business logic in your controllers or views.
精彩评论