David Harbage
David Harbage

Reputation: 697

Using a modal to show edit path

I am writing a ruby on rails 3 application that is very simple: users can create posts that display on their homepage, as well as edit these posts and delete them. My question pertains to the editing function. I know the typical way to edit something is to call

<%= link_to "edit", edit_post_path(post_item) %>

where post_item is the selected post. However, instead of redirecting the user to the edit page (/views/posts/edit.html.erb), I would like to make it so that when the user clicks the edit button, the edit page is rendered in a modal. I am using twitter-bootstrap, and know how to create modals. Any ideas?

Upvotes: 1

Views: 4965

Answers (3)

aRtoo
aRtoo

Reputation: 1892

I tried to follow the voted answer but it did not work for me. So I have planned a working solution. Here's what I did.

1.) I first added //= require jquery-ui on my JavaScript assets

2.) I created a form for editing my notes. Id names are being used here. Also if you notice, my URL on the form tag is an empty string. You'll see on my JavaScript.

<%# this is the form that is used to update the notes %>
<div id="dialog" title="Update Notes">
  <%= form_tag('', method: "put", class: 'comment_form', id: 'workorder_form') do %>
    <div>
      <%= text_area_tag(:message, '', class: "form-control workorder_notes_text_area") %>
    </div>
    <div class="mt-5 text-right">
      <%= submit_tag("Submit", type:"submit", class:"btn btn-primary", id: 'workorder_notes_button')  %>
    </div>
  <% end %>
</div>

3.) for every notes that are listed I get the note id and attach it to the note message

<%# id attribute is also used to get the %>
   <td class="text-center" id="note_message_id_<%=note.id%>">
        <%= note.message %>
   </td>

4.) for the edit button I used the data attribute to add some data like ID of the note. also, classname edit-workorder is being used to listen for a click event for each button

<button data-notesid="<%= note.id%>" class="btn btn-success edit-workorder">
   <span class="icon icon-pencil"></span> 
</button>

JavaScript part:

5.) listen to click event for every edit button

$('.edit-workorder').click(function(e) {}

The next JavaScript are inside the click event function.

6.) This grabs the data inside the data attribute of the button. refer to number 4.

const notesId = $(this).data('notesid');

7.) this will grab the current message and trim it.

// this will return the current notes and removes spaces
    const notesMsg = $(`td#note_message_id_${notesId}`)
      .text()
      .trim();

8.) grabs the dialog.

// dialog modal
    // this comes from _workordernotes.html.erb
    const dialogUI = $('#dialog');

9.) this will be attached to the form_tag so the URL will be dynamic. I need this because of the IDs of the notes.

 $('#workorder_form').attr('action',`${workorderId}/workordernotes/${notesId}`);

10.) this is the dialog part. The close method is important because when you first open the modal and close it. the action URL won't change because the state is preserved. You need to destroy the element or put them in their initial state (the empty string state).

  // dialog API
    // https://api.jqueryui.com/dialog/
    dialogUI.dialog({
      modal: true,
      draggable: false,
      height: 400,
      width: 600,
      resizable: false,
      // show: { effect: 'slideDown', duration: 1000 },
      // on close dailog should return from its normal state or else
      // action attribute wont change
      close: function(e, ui) {
        dialogUI.dialog('destroy');
      }
    });

11.) lastly this will attach the current message on the text area of the form.

$('textarea#message').val(notesMsg);

workordernotes_controller.rb RoR

12.) here's my update method. This is not done yet. I just need to create a try-catch block here then it's done, but overall, it's working.

def update
   Spree::Workordernote.find(params[:id]).update(message: params[:message])
   redirect_to admin_workorder_path(params[:workorder_id])
end

Full JavaScript code:

$(document).ready(function() {
  $('#dialog').hide();

  const workorderId = $('tbody#table_body').data('workorderid');

  $('.edit-workorder').click(function(e) {
    e.preventDefault();

    // returns the workordernotes ID
    // this comes from _workordernotes.html.erb button
    const notesId = $(this).data('notesid');

    // this will return the current notes and removes spaces
    const notesMsg = $(`td#note_message_id_${notesId}`)
      .text()
      .trim();

    // dialog modal
    // this comes from _workordernotes.html.erb
    const dialogUI = $('#dialog');

    // this should be called before dialog or else action attr wont attached
    $('#workorder_form').attr(
      'action',
      `${workorderId}/workordernotes/${notesId}`
    );

    // dialog API
    // https://api.jqueryui.com/dialog/
    dialogUI.dialog({
      modal: true,
      draggable: false,
      height: 400,
      width: 600,
      resizable: false,
      // show: { effect: 'slideDown', duration: 1000 },
      // on close dailog should return from its normal state or else
      // action attribute wont change
      close: function(e, ui) {
        dialogUI.dialog('destroy');
      }
    });

    // attached the old message on text area
    $('textarea#message').val(notesMsg);
  });
});

Upvotes: 0

Lievcin
Lievcin

Reputation: 938

Not sure if you're still working on this, I had a similar problem and resolved it by doing the following:

I'm using the jquery-ui dialog, not bootstrap modal as couldn't make it work. so add the library there should you not have it.

in assets/javascripts/application.js i have

$(document).ready(function() {

        var modal=$('#modal');
        $('#edit_house_link').click(function(e) {
          var edit_url = $(this).attr('href'); 
          modal.load(edit_url + ' #content',function(){
            modal.dialog("open");
          });
        });
        modal.dialog({ autoOpen: false, title: "Your title", draggable: true,
        resizable: false, modal: true, width:'auto'});

    });

then in my layouts/application.html.erb i have

<p id="modal"></p>

somewhere inside the body of the page, which is the element where we're going to load all the info.

in my index view i have the link that handles this, note that my model is called house, so change to whatever yours is called.

<%= link_to 'Edit', edit_house_path(house), :remote => true, :id => 'edit_house_link' %>

As you can see, i'm giving this the edit_house_link id, which I'm referencing in the application.js file.

inside the house controller i have:

def update
  @house = House.find(params[:id])
  respond_to do |format|
    if @house.update_attributes(params[:house])
      format.js 
    else
      format.js
    end
  end
end

and then i have three views, one is the edit.html.erb, update.js.erb and _form.html.erb for the views/houses folder.

so inside views/houses/update.js.erb i have:

<%- if @house.errors.any? %>
  $('#modal').html('<%= escape_javascript(render('form')) %>');
<%- else %>
  $('#modal').dialog('close');
  $('#modal').remove();
  window.location.reload();
<%- end %>

this code is to prevent the modal from closing, should there be errors. as you can see, if no errors are present, then it closes the dialog and refreshes the page to display the changes.

in the views/houses/edit.html.erb file this is what I have:

<div id="content">
<%= render :partial => 'form' %>
</div>

so if you look again at the content id in the application.js file, we're loading this inside the modal.

and finally the partial views/houses/_form.html.erb

<%= simple_form_for(@house, :html => { :class => 'form-vertical' }, :remote => true) do |f| %>
<fieldset>
    <%= f.input :address %>
    <%= f.input :postcode %>
    <%= f.input :city %>
    <%= f.input :country %>
</fieldset>
    <div class="form-actions">
    <%= f.button :submit %>
    </div>
<% end %>

I'm using simple_form, so you can adapt to your needs by having a normal form, but i had to add the :remote => true attribute for it to actually work.

hopefully this helps!

Upvotes: 1

VadimAlekseev
VadimAlekseev

Reputation: 2248

Easiest way is to set remote => true option to your link. And on client side render js which will render partial with your form, and call $('#myModal').modal(options) and open the dialog.

Upvotes: 0

Related Questions