AJAX ok, but when JavaScript is disable

Hi!
Situation:
I have a form_remote_tag which is anded with submit_tag. There is a
table with a number of rows inside. Action in this form updates all
lines.
In row, the right most cell contains a link_to_remote ‘Delete’
(:url,…etc,:href).
Everything works perfectly when JavaScript is available or not.
But…
Here rise one problem. Everybody know, that for security reason with
actions like delete, we always need request as ‘post’.
When JavaScript is on, we are happy, but when JavaScript is off then
:method => :post not works.
I’ve tried change link_to_remote on submit_to_remote but options :html
seems not works (Firefox). This mean when JS is on is ok, when is off,
not happen.
I couldn’t resolve this problem with another helpers like
button_to_function or remote_function.
We must to remember that we are inside the form.
Questions:
How can we achieve request always as post?
Can we talk about degrade server-side without resolve this problem, is
any point in it ?
How many percent of users have browsers where JavaScript is off ? Is
this problem still important nowadays ?
Any suggestions?

Walde M. wrote:

How many percent of users have browsers where JavaScript is off ? Is
this problem still important nowadays ?
Any suggestions?

I’m sorry if this only half-answers your question, as I cannot speak to
any specific functionality that Rails provides to deal with this (though
I’d be interested in hearing about it).

The percentage of people without JavaScript support is actually pretty
high these days, especially when considering the number of people who
are accessing the web mobile devices that may not support scripting, as
well as those users running browser extensions like Script Blocker that
disable scripting selectively. So yes, it’s worth solving unless you are
coding for a closed environment where you know that this won’t be an
issue. (Typically, this would mean an Intranet, however with the number
of users who carry Blackberries, etc., and who access internal resources
with these devices, this may no longer be as ‘closed’ of an environment
as before.)

So, here’s what I would do. Actually, you have to options:

OPTION 1

1.) Rename the current delete method of your controller that responds to
Post requests. If it’s called ‘delete’, change it to ‘destroy’, or vice
versa. I’ll assume you’ve renamed it from delete to destroy
2.) Create a new method with the old name, i.e. delete, which when
called does much of what any ‘detail’ view would do (show the item
details) except add a form to the view that submits via post to the new
action (i.e. destroy).
3.) Change your list view to use the new ‘delete’ action for the
underlying link URL, but change the JavaScript to submit to the renamed
‘destroy’ action.

If a user has JS enabled, clicking the link will run the destroy method
which responds to the posted form. Otherwise, they’ll see the interim
‘delete’ page which prompts them to submit the form explicitly.

OPTION 2

1.) Create a new JavaScript function in your view that accepts an
argument ‘id’ which finds a form element by id
("#{controller_name}delete#{id}" and replaces it DOM-style with a
javascript link that does whatever your current Delete link does.
2.) Change your list view so that it writes out a post-method form where
the Delete link was previously. Name the submit button as Delete, and
don’t bother with any reset links or buttons. Give each form a unique ID
(something like ‘id="#{controller_name}delete#{primary_id}"’
3.) After each form, add script for an event listener that calls the new
function onload and pass the #{primary_id} as the ‘id’ argument.

Now all form elements will be replaced with textual links unobtrusively
and in a degradable way. Note that you could even leave out steps 1 and
3 in which case you would just have a button rather than the text link.
(And you could then use CSS to replace the button with an image
instead.)

Hope that helps.

Chris B. wrote:

OPTION 2

1.) Create a new JavaScript function in your view that accepts an
argument ‘id’ which finds a form element by id
(“#{controller_name}delete#{id}” and replaces it DOM-style with a
javascript link that does whatever your current Delete link does.
2.) Change your list view so that it writes out a post-method form where
the Delete link was previously. Name the submit button as Delete, and
don’t bother with any reset links or buttons. Give each form a unique ID
(something like ‘id=“#{controller_name}delete#{primary_id}”’
3.) After each form, add script for an event listener that calls the new
function onload and pass the #{primary_id} as the ‘id’ argument.

Here’s an example. The Server-side code is PHP in this case, so ignore
that, but the JavaScript functionality is still applicable. Note that in
my case I was changing thre kinds of forms to links: edit, details, and
delete.

