Forum: Ruby on Rails How to get dynamically created inputs from html form back to

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-03 04:03
(Received via mailing list)
Thank you in advance.

Although I have many years of experience in general, including cross-
platform processing, I am not an HTML/Javascript programmer.  As a
result, I do not have certain specific baseline skills and/or knowledge
that are presumed in the Rails and Ajax documentation.

I am experienced with DOM manipulations, so the bare mechanics of
manipulating the browser GUI via Javascript and the DOM tree are self
evident.  However, this does not get the input data back to the server
side of the application.

The immediate question that have is this:  When I dynamically add rows
of inputs to a table, how do get the values entered by the user into
those rows back to the rails application?



The specific example is a screen for editing a question with its list of
answers as a sovereign application.  Both code snips are from
views/question/_form.rhtml.

I realize that I do not have the server side of the interface
represented from controllers/question_controller.rb.  In part, this is
because I have a gap in my knowledge about what the HTTP post will
provide back to the ruby app - it is presumed to be self evident in the
books I have available.




The following javascript is the code that inserts the new row into the
table in the browser context (not the backing RDBMS):

function insertAnswer () {
  table = document.getElementById ("answersTable");
  body = document.getElementById ("answersBody");

  row = document.createElement("tr");
  row.setAttribute("id","");
  // need to build a Ruby friendly naming scheme for
  // elements so they can be passed as parameters
  data = document.createElement("td");
  row.appendChild(data);
  data = document.createElement("td");
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","checkbox");
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  node.setAttribute ("size","3");
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkPresentation");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkAnswerToPresentation");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-document.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkAudio");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkPresentationToAudio()");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-audio.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkArt");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkPresentationToArt()");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-art.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","answer");
  btn.setAttribute("id","delete_"+table.rows.length);
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","deleteAnswer(this)");
  node = document.createElement("img");
  node.setAttribute ("src","/images/trashcan_empty.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  body.appendChild(row);
}



The equivalent RHTML fragment that builds the table from previosly
stored data is:

   <%
  answers = Answers.find (:all, :conditions => "parent_id = " +
@question.id.to_s)
  for answer in answers
    presentation = Presentations.find_by_id
(answer.presentation_id.to_s);
  -%>
    <tr>
      <td><%= answer.id %></td>
      <td><%= answer.seq %></td>
      <td><input type="text"><%= answer.iscorrect %></input></td>
      <td><%= text_field_tag 'answer.points', answer.points %></td>
      <td><%= text_field_tag 'presentation.text', presentation.text %
></td>
      <td><input name="loadText" type="img" value="file..."
src="/images/file-document.png" onClick="linkPresentation()"/></td>
      <td><%= presentation.audio %></td>
      <td><input name="loadText" type="img" value="file..."
src="/images/file-audio.png" onClick="linkAudio()"/></td>
      <td><%= presentation.visual %></td>
      <td><input name="loadText" type="img" value="file..."
src="/images/file-art.png" onClick="linkArt()"/></td>
      <td><INPUT NAME="delete" TYPE="img" VALUE="Del"
src="/images/trashcan_empty.png" onClick="deleteAnswer(this)"/></td>
    </tr>
  <% end %>
6c27f78ab0eee78732ae54e8b8718b84?d=identicon&s=25 David Felstead (Guest)
on 2006-06-03 06:50
(Received via mailing list)
If you name your input tags with a [] suffix, the data returned in
your 'params' hash will be returned in an array.  For example, if the
following HTML is submitted back to your controller:

<input name="foo[]" type="hidden" value="a"/>
<input name="foo[]" type="hidden" value="b"/>
<input name="foo[]" type="hidden" value="c"/>

If you look in params['foo'] you will see ['a', 'b', 'c']

At a bare level that is the way it works.  You can get even cleverer
by using named fields in your params to better encapsulate your data -
you might want to look in the source files
rails/action_pack/lib/action_controller/cgi* to examine how the params
hash is constructed.

Cheers!

-David Felstead
299a899790e493870c7e3d7c45e6516d?d=identicon&s=25 Scott Becker (Guest)
on 2006-06-03 09:34
(Received via mailing list)
This isn't a direct answer to your question (someone else handled that
already), but on the javascript / DOM generation side of things, I
just wanted to point you in the direction of the super handy
Script.aculo.us Builder class:

