0

I'm running javascript on Ruby on Rails to insert a form dynamically into a page when the user clicks on a link.

It's not working, but one particular bug gets my goat: that my form return form.children UNDEFINED in the debugging console.

Here is the form (in Rails):

<div class="field">
<%= f.label :name, 'Tag:' %>
<%= f.text_field :name %>   
</div>

And here is the javascript in application.js;

function add_fields(link, association, form) {
console.log(form);
console.log(form.children);
link.insertBefore(form, link.previousSibling.previousSibling);
}

I have tested whether the app actually calls the javascript by outputting text and the like and it works. The problem is clearly with my form.

Finally, the console output:

<div class="field">
<label for="post_tags_attributes_4_name">Tag:</label>
<input id="post_tags_attributes_4_name" name="post[tags_attributes][4][name]" size="30" type="text" />  
</div> application.js:21
undefined application.js:22
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8 

By the way, I'm trying to implement railscast #197 on dynamically inserted nested forms and it's been a disaster!

--EDIT---

Here is how I call the add_fields function that builds the field element:

The ruby function:

module TagsHelper
    def link_to_add_fields(name, post_form, association)
        new_object = post_form.object.class.reflect_on_association(association).klass.new
        tag_field = post_form.fields_for :tags, new_object do|tag_form|
            render("tag_field", :f=>tag_form)
        end
        #debugger
        link_to_function(name, "add_fields(this, \"#{association}\", \"#{escape_javascript(tag_field)}\")")
    end
end

And here is the tag_field html:

<div class="field">
    <%= f.label :name, 'Tag:' %>
    <%= f.text_field :name %>   
</div>
Laurent
  • 1,554
  • 19
  • 42

1 Answers1

2

Clearly this won't work because an element can't have a child that is also a sibling.

link.insertBefore(form, link.previousSibling.previousSibling);

Your code want's to put form as a child of link and before one of link's siblings, which would be impossible.

Remember, .insertBefore must be called from the parent that it's being inserted into. So to insert form before link, the .insertBefore must be called from the parent of link.


So maybe you meant to use link.parentNode instead.

link.parentNode.insertBefore(form, link.previousSibling.previousSibling);

This will insert form before the second previous sibling of link.

I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
  • y While your input is useful it doesn't explain why Javascript doesn't recognize my div at all. For instance, if I build a simple div with label and text field, then pass it to the insertBefore, it gets inserted (in the corrected scenario you submitted). However, the div I pass into the function does not. Can I be honest with you? I find it frustrating. – Laurent Dec 10 '12 at 01:23
  • 1
    @Laurent: `form` is a string of HTML? `console.log(typeof form)` You'll have better luck when asking JavaScript questions if you provided the rendered HTML instead of server-side application code. – I Hate Lazy Dec 10 '12 at 01:32
  • I figured it out. I was passing the form (as you can see) and a link, and trying to place the form before the link. I wasn't escaping javascript before. But when I typed $(link) instead, the tag was properly placed. If anyone has a really simple explanation of what the difference between link and $(link) was, and wants to share it, I'd be delighted. – Laurent Dec 10 '12 at 01:52
  • 1
    @Laurent: To be honest, I can't see that you're passing the `form`. All I see is that you're passing `\"#{escape_javascript(tag_field)}\"`, and I don't know what your server renders with that syntax. If it renders a selector string like `"#my_form"`, then you're *not* passing the form. You're passing a selector string *(think CSS)* that hopefully points to your form. So `form` would be that string, and `$(form)` would be passing that string to the `$` function, which I'll assume refers to the `jQuery` library. If it is jQuery, it'll perform a DOM selection using that selector. – I Hate Lazy Dec 10 '12 at 02:01
  • 1
    ...now in your case `link` is being passed `this`, which is presumably the element that received the event. When you do `$(this)`, you're creating a new jQuery object *(again assuming jQuery)* with that element, which would change the meaning of `.insertBefore` because it would be using jQuery's `insertBefore` instead of the native DOM `.insertBefore`. So basically it sounds like you need to do some basic tutorials that cover native DOM methods, and then usage of the jQuery library. – I Hate Lazy Dec 10 '12 at 02:04
  • That was the problem so if you want to accrue approvals copy/paste as separate answer and I'd be happy to oblige – Laurent Dec 10 '12 at 02:31