Method chaining and RJS templates

Hi

Ex, how do I get the empty_cart.rjs template to be ‘fired’ when I do
this in my controller.

my Controller

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
empty_care
else
foo
end
end

def empty_cart
does_something_here
end
end

remove_from_cart.rjs

page.replace_html( “cart”, :partial => “cart”, :object => @cart )
page[:cart].visual_effect :shake

empty_cart.rjs

page[:cart].visual_effect :puff

For some reason it doesn’t run the AJAX calls in the empty_cart.rjs but
does run the ones in remove_from_cart.rjs

As I want the cart hidden ONLY when it’s empty I don’t want to put a
“page[:cart].visual_effect :puff” every time I just remove an item.

(Sorry but still hazy on this automatic loading of templates at the end
oa method in a controller etc etc)

Thanks.

Because the action is “remove_from_cart” (see params[:action]), and
that’s
what Rails uses to find the view to use. Do this:

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
empty_cart
render :partial => “empty_cart”
else
foo
end
end

Jason

Sorry, hit Send too quickly.

You can also do, instead of the above :

def empty_cart
render :partial => “empty_cart”
end

as long as there is no render call in remove_from_cart. Otherwise you’ll
get
an error along the lines of “can’t render more than once”.

Jason

Ah, if you do :partial => “empty_cart”, then the file Rails looks for is
_empty_cart.rjs (note the beginning underscore).

Can you just do render “empty_cart” ?

Jason

Jason R. wrote:

Ah, if you do :partial => “empty_cart”, then the file Rails looks for is
_empty_cart.rjs (note the beginning underscore).

Can you just do render “empty_cart” ?

Jason

When I try that (It’s actually "render :file => “empty_cart”)

I get the following error (I have also tried using empty_cart.rjs to the
same results)

OUTPUT FROM LOG FILE
Processing StoreController#remove_from_cart (for 66.11.89.241 at
2007-07-03 15:12:27) [POST]
Session ID: 830e085b4b3270e2a1fc382f30a77a99
Parameters: {“commit”=>“Remove from Cart”,
“action”=>“remove_from_cart”, “id”=>“1”, “controller”=>“store”}
Rendering empty_cart

ActionView::TemplateError (No such file or directory - empty_cart) in
empty_cart:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:405:in 

read' /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:405:inread_template_file’
<>>
<<<<<<<<<< END OUTPUT

I think what’s got me all ‘flustered’ is how to just call another method
in my controller and have THAT partial or RJS be executed automatically
and NOT the calling method.

Jason R. wrote:

Sorry, hit Send too quickly.

You can also do, instead of the above :

def empty_cart
render :partial => “empty_cart”
end

as long as there is no render call in remove_from_cart. Otherwise you’ll
get
an error along the lines of “can’t render more than once”.

Jason

Tried both solutions and neither work. When I use the render :partial =>
“empty_cart” in my remove_from_cart method it tells me there is no such
partial. SO I change it to be “Cart” (as I do have a cart partial in
there). no go, still fires the remove_from_cart Ajax definitions in the
remove_from_cart.rjs and doesn’t actually ‘empty’ the cart and ‘puff’ it
out of the view.

Incidentally, I have no errors in the log/development.log file So it
seems to be just a ‘flow’ issue.

(output log for sanity sake…) NOtice that there is a rendering call to
my store/empty_cart but it’s just not changing my view.