http://wiki.script.aculo.us/scriptaculous/show/Builder

You can really cut down the lines needed to build those table rows...
its a nice thing :)

--
Scott Becker
Electro Interactive, Inc.
http://www.ElectroInteractive.com
Blog: http://synthesis.sbecker.net
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2006-06-04 03:53
(Received via mailing list)
Scott Becker wrote:
> This isn't a direct answer to your question (someone else handled that
> already), but on the javascript / DOM generation side of things, I
> just wanted to point you in the direction of the super handy
> Script.aculo.us Builder class:
>
> http://wiki.script.aculo.us/scriptaculous/show/Builder
>
> You can really cut down the lines needed to build those table rows...
> its a nice thing :)

Thanks, that's useful to know.

A much quicker method that often works for building complex structures
is to just clone one that already exists in the page (either a visible
one or a hidden prototype), and then insert the dynamic parts in the
appropriate places.

--
We develop, watch us RoR, in numbers too big to ignore.
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-04 04:40
(Received via mailing list)
Thank you!

This is too easy ... and it is beginning to make sense.

I will definitely look at the cgi* files to see what is going in
internally.
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-04 04:40
(Received via mailing list)
Looks very interesting ... I'll certainly consider it.

Thanks!
A2b2f4ee23989dc68529baef9cbddcd6?d=identicon&s=25 Julian 'Julik' Tarkhanov (Guest)
on 2006-06-04 05:21
(Received via mailing list)
On 3-jun-2006, at 9:31, Scott Becker wrote:

> This isn't a direct answer to your question (someone else handled that
> already), but on the javascript / DOM generation side of things, I
> just wanted to point you in the direction of the super handy
> Script.aculo.us Builder class:
>
> http://wiki.script.aculo.us/scriptaculous/show/Builder
>
> You can really cut down the lines needed to build those table rows...
> its a nice thing :)

Holy bejezus. Never thought there is such a thing there. Many thanks,
my sunday is spent :-)

--
Julian 'Julik' Tarkhanov
please send all personal mail to
me at julik.nl
A2b2f4ee23989dc68529baef9cbddcd6?d=identicon&s=25 Julian 'Julik' Tarkhanov (Guest)
on 2006-06-04 05:21
(Received via mailing list)
On 4-jun-2006, at 4:40, David Johnson wrote:

> Looks very interesting ... I'll certainly consider it.
>
> Thanks!

I don't know what is going on with insertions, but I know that an
input _DELETED_ through the DOM in Safari is still going to get
submitted. I would excercise double alertness if I was on Safari in
your place.

--
Julian 'Julik' Tarkhanov
please send all personal mail to
me at julik.nl
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-04 05:33
(Received via mailing list)
I would hope that it would be submitted - without a submission I would
not know to delete it from the database.

I will certainly construct a full suite of tests to ensure that oddities
of all of the browsers are being covered.
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-05 04:15
(Received via mailing list)
I spoke too soon.  This  works for all text input elements, but it
appears that this does not work if the element is a checkbox.

I'll have to check the cgi* source code still, but someone might be able
to answer this more quickly.

Is this a bug in rails or "by design" behavior?

logged output of submission:
# note that iscorrect should contain ["","on",""], at the very least.