http://pastie.caboo.se/97225

Chris B. wrote:

Thank for your effort, Chris !

OPTION 1 is simply enough and clear. We can say, that it is some kind of
:confirm option. Downside this way is more code, especially when our
application is quite big.
OPTION 2 is very interesting. Although it meets with some resistance,
because we just have one form which contains all rows (updates all rows
at the same time). Every another one inside causes problem.

Maybe is other solution ?

Walde M. wrote:

OPTION 2 is very interesting. Although it meets with some resistance,
because we just have one form which contains all rows (updates all rows
at the same time). Every another one inside causes problem.

Maybe is other solution ?

Is there a possibility of sending me a link to the page? Or, could you
just post the source of the rendered HTML page to http://pastie.caboo.se
and send me a link to that instead?

Chris B. wrote:

Walde M. wrote:

OPTION 2 is very interesting. Although it meets with some resistance,
because we just have one form which contains all rows (updates all rows
at the same time). Every another one inside causes problem.

Maybe is other solution ?

Is there a possibility of sending me a link to the page? Or, could you
just post the source of the rendered HTML page to http://pastie.caboo.se
and send me a link to that instead?

Hi, Chris and everybody.

After simplification code looks like this:

  1. Main document - fragment.
<%= render(:partial => 'document_lines', :locals => {:document => @document}) %>
  1. Partial _document_lines.rhtml . There is form.

<% form_remote_tag(
:url => {:action => :update_document_lines,
:id => document },
:update => ‘ajax_wraper’,
:before => “Element.show(‘spinner’);”,
:complete => “Element.hide(‘spinner’);”
) do %>


<%= render(:partial => …) %>
<% for @document_line in document.document_lines %>
<%= render(:partial => ‘document_line’, :object =>
@document_line, :locals => {:document => document}) %>
<% end %>
<%= render(:partial => …) %>
<%= render(:partial => …) %>

<%= submit_tag “Save changes”%>

  1. Partial _document_line.rhtml .
<%= h(document_line.name) %> <%= text_field 'document_line[]', 'quantity', :SIZE => 6 %> <%= h(document_line.unit) %> <%= link_to_remote "x", {:url => {:action => 'delete_document_line', :id => document_line}, :confirm => "Are you sure you want to delete this line ?", :update => 'ajax_wraper', :before => "Element.show('spinner');", :complete => "Element.hide('spinner');"}, {:href => url_for(:action => 'delete_document_line', :id => document_line)}%>
  1. Controller contains these methods: update_document_lines and
    delete_document_line, of course. Both of them uses test - request.xhr?
    in order to distinguish requests.
    But first of all, requests to these methods must pass test - verify
    :method => :post.It is impossible to pass this test for link_to_remote,
    when JavaScript is off.
    What a pity, that only form_remote_tag works so perfectly when
    JavaScript is on or off. I would like to submit_to_remote can work in
    the same manner.

Any suggestions ?

Walde M. wrote:

<% form_remote_tag(
:url => {:action => :update_document_lines,
:id => document },
:update => ‘ajax_wraper’,
:before => “Element.show(‘spinner’);”,
:complete => “Element.hide(‘spinner’);”
) do %>


<%= render(:partial => …) %>
<% for @document_line in document.document_lines %>
<%= render(:partial => ‘document_line’, :object =>
@document_line, :locals => {:document => document}) %>
<% end %>
<%= render(:partial => …) %>
<%= render(:partial => …) %>

<%= submit_tag “Save changes”%>
  1. Partial _document_line.rhtml .
<%= h(document_line.name) %> <%= text_field 'document_line[]', 'quantity', :SIZE => 6 %> <%= h(document_line.unit) %> <%= link_to_remote "x", {:url => {:action => 'delete_document_line', :id => document_line}, :confirm => "Are you sure you want to delete this line ?", :update => 'ajax_wraper', :before => "Element.show('spinner');", :complete => "Element.hide('spinner');"}, {:href => url_for(:action => 'delete_document_line', :id => document_line)}%>

