You're looking at the form. And it's not surprising it looks the same
because you wrote it to be that way. Here's my guess, and I can't tell
without (essentially) being you, watching your http requests, etc.
Here's the flow of what happens when you submit your form:
You:
Think you did a PUT via XMLHttpRequest
Your Browser:
Creates an xhr object and actually POSTs the form with a hidden
field that contains
the n/v pair _method=put
Rails (not necessarily in this order):
Gets the request and sees it is a POST
Parses the headers, especially the Accept headers
Looks for the hidden field, to see how it should be routed
Sees it and routes to your update method
Your update method
does a respond_to |format|
respond_to
Looks through the Accept headers to see if it can figure out what
the browser
request is looking for.
In actual practice, you have to get a number of things exactly right
to make this happen. These are far easier to get right if you are
using Prototype but no matter. If you are using jQuery's Ajax function
(jQuery is a great choice too), I've seen inconsistencies in how the
Accept header is set as compared to Prototype. The things that need to
go right are:
1. Be sure before each Ajax call, the Accept header is set. E.g.:
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/
javascript")}
});
2. In Rails, make sure your headers are sorted so that the
embarrassing ones are
parsed out early. I'm not sure why you have a frozen array -- you
didn't mention
a Rails version, but I have 2.3 running with that sorting snipped
just fine.
If you are not sure which code is being executed, work backwards, and
put RAILS_DEFAULT_LOGGER.debug "rendering script" in your script
section and RAILS_DEFAULT_LOGGER.debug "rendering html" in your html
section. See if you are really running the code you think you are.
Finally, if you want to make this not happen again, write tests or
specs to ship requests with these headers to your controller and make
certain it renders the right content.
Hope this helps.
On Feb 22, 2009, at 11:58 AM, groovetrain wrote:
>
> This is a long one, but it should be easy to parse through.
>
> Thank you for the reply. A couple of things:
>
> 1) The problem is with Firefox, that's what I'm using for development.
> 2) The above code (and the code from the codetunes.com site) give the
> error "can't modify frozen array"
>
> I set it as a before filter in my blog_entries controller.
>
> This does bring up a few things. Does this mean that the reason for
> my problem is that Rails is not detecting that the browser is sending
> and xhr request, and thus assuming that it is html? This is strange
> to me because it's literally the same form. I ran a test where I left
> the form alone, and didn't try to change to form so that it would PUT/
> UPDATE, I left it as POST/CREATE, and Rails returned the js file, not
> the html file.
>
> In my javascript returned from the "create" method, I am modifying the
> form that that it looks just the same as the form that is generated
> when I am editing an entry. It may be something that I'm doing, I
> don't know. I'll post the HTML and javascript (according to firebug)
> for fun:
>
> HTML of the create form before I submit it for the first time:
> /* ---------------------------------- BEGIN */
> <form action="/blog_entries" class="new_blog_entry"
> id="submit_blog_entry" method="post" onsubmit="$.ajax({data:$.param($
> (this).serializeArray()) + '&authenticity_token=' +
> encodeURIComponent('3ec895eb98a99ed6f134a6112d93005e50f2834b'),
> dataType:'script', type:'post', url:'/blog_entries'}); return false;">
> <div style="margin: 0pt; padding: 0pt;">
> <input name="authenticity_token"
> value="3ec895eb98a99ed6f134a6112d93005e50f2834b" type="hidden">
> </div>
> <div class="indentUppercase">Title</div>
> <input class="inputLarge onehundredpercent" id="blog_entry_title"
> name="blog_entry[title]" size="30" type="text">
> <br>
> <div class="indentUppercase">Post</div>
> <textarea cols="40" id="blog_entry_content" name="blog_entry
> [content]" rows="20"></textarea>
> <table width="100%">
> <tbody><tr>
> <td>
> <table>
> <tbody><tr>
> <td class="blueControl" id="savePost"><input
> class="blueControl" name="commit" value="Save" type="submit"></td>
> <td class="blueControl" id="saveAndUnpublish"
> style="display: none;">Save & Unpublish</td>
> <td class="blueControl" id="publishPost">Publish</td>
> </tr>
> </tbody></table>
> </td>
> <td class="right" width="50">
> <table>
> <tbody><tr>
> <!-- this is the button to replace this html and generate a new
> partial for a new blog entry -->
> <td class="blueControl" id="newPost"><input onclick="$
> ("form#submit_blog_entry").block_this(); $.ajax
> ({data:'authenticity_token=' + encodeURIComponent
> ('3ec895eb98a99ed6f134a6112d93005e50f2834b'), dataType:'script',
> type:'post', url:'/dashboard/new_blog_entry'});" value="new"
> type="button"></td>
> </tr>
> </tbody></table>
> </td>
> </tr>
> </tbody></table>
> <br> </form>
> /* ---------------------------------- END */
>
>
>
>
> Here is the javascript that is returned that changes the form:
> /* ---------------------------------- BEGIN */
> $("#status").show();
> $("#status").html("Blog entry was successfully created.");
> $(".blockMe").unblock();
> $("#status").fadeOut(3000);
> if($("#submit_blog_entry").find("input[name*='method']").length == 0)
> $("#submit_blog_entry").find("input
> [name*='authenticity_token']").parent().prepend("<input type=\"hidden
> \" name=\"_method\" value=\"put\" />");
> else
> $("#submit_blog_entry").find("input[name='_method']").val("put");
> $("#submit_blog_entry").attr("action", "/blog_entries/50");
> var onsub = $("#submit_blog_entry").attr("onsubmit");
> $("form#submit_blog_entry").removeAttr("onsubmit");
> $("form#submit_blog_entry").removeAttr("onSubmit");
> onsub = onsub.replace(/url:'\/blog_entries/g, "url:'/blog_entries/
> 50");
> $("form#submit_blog_entry").submit(function(){ eval(onsub); } );
> $("form#submit_blog_entry").attr("onsubmit", onsub);
> alert(onsub);
> /* ---------------------------------- END */
>
>
>
>
> And here is the html of the form after that return:
> /* ---------------------------------- BEGIN */
>
> <form action="/blog_entries/50" class="new_blog_entry"
> id="submit_blog_entry" method="post">
> <div style="margin: 0pt; padding: 0pt;">
> <input name="_method" value="put" type="hidden">
> <input name="authenticity_token"
> value="3ec895eb98a99ed6f134a6112d93005e50f2834b" type="hidden">
> </div>
> <div class="indentUppercase">Title</div>
> <input class="inputLarge onehundredpercent" id="blog_entry_title"
> name="blog_entry[title]" size="30" type="text">
> <br>
> <div class="indentUppercase">Post</div>
> <textarea cols="40" id="blog_entry_content" name="blog_entry
> [content]" rows="20"></textarea>
> <table width="100%">
> <tbody><tr>
> <td>
> <table>
> <tbody><tr>
> <td class="blueControl" id="savePost"><input
> class="blueControl" name="commit" value="Save" type="submit"></td>
> <td class="blueControl" id="saveAndUnpublish"
> style="display: none;">Save & Unpublish</td>
> <td class="blueControl" id="publishPost">Publish</td>
> </tr>
> </tbody></table>
> </td>
> <td class="right" width="50">
> <table>
> <tbody><tr>
> <td class="blueControl" id="newPost"><input onclick="$
> ("form#submit_blog_entry").block_this(); $.ajax
> ({data:'authenticity_token=' + encodeURIComponent
> ('3ec895eb98a99ed6f134a6112d93005e50f2834b'), dataType:'script',
> type:'post', url:'/dashboard/new_blog_entry'});" value="new"
> type="button"></td>
> </tr>
> </tbody></table>
> </td>
> </tr>
> </tbody></table>
> <br> </form>
> /* ---------------------------------- END */
>
>
>
>
> You may notice that the onsubmit attribute doesn't contain anything,
> however, a quick call using:
>
> $("form#submit_blog_entry").attr("onsubmit");
>
> yields:
>
> $.ajax({data:$.param($(this).serializeArray()) +
> '&authenticity_token=' + encodeURIComponent
> ('3ec895eb98a99ed6f134a6112d93005e50f2834b'), dataType:'script',
> type:'post', url:'/blog_entries/50'}); return false;
>
> Which is exactly what it's supposed to be (or so it seems), when I
> look at the onsubmit for the form generated from the partial for an
> edit, it looks like
>
> $.ajax({data:$.param($(this).serializeArray()) +
> '&authenticity_token=' + encodeURIComponent
> ('3ec895eb98a99ed6f134a6112d93005e50f2834b'), dataType:'script',
> type:'post', url:'/blog_entries/46'}); return false;
>
> exactly the same, but this one actually returns the js format, not the
> html
>
> Anyone have any ideas? Thanks again for the help thus far.
>
> Groove
>
>
>
> On Feb 22, 1:51 am, "s.ross" <[email protected]> wrote:
>> This may be the browser-dependency issue with recognition of accept
>> headers. I adapted a snippet that you can put in your
>> ApplicationController:
>>
>> protected
>> def correct_safari_and_ie_accept_headers
>> ajax_request_types = ['text/javascript', 'application/json',
>> 'text/xml']
>> request.accepts.sort!{ |x, y| ajax_request_types.include?
>> (y.to_s) ? 1 : -1 } if request.xhr?
>> end
>>
>> What it does is sort the content types the browser claims to accept
>> so
>> that the ajax'ey ones are first. That seems to fix a lot of this sort
>> of problem.
>>
>> Attribution for original code:http://codetunes.com/
>>
>> Hope this helps with your problem.
>>
>> On Feb 21, 2009, at 8:48 PM, groovetrain wrote:
>>
>>
>>
>>> For the life of me I can't figure this one out, although I can't
>>> find
>>> anyone else who's attempted to do this, and probably with good
>>> reason.
>>
>>> Context: blog using AJAX
>>
>>> What I'm trying to do: when the user initially saves a blog entry,
>>> or
>>> when auto-saving, I want subsequent saves to not create a new blog
>>> entry
>>
>>> Why I can't just reload the partial:
>>> - That would interrupt the flow of blog-writing
>>> - I'm using FCKEditor, and it takes a few seconds to pop in place
>>> upon the reload of the partial (which is less than elegant)
>>
>>> The solution that I came up with was to use the returned and
>>> evaluated
>>> javascript/jQuery to re-write parts of the form so that it would
>>> submit to my blog_entries controller and be treated correctly.
>>> So, I
>>> have to change the method from POST to PUT, which in rails means
>>> creating a hidden form element like so:
>>
>>> <input type="hidden" name="_method" value="put" />
>>
>>> and then changing the form action from
>>
>>> action="/blog_entries"
>>> to
>>> action="/blog_entries/21" - or whatever the id is of the newly
>>> created
>>> blog entry
>>
>>> There's also a little place in the onsubmit attribute that I'm also
>>> changing from /blog_entries to /blog_entries/21
>>
>>> What's happening is it will submit, the updates will be successful,
>>> but my update method will always respond with format.html, not
>>> format.js, which is meaning I'm getting forwarded to my default
>>> scaffold "show" page, which greets me with a nice "Your blog entry
>>> was
>>> successfully updated."
>>
>>> My update method looks like:
>>
>>> def update
>>> @blog_entry = @user.blog_entries.find(params[:id])
>>
>>> respond_to do |format|
>>> if @blog_entry.update_attributes(params[:blog_entry])
>>> flash[:notice] = 'Blog entry was successfully updated.'
>>> format.js { redirect_to(:action => 'success') } if
>>> request.xhr?
>>> format.html { redirect_to(@blog_entry) }
>>> format.xml { head :ok }
>>> else
>>> format.html { render :action => "edit" }
>>> format.xml { render :xml => @blog_entry.errors, :status
>>> => :unprocessable_entity }
>>> end
>>> end
>>> end
>>
>>> I've checked the server console, and it is receiving the PUT method,
>>> and my blog entry is getting updated, but it doesn't make any
>>> sense to
>>> me why it is always being redirected, seemingly skipping over the
>>> format.js line every time. I've tried this line like this, and each
>>> of them yields the same thing:
>>
>>> format.js { redirect_to(:action => 'success') }
>>> format.js if request.xhr? (with the appropriate
>>> update.js.rjs
>>> file in place)
>>> format.js
>>
>>> If anyone could help me figure this one out it would be great!
>>> Sorry
>>> about the long post, I wanted to be clear what was going on.
>>
>>> Groove
> >
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby
on Rails: Talk" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---