Search code examples
javascriptruby-on-railshamlujs

How to access ruby instance variable in Javascript through HAML template


I have a partial that is being rendered with the CommentsController. Lets call this partial _edit_comment.html.haml where I'm passing in the instance variable @comment.

$("#edit-comment-#{object.id}").html('#{ escape_javascript(render :partial => 'edit_comment', locals: { story: @comment.story, comment: @comment }) }')

In my partial, I have JS which I want to call when a button is clicked.

= form_with model: [story.industry, story, comment], class: "w-100" do |f|
  %fieldset.w-100                                    
    = f.text_area :content, rows: 2, hide_label: true, class: 'px-2 py-1', placeholder: "Leave a comment" 
  %fieldset  
    = f.submit "Update", class: 'btn submit text-white mt-2'   
    %span.btn.cancel.text-white.mt-2 Cancel-
:javascript
  $(document).ready( function() {
    $('.cancel').click(e => {
      console.log("#{@comment}")       
    })
  } );

I understand that "#{}" works for strings but I want to be able to access the entire object instead. Currently, the result of the console will be

#<Comment:0x000055971a9c5278>

I need to get the entire ruby object because I want to pass it as a variable to another partial which will be rendered. Something like this:

:javascript
  $(document).ready( function() {
    $('.cancel').click(e => {
      $("#edit-comment-#{object.id}").html('#{ escape_javascript(render :partial => 'edit_comment', locals: { story: "#{@comment}".story, comment: "#{@comment}" }) }')
    })
  } );

Is this possible?


Solution

  • Consider using a nested form...or a similar variation

    Here is a link to the rails guide: https://guides.rubyonrails.org/form_helpers.html#nested-forms

    It looks liked you want a nested form, and you want to edit comments, using the same form that is used when you want to edit a story (?) or story industry (?). If that's the case then perhaps investigate using nested forms.

    Secondly, another gotcha to be aware of: you cannot place one HTML form inside another one: that can't be done. So the javascript workflow you are proposing must render the edit comment partial OUTSIDE the form you have above.........or you can employ a mix of javascript and use the nested forms method above using jquery/javascript. Ryan Bates has devoted two excellent rails casts to this topic - here they are (1) and (2):

    But to answer your question:

    ...it can't be done like that

    I don't know if you can actually do it the way you were originally trying to do it - to pass complex ruby objects like that

    ...but there are solutions:

    (A) Render the partials and hide as needed

    Why not try a different approach: render the partial directly in your html (but hide it from being seen by the user).

    When the button is clicked, you can simply toggle the visibility of the already existing partial.

    Since you're using jQuery:

    (B) Fetch the partial ayschronously

    You can fetch the partial with request in your event handler - stimulus js does this type of workflow very well. You can use the fetch API to get the html partial, or you can use AJAX if you want to support internet explorer etc.

    ...........you would have to decide whether you want the form nested, or you are happy with completely separate forms.