OUTPUT LOG
Processing StoreController#empty_cart (for 66.11.89.241 at 2007-07-03
13:53:44) [POST]
Session ID: 830e085b4b3270e2a1fc382f30a77a99
Parameters: {“commit”=>“Empty cart”, “action”=>“empty_cart”,
“controller”=>“store”}
Rendering store/empty_cart
Completed in 0.01094 (91 reqs/sec) | Rendering: 0.00968 (88%) | DB:
0.00000 (0%) | 200 OK [http://foo.bar.baz/store/empty_cart]
<<<<<<<<<< END OUTPUT LOG

Sorry, but that’s not how Rails works. It doesn’t figure out the view
from
the function being ran, but from the request from the client. E.g.

www.host.com/users/login

becomes:

params = {
:controller => “users”,
:action => “login”
}

And thus, if there is no explicit render call in UsersController#login,
then
Rails looks for login.rhtml / login.rxml / login.rjs

What you can do is an explicit render call as such:

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
render :action => “empty_cart”
else
foo
end
end

def empty_cart
does_something_here
end
end

But that only tells Rails what view to look for, it doesn’t actually
call
SomeController#empty_cart, so the “does_something_here” won’t actually
get
done, but you will get “empty_cart.rhtml” to render.

From what I’ve gathered from your code and responses, I would say this
is
your best bet:

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
empty_cart
render :partial => “empty_cart”
else
foo
end
end

def empty_cart
does_something_here
end
end

and then have a view file under views/some_controller/_empty_cart.rjs.

Hope that helps.

Jason

Jason R. wrote:

Sorry, but that’s not how Rails works. It doesn’t figure out the view
from
the function being ran, but from the request from the client. E.g.

www.host.com/users/login

becomes:

params = {
:controller => “users”,
:action => “login”
}

And thus, if there is no explicit render call in UsersController#login,
then
Rails looks for login.rhtml / login.rxml / login.rjs

What you can do is an explicit render call as such:

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
render :action => “empty_cart”
else
foo
end
end

def empty_cart
does_something_here
end
end

But that only tells Rails what view to look for, it doesn’t actually
call
SomeController#empty_cart, so the “does_something_here” won’t actually
get
done, but you will get “empty_cart.rhtml” to render.

From what I’ve gathered from your code and responses, I would say this
is
your best bet:

class SomeController < ApplicationController
def remove_from_cart
if quantity <= 0
empty_cart
render :partial => “empty_cart”
else
foo
end
end

def empty_cart
does_something_here
end
end

and then have a view file under views/some_controller/_empty_cart.rjs.

Hope that helps.

Jason

Hi

Actually, aside from having empty_cart.rjs and NOT _empty_cart.rjs (as
none of my rjs templates require the starting _, only my partials do) I
still am not able to call another method and then render the
empty_cart.rjs. It seems that every time the remove_from_cart.rjs is the
final render even with [render :file => “empty_cart” or
“empty_cart.rjs”] after the method call.

I’ll post some more code in a bit once I can get access to my home
machine.

Thanks.

Jean N. wrote:

Hi

Actually, aside from having empty_cart.rjs and NOT _empty_cart.rjs (as
none of my rjs templates require the starting _, only my partials do) I
still am not able to call another method and then render the
empty_cart.rjs. It seems that every time the remove_from_cart.rjs is the
final render even with [render :file => “empty_cart” or
“empty_cart.rjs”] after the method call.

I’ll post some more code in a bit once I can get access to my home
machine.

Thanks.

Synopsis of Issue: (many replies make it hard to follow the current
state of my app)

The ‘empty_cart’ action works perfects if button/link clicked from the
site. Calling ‘remove_from_cart’ works perfectly EXCEPT when the item
being removed is the LAST item in the cart. When that happens I want to
(redirect?) the call to remove_from_cart to the empty_cart action and
then have the associated empty_cart.rjs template rendered, NOT the one
that Rails would render [by default] “remove_from_cart.rjs”.

Here’s some code I have (snipped in places for brevity).

controller / rjs code

THE CONTROLLER
def remove_from_cart
@cart = find_cart
begin
product = Product.find( params[:id])
rescue ActiveRecord::RecordNotFound
logger.error( “Attempt to access invalid product #{params[:id]}”)
redirect_to_index( “Invalid product to remove.” )
else
@current_item = @cart.remove_product(product)
end

#-- Where the magic should happen!
if @cart.items.size <= 0
  empty_cart
  render :partial => "empty_cart"
end

end

def empty_cart
session[:cart] = nil
end

THE RJS TEMPLATES: remove_from_cart.rjs
page.replace_html( “cart”, :partial => “cart”, :object => @cart )
page[:cart].visual_effect :shake

THE RJS TEMPLATES: empty_cart.rjs
page[:cart].visual_effect :puff

<<<<<<<<<< end controller / rjs code

Log file Error:
ActionView::ActionViewError (No rhtml, rxml, rjs or delegate template
found for store/_empty_cart in
/home/jbateman/sandbox/depot/config/…/app/views):
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:399:in
find_template_extension_for' /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:335:inpick_template_extension’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:249:in
render_file' /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:275:inrender’

I ‘feel’ I have researched this well and have a good understanding of
the flow of events ‘behind the scenes’ (Thanks mostly to all of you who
have replied to me) but I’m still missing something that is not
‘clicking’ in my head on how to do this.

Thanks!

Jean N. wrote:

Wow…

If I rename my .rjs file _empty_cart.rjs (from empty_cart.rjs) and NOT
empty_cart.rjs the above code works, BUT, then the original code to
‘empty the cart’ breaks with the SAME Errors found in the log file as I
posted above.

Now ‘this’ doesn’t work:

_cart.rhtml partial (that handles the cart and an “empty_cart” button.)

<% form_remote_tag :url => { :action => :empty_cart } do %>
<%= submit_tag “Empty cart” %>
<% end %>

The controller.
def empty_cart
session[:cart] = nil
end

The _empty_cart.rjs template
page[:cart].visual_effect :puff

This, to me, would be a sign of a BUG in the framework.

Okay so to fix my problem is as follows. To allow me to have an “Empty
Cart” button, which calls the empty_cart action and render the
empty_cart.rjs (automatically), AND have a remove_from_cart action that
will call the empty_cart ONLY if the number of items in my cart reaches
ZERO I have to have BOTH an empty_cart.rjs AND an _empty_cart.rjs file.

The first is for the ‘automatic’ rendering built into rails and the
second is for my call to ‘render :partial => “empty_cart”’ in the
remove_from_cart action.

Not very DRY nor very elegant… but I’m waiting for someone more versed
in Rails to show me where I’m messing up.

Thanks.

What about:

def empty_cart
session[:cart] = nil
render :partial => “empty_cart”
end

then you just need _empty_cart.rjs and can get rid of empty_cart.rjs.

You do make a lot of view files when you start dealing with AJAX and
RJS,
it’s just how Rails is. But you can call any view file from any
controller
through the #render method.

It’s also possible to run RJS code in your controller, like so:

def empty_cart
session[:cart] = nil
render_update do |page|
page.visual_effect :puff
end
end

but I’m not a fan of this as it starts to muddle the line between View
and
Controller.

Jason

Jason R. wrote:

What about:

def empty_cart
session[:cart] = nil
render :partial => “empty_cart”
end

then you just need _empty_cart.rjs and can get rid of empty_cart.rjs.

Good idea,

Just weird I have to ‘override’ the default behavior because sometimes
you need _ and sometimes you don’t.

I’ll live with it for now, I’ll keep default behavior until I have to do
something special with it, at least when I do I’ll know what to do.

Thanks to all for the advice.

Jean N. wrote:

Jean N. wrote:

Synopsis of Issue: (many replies make it hard to follow the current
state of my app)

The ‘empty_cart’ action works perfects if button/link clicked from the
site. Calling ‘remove_from_cart’ works perfectly EXCEPT when the item
being removed is the LAST item in the cart. When that happens I want to
(redirect?) the call to remove_from_cart to the empty_cart action and
then have the associated empty_cart.rjs template rendered, NOT the one
that Rails would render [by default] “remove_from_cart.rjs”.

Here’s some code I have (snipped in places for brevity).

controller / rjs code

THE CONTROLLER
def remove_from_cart
@cart = find_cart
begin
product = Product.find( params[:id])
rescue ActiveRecord::RecordNotFound
logger.error( “Attempt to access invalid product #{params[:id]}”)
redirect_to_index( “Invalid product to remove.” )
else
@current_item = @cart.remove_product(product)
end

#-- Where the magic should happen!
if @cart.items.size <= 0
  empty_cart
  render :partial => "empty_cart"
end

end

def empty_cart
session[:cart] = nil
end

THE RJS TEMPLATES: remove_from_cart.rjs
page.replace_html( “cart”, :partial => “cart”, :object => @cart )
page[:cart].visual_effect :shake

THE RJS TEMPLATES: empty_cart.rjs
page[:cart].visual_effect :puff

<<<<<<<<<< end controller / rjs code

Log file Error:
ActionView::ActionViewError (No rhtml, rxml, rjs or delegate template
found for store/_empty_cart in
/home/jbateman/sandbox/depot/config/…/app/views):
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:399:in
find_template_extension_for' /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:335:inpick_template_extension’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:249:in
render_file' /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/base.rb:275:inrender’

I ‘feel’ I have researched this well and have a good understanding of
the flow of events ‘behind the scenes’ (Thanks mostly to all of you who
have replied to me) but I’m still missing something that is not
‘clicking’ in my head on how to do this.

Thanks!

Wow…

If I rename my .rjs file _empty_cart.rjs (from empty_cart.rjs) and NOT
empty_cart.rjs the above code works, BUT, then the original code to
‘empty the cart’ breaks with the SAME Errors found in the log file as I
posted above.

Now ‘this’ doesn’t work:

_cart.rhtml partial (that handles the cart and an “empty_cart” button.)

<% form_remote_tag :url => { :action => :empty_cart } do %>
<%= submit_tag “Empty cart” %>
<% end %>

The controller.
def empty_cart
session[:cart] = nil
end

The _empty_cart.rjs template
page[:cart].visual_effect :puff

This, to me, would be a sign of a BUG in the framework.