OK, so if I understand correctly you need two things to happen as-is.
1.) Users can update line-item quantities en masse and then update the
whole order.
2.) Users can remove individual line items from the order.

So how about this, which is a hybrid solution from those I posted
earlier.

1.) Update your line item partial so that instead of writing the AJAX
link it drops a checkbox form element. Give each checkbox a unique id
such as “line_item_#{primary_id}”

2.) Update your update_document_lines action to delete any line items
where the check box is marked.

3.) Add javascript to the page that will, onload, :
3.1.) getElementsByTagName(‘input’);
3.2.) loop through returned element set and test that
element.getAttribute(‘type’) == ‘checkbox’ and that the id is in the
form of ‘line_item_xx’
3.3.) Use DOM to replace the checkbox with the AJAXified link

This will allow non-JS capable browser to delete checked line items when
the rest of the form is submitted. On JS-enabled browsers the checkboxes
will be replaced with the links as they are now.

Chris B. wrote:

So how about this, which is a hybrid solution from those I posted
earlier.

1.) Update your line item partial so that instead of writing the AJAX
link it drops a checkbox form element. Give each checkbox a unique id
such as “line_item_#{primary_id}”

2.) Update your update_document_lines action to delete any line items
where the check box is marked.

3.) Add javascript to the page that will, onload, :
3.1.) getElementsByTagName(‘input’);
3.2.) loop through returned element set and test that
element.getAttribute(‘type’) == ‘checkbox’ and that the id is in the
form of ‘line_item_xx’
3.3.) Use DOM to replace the checkbox with the AJAXified link

Here’s a basic example: Parked at Loopia

(The addEvent function is from
Dean Edwards: addEvent() – My Solution , or substitute your
own event register.)

Chris B. wrote:

OK, so if I understand correctly you need two things to happen as-is.
1.) Users can update line-item quantities en masse and then update the
whole order.
2.) Users can remove individual line items from the order.

So how about this, which is a hybrid solution from those I posted
earlier.

1.) Update your line item partial so that instead of writing the AJAX
link it drops a checkbox form element. Give each checkbox a unique id
such as “line_item_#{primary_id}”

2.) Update your update_document_lines action to delete any line items
where the check box is marked.

3.) Add javascript to the page that will, onload, :
3.1.) getElementsByTagName(‘input’);
3.2.) loop through returned element set and test that
element.getAttribute(‘type’) == ‘checkbox’ and that the id is in the
form of ‘line_item_xx’
3.3.) Use DOM to replace the checkbox with the AJAXified link

This will allow non-JS capable browser to delete checked line items when
the rest of the form is submitted. On JS-enabled browsers the checkboxes
will be replaced with the links as they are now.

Thanks! Chris once more.

I’m impressed by your creativity.
Your new proposition is technically possible.
As you know, every application should be projected according to certain
repetitive rules. Regard for this, I don’t think that it is the best
idea.
I have to admit that I (and anybody on this forum) didn’t present better
proposition. Among of your all concepts I will choose the first.

Thank for your help.

Questions:
How can we achieve request always as post?
Can we talk about degrade server-side without resolve this problem, is
any point in it ?
How many percent of users have browsers where JavaScript is off ? Is
this problem still important nowadays ?
Any suggestions?

Posted viahttp://www.ruby-forum.com/.

This is actually quite a few questions, and not a short answer, i
would suggest searching on these terms:

  • ajax /prototype graceful degradation
    -browser capability detection
  • browser sniffing/identification

On Sep 14, 2007, at 11:29 AM, gene tani wrote:

This is actually quite a few questions, and not a short answer, i
would suggest searching on these terms:

  • ajax /prototype graceful degradation
    -browser capability detection
  • browser sniffing/identification

While I don’t remember all the details, there was a blog post
somewhere fairly recently that talked about this issue and part of
the solution was to have a pair of actions delete/destroy that
paralleled the new/create and edit/update actions that are get/post
HTML pairs. The ‘delete’ action returns a form that performs the
same function as the normal :confirm action when JavaScript is
there. That form does a “normal” post to the destroy action.

I can’t recall how the JavaScript detection was done, but I know the
article covered this.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]