In my Rails app I have an #events collection of objects inherited from Event::Base < AR::Base model.
If rendering it like render :partial => 'event', :collection => #events it's possible to give an option :as => :event to change the name of a local variable corresponding to the object inside of the partial. But what to do when the name of the partial is not constant? The aforementioned way (render #events, :as => :event) doesn't work.
PS. There's a solution at blog.obiefernandez.com but it uses the last part of the partial name and this just doesn't fit for me.
I haven't tried this myself to verify, but this might work:
class Event < ActiveRecord::Base
def to_partial_path
# assuming that you need different partials based on an attribute "variety"
"events/#{variety}"
end
end
You may also need to use the :partial key, eg: render :partial => #events, :as => :event instead of render #events, :as => :event
EDIT: This only works in Rails 3.2+ ... see Obie's writeup on the topic.
Related
So essentially I've setup a route to match "products/:product", which seems to respond to a page like baseurl/products/toaster and displays the toaster product. My problem is I can't seem to use link_to to generate this path, and by that I mean I don't know how. Any help on this?
There are several solutions on this one :
<%= link_to 'Toaster', { :controller => 'products', :action => 'whatever', :product => 'toaster' } %>
But it's not really Rails Way, for that you need to add :as => :product at the end of your route. This will create the product_path helper that can be used this way :
<%= link_to 'Toaster', product_path(:product => 'toaster') %>
Within your routes file you can do something like:
match "products/:product" => "products#show", :as => :product
Where the controller is ProductsController and the view is show
within the Products controller your have
def show
#product = Hub.find_by_name(params[:product])
respond_to do |format|
format.html # show.html.erb
end
end
Where whatever is in the products/:product section will be available via params.
Then, since we used :as in your routes you can do this with link_to:
<%= link_to product(#product) %>
Where #product is an instance of a product or a string. This is just an example and the param can be anything you want, the same goes for controller/action. For more info you should check out this.
Hope this helps!
My user model has many projects, and each project has many invoices.
I am calling the following render
render :partial => "layouts/allInvoices", :collection => #projects, :as => :p
And inside the allInvoices, I wish to iterate over each project's invoices.
I can use
- p.invoices.each do |i|
But I'd rather use a collection. I'm not sure how to phrase it though
= render :partial => "layouts/invoiceItem", :collection => p.invoices, :as => :i
Doesn't work. Do I need to set up the nested iteration inside the controller?
Thanks
Do you mean something like...
= render "layouts/allInvoices", :p => #projects
Then
- # layouts/allInvoices
- p.invoices.each do |invoice|
= render "layouts/invoiceItem", :i => invoice
- # layouts/invoiceItem
= i.id # this gives id of Invoice
I have a Parent model which has Children. If all the Children of a certain Parent are deleted, I'd like to automatically delete the Parent as well.
In a non-AJAX scenario, in the ChildrenController I would do:
#parent = #child.parent
#child.destroy
if #parent.children.empty?
redirect_to :action => :destroy,
:controller => :parents,
:id => #parent.id
end
But this is impossible when the request is XHR. The redirect causes a GET request.
The only way I can think of to do this with AJAX is add logic to the response RJS, causing it to create a link_to_remote element, "click" it, and then remove it. It seems ugly. Is there a better way?
Clarification
When I use the term redirect, I do not mean an HTTP redirect. What I mean is that instead of returning the RJS associated with destroying Child, I want to perform destroy on Parent and return the RJS associated with destroying Parent.
I would listen to what nathanvda says, but you can do it via ruby syntax (and you don't need erb scriptlets in rjs):
if #parent.children.empty?
page.redirect_to(url_for :action => :destroy,
:controller => :parents,
:id => #parent.id)
else
.. do your normall stuff here ..
end
A better approach to destroying the parent through a redirect is doing it in an after_hook. Not only you don't have to tell your user's browser to make another request, you also don't need to keep track of everywhere in the code where you delete children so you don't end up with hanging parents.
class Parent < ActiveRecord::Base
# also worth getting the dependent destroy, so you don't have hanging children
has_many :chilren, :dependent => :destroy
end
class Child < ActiveRecord::Base
after_destroy { parent.destroy if parent.children.empty? }
end
Then you can just handle however you prefer what to show the user when that happens, like redirecting the user to '/parents'.
I would guess you could set the window.location.href in your rjs, something like
<% if #parent.children.empty? %>
window.location.href='<%= url_for :action => :destroy,
:controller => :parents,
:id => #parent.id %>'
<% else %>
.. do your normall stuff here ..
<% end %>
assuming you render javascript. Not sure if it is completely correct, but hope you get the idea.
[EDIT: added controller code]
TO make it clearer, your controller would look as follows
#parent = #child.parent
#child.destroy
if #parent.children.empty?
render :redirect
end
how do i pass a polymorphic object to another controller?
for example redirecting from messages/1/
to requests/new?object_type=message&object_id=1
or, second example, from files/154/
to requests/new?object_type=file&object_id=154
is
redirect_to new_request_path(:object_type => params[:controller].classify, :object_id => params[:id])
right?
Request model has
belongs_to :object , :polymorphic => true
You nest your routes, for example:
messages/1/requests/new
files/154/requests/new
redirect_to new_comments_request_path(Comment.find(1))
routes guide
I have frequently run into the situation where I want to update many records at once - like GMail does with setting many messages "read" or "unread".
Rails encourages this with the 'update' method on an ActiveRecord class - Comment.update(keys, values)
Example - http://snippets.dzone.com/posts/show/7495
This is great functionality, but hard to map to a restful route. In a sense, I'd like to see a :put action on a collection. In routes, we might add something like
map.resources :comments, :collection => { :update_many => :put }
And then in the form, you'd do this...
<% form_for #comments do |f| %>
...
This doesn't work on many levels. If you do this: :collection => { :update_many => :put }, rails will submit a post to the index action (CommentsController#index), I want it to go to the 'update_many' action. Instead, you can do a :collection => { :update_many => :post }. This will at least go to the correct action in the controller.
And, instead of <% form for #comments ... you have to do the following:
<% form_for :comments, :url => { :controller => :comments, :action => :update_many } do |f| %>
It will work OK this way
Still not perfect - feels a little like we're not doing it the 'Rails way'. It also seems like :post, and :delete would also make sense on a collection controller.
I'm posting here to see if there's anything I missed on setting this up. Any other thoughts on how to restfully do a collection level :post, :put, :delete?
I've run into a few situations like you describe. The first couple of times I've implemented form almost identical to the one you suggest.
About the third time I hit this problem I realized that every item I'm updating has a common belongs_to relationship with something else. Usually a user. That's exactly the epiphany you need to make sense of this RESTfully. It will also help you clean clean up the form/controller.
Don't think of it as updating a bunch of messages, think of it as updating one user.
Here's some example code I've used in the past to highlight the difference. Assuming that we we want bulk operations on messages that belong to the current_user...
As of rails 2.3 we can add
accepts_nested_attributes_for :messages
to the user model. Ensure messages_attributes is part of attr_accessible, or is not attr_protected.
Then create the route:
map.resources :users, :member => {:bulk_message_update, :method => :put}
Then add the action to the controller. With AJAX capabilities ;)
def bulk_message_update
#user = User.find(params[:id])
#user.update_attributes(params[:user])
if #user.save
respond_to do |format|
format.html {redirect}
format.js {render :update do |page|
...
}
end
else
....
end
Then your form will look like this:
<% form_for current_user, bulk_message_update_user_url(current_user),
:html => {:method => :put} do |f| %>
<% f.fields_for :messages do |message| %>
form for each message
<% end %>
<%= sumbit_tag %>
<% end %>
I often add collection-based update_multiple and destroy_multiple actions to an otherwise RESTful controller.
Check out this Railscast on Updating Through Checkboxes. It should give you a good idea how to approach it, come back and add to this question if you run into troubles!