{"iscorrect"=>["on"], "audiofilename"=>["xxx", "", "yyy"],
"commit"=>"Save", "imagefilename"=>["", "", ""], "points"=>["", "", ""],
"action"=>"update", "id"=>"16",
"question"=>{"explanation_presentation"=>{"textvalue"=>""},
"tip_presentation"=>{"textvalue"=>"Matthew 1:18"}, "points"=>"5",
"time_allowed"=>"30", "presentation"=>{"textvalue"=>"Who was found to be
with child through the Holy Spirit?"}, "parent"=>{"name"=>"Matthew (red
level)"}}, "controller"=>"question", "presentationtext"=>["", "aaa",
""], "parent_id"=>{"1"=>""}}

["on"]
["", "aaa", ""]
["xxx", "", "yyy"]
["", "", ""]


My environment is:
WEBrick 1.3.1
ruby 1.8.4 (2005-12-24) [i386-linux]

excerpt from Ruby controller:

  def update

    p params

    isCorrect=params['iscorrect']
    presentationText = params['presentationtext']
    audioFileName = params['audiofilename']
    imageFileName = params['imagefilename']
    p isCorrect
    p presentationText
    p audioFileName
    p imageFileName

  end



javascript excerpt:

function insertAnswer () {
  table = document.getElementById ("answersTable");
  body = document.getElementById ("answersBody");

  row = document.createElement("tr");
  row.setAttribute("id","");
  // need to build a Ruby friendly naming scheme for
  // elements so they can be passed as parameters
  data = document.createElement("td");
  row.appendChild(data);
  data = document.createElement("td");
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","checkbox");
  node.setAttribute ("name","iscorrect[]")
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  node.setAttribute ("size","3");
  node.setAttribute ("name","points[]")
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  node.setAttribute ("name","presentationtext[]")
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkPresentation");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkAnswerToPresentation");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-document.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  node.setAttribute ("name","audiofilename[]")
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkAudio");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkPresentationToAudio()");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-audio.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  node = document.createElement("input")
  node.setAttribute ("type","text");
  node.setAttribute ("name","imagefilename[]")
  data.appendChild(node);
  row.appendChild(data);
  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","linkArt");
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","linkPresentationToArt()");
  node = document.createElement("img");
  node.setAttribute ("src","/images/file-art.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  data = document.createElement("td");
  btn = document.createElement("button")
  btn.setAttribute ("name","answer");
  btn.setAttribute("id","delete_"+table.rows.length);
  btn.setAttribute ("type","button");
  btn.setAttribute ("onClick","deleteAnswer(this)");
  node = document.createElement("img");
  node.setAttribute ("src","/images/trashcan_empty.png");
  btn.appendChild(node);
  data.appendChild(btn);
  row.appendChild(data);

  body.appendChild(row);
}

















1
)



h<










()");
y


Ù
);







t")













")







t")







}

utt
	butt





d");
Q




taÁ

h<


;


}
6c27f78ab0eee78732ae54e8b8718b84?d=identicon&s=25 David Felstead (Guest)
on 2006-06-06 01:55
(Received via mailing list)
HTML check box tags do not POST a value to the server if they are
unchecked, so in this case the behaviour you're experiencing is
correct, if undesired.

Have a look at the API call for check_box
(http://ap.rubyonrails.com/classes/ActionView/Helpe...)
to see how you can work around this.  The cliff notes version is that
you add a second hidden input field to post a value for unchecked
checkboxes like so:

<input type="checkbox" name="foo[]" value="on"/>
<input type="hidden" name="foo[]" value="off"/>

This will solve your problem - instead of ["on"] you will get
["off","on","off"]

Cheers!

-David Felstead
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-06 03:37
(Received via mailing list)
I was beginning to suspect that it might be a function of the HTML
checkbox standard - thanks for the confirmation.

I have seen this idiom frequently, and it seemed like an odd thing to
not catch in Rails testing.  It makes sense now.

Thanks again for the feedback.
Be09addcbb47f2a684fa5c48bac94149?d=identicon&s=25 David Johnson (Guest)
on 2006-06-06 05:17
(Received via mailing list)
The idiom suggested resulted in extra values being returned under
Firefox (all of the hidden values were _always_ returned).

Here is the idiom I ended up using for checkboxes:

function checkboxClick(element) {
  inputValue = element.firstChild

  if (inputValue.getAttribute ("value") == "off")
  {
    inputValue.setAttribute ("value", "on")
    element.setAttribute ("background","/images/cbChecked.png");
  }
  else {
    inputValue.setAttribute ("value", "off")
    element.setAttribute ("background","/images/cbUnchecked.png");
  }
}


function insertRow () {
  table = document.getElementById ("answersTable");
  body = document.getElementById ("answersBody");
  row = document.createElement("tr");

// other non-checkbox stuff not shown ...

  data = document.createElement("td");
  data.setAttribute("id","correct_"+table.rows.length);
  data.setAttribute ("background","/images/cbUnchecked.png");
  node = document.createElement("input")
  node.setAttribute ("type","hidden");
  node.setAttribute ("name","iscorrect[]")
  node.setAttribute ("value","off")
  node.setAttribute ("style", "background-repeat: no-repeat")
  data.appendChild(node);
  data.setAttribute ("onClick","checkboxClick(this)");
  row.appendChild(data);

// other non-checkbox stuff not shown ...

  body.appendChild (row)
}
This topic is locked and can not be replied to.