Stumped by Ajax + Session + Saving Model + Private Method

Got stumped by this one... Had a working form_remote_tag that used rjs to toggle between a static view of the data and a form with inputs. Saving that form updated an ActiveRecord model.

Then I realized I needed to move the actions I was using to view/edit/save this form into the private section of the controller (calling them directly via URL not a good thing).

Now the Saving doesn't actually save anything. No crashes, but the data isn't updated. I suspect it is because I store the ID of the object in a session, and somehow the Ajax invocation of the action fails to restore the session -- but why the method being private vs public would matter to that, I don't know. Or maybe it is something else.

The saving method looks like this:

   def emergency_contact_save

     @client = Client.find(session[:selected_client_id])      @client.emergFirstName = params[:emergFirstName]      ..... more params .....      respond_to do |request_format|        request_format.js      end


works perfect if it is a public method, doesn't work if it is private.

Not sure what other code I'd need to show.

Any ideas??

-- gw

If emergency_contact_save is the method you are calling from your form and you moved it from public to private you should know then you cannot call a private method via a request. Or is emergency_contact_save called from def save which is public?

Cheers, Nicholas

Yeah I was just about to post that after thinking about this, I realized the Ajax request is still making a call to a public URL that Rails is going to map to an action. The screen gymnastics continued to work because Rails would still find the .rjs file and run them.

OK, so here's the real problem -- if I put it all back as public actions, then using the web UI works all good. However, if I simply enter a URL for that Ajax into the browser I get a big messy javascript upchuck on the page, _and_ it wipes out the data in that model that is supposed to be updated because there is no data. Clearly, calling this URL directly is not good.

Submitting no data (i.e. clearing what existed) is a valid submission, so I can't just add some code that says an submission with all fields as empty is invalid -- it's not invalid.

So, what's the pattern for ensuring that calls to controller actions are called by their proper Ajax context? That and I have to figure out why the Javascript upchuck?

-- gw

I think it's your implementation of the form/params hash that could improve and solve your issue here:

--- controller ---

def emergency_contact_save      @client = Client.find(session[:selected_client_id])       @client.attributes = params[:client] # instead of setting them individually      respond_to do |request_format|        request_format.js      end    end

--- view ---

<% remote_form_for @client do %> ...fields..... <%end %>

This way if the params[:client] has is nil none of the attributes will be touched.

HTH, Nicholas

Nah, that doesn't matter. How the model is poulated isn't relevant. I've removed the model altogther to better understand what Ajax and Rails are doing here, and somehow, Rails is still trying to call the RJS template even when the request is not XHR.

So, I now have this:

   def emergency_contact_save

     if request.xhr?        .... logger step comfirms an XHR request ....

       respond_to do |request_format|          request_format.js        end      else        .... logger step comfirms a Non-XHR request ....      end


And, I have a file emergency_contact_save.rjs

All works fine when using the web UI.

When I use a direct URL /{controller}/emergency_contact_save I still get a javascript error which seems to come from Rails trying to run the emergency_contact_save.rjs file.

If I remove the emergency_contact_save.rjs file and again try the URL /{controller}/emergency_contact_save I get a Rails error saying there is no rjs file.

Now, that makes no sense to me at all that Rails should still be trying to find the rjs file at this point.

Since that testing, I've restarted Rails and that seems to have changed a few things. Then I ended up building up to this code (added redirect):

   def emergency_contact_save

     if request.xhr?        .... logger step comfirms an XHR request ....

       respond_to do |request_format|          request_format.js        end      else        .... logger step comfirms a Non-XHR request ....

       redirect_to :action => '...the basic view of the page...'      end


That combination of xhr? and the redirect seems to be the cure. Now, maybe I need to do some refactoring to allow a legit POST, but for now this seems to take care of the problem of how to handle Ajax vs non-Ajax calls.

Thanks for bouncing some ideas, Nicholas.

-- gw

You could also do something like this at the top of your controller:

verify :only => 'emergency_contact_save', :xhr => true.

That would be nicer than having the conditional statement in the method



In addition, I think the catch all route at the end of your routes file will ignore the format and pickup the default template in this order:

emergency_contact_save.html.erb emergency_contact_save.rjs.js

The respond to stuff would only work strictly if you were using routes defined by resources and you had no catch all route at the end. I might be totally wrong here, but worth a try if you are interested.

HTH, Nicholas

OK, I'll check that stuff out -- thanks.

-- gw

Ah, ok, verify does the exact same thing as the conditional. Interesting. Now, whether it's "nicer" is questionable IMO.

In a case where the same conditions and actions apply to multiple actions, I can see the attraction of eliminating the redundancy, but in cases where it'd be the rare method that uses the verify, IMO having the conditional right in the method code is more explicit, and not prone to missing those details. I'm not a fan of splitting up code like this (again, unless it is something where the redundancy would become tedious, then splitting the code with verify becomes the lesser "evil").

Anyway, I can see that using Ajax requires more planning than just banging out the css and rjs files, and there's a few options to deal with it.

Thanks for your help.

-- gw

Just to clarify, that should be



action = 'emergency_contact_save' format = 'js' (as in, respond_to do |format| ... format.js... ) renderer = rjs engine (vs erb/haml/builder/etc)