2

I'm venturing in Ruby on Rails for a while but never did anything with js. I searched the internet some tutorials but nothing went right to what I wanted.

I have a rails application where in one view I have two partials, one form and another that shows a list of tasks that were created by the form. I want that when the submit button is clicked, the partial list is updated with the new newly created task, while also keeping the others that were created before.

Anyone have a better link to help me? Or a little time to explain to me?

Edit:

with the help of the answers below, I changed my code to:

_to_do_list.html.erb ( with the partial form, it should update the form below, list when pressed the button subit form.)

<%= form_for @todo_list, remote: true do |f| %>
....
<div class="field">
      <%= f.label :name%><br />
      <%= f.text_field :name %>
    </div>
    <div class="field">
      <%= f.label :description %><br />
      <%= f.text_area :description %>
    </div>

    <div class="field">
      <%= f.label 'Público?'%>
      <%= f.check_box :is_public %>
    </div>

    <div class="actions">
      <%= f.submit %>
    </div>
    <div id= "show_list"> 
      <%= render 'show'  %> 
    </div>

And, partial show, not changed, which must be updated:

<table id="list" border="1" >
  <tr>
    <th>Name</th>
    <th>Description</th>
    <th>is public?</th>
    <th>Add Tasks</th>
    <th>erase List</th>
  </tr>
  <% @todo_lists.each do |f|%>
  <tr>
    <td><%=f.name %></td>
    <td><%=f.description%></td>
    <td><%=f.is_public  %></td>
    <td><%= link_to new_task_path :method => :get %></td>
    <td> <%= link_to 'Destroy', f, confirm: 'Are you sure?', method: :delete%></td>
  </tr>
  <% end %>
</table>

And, new js file :

$("#todos").html('<%=j render partial: "to_do_lists/show", locals: { todo_lists: @todo_list } %>');

Controller:

def create
    @todo_list = ToDoList.new(params[:to_do_list])
    @todo_list.member_id = current_member.id

    respond_to do |format|
      if @todo_list.save
        format.js{}
        format.html { redirect_to @todo_list, notice: 'Lista ' +@todo_list.name+ ' foi criada com sucesso!' }
        format.json { render json: @todo_list, status: :created, location: @todo_list }
      else
        format.html { render action: "new" }
        format.json { render json: @todo_list.errors, status: :unprocessable_entity }
      end
    end
  end

The idea is to create a new row in the table with the new task created without leaving the page.

Thank you.

Edit

4
  • We can't really help very specifically without seeing your HTML and a more detailed description of what you're trying to do. Conceptual questions like "How, in general do I do X?" are much harder to answer and make sure the answer is on target than questions like "Here's my HTML and I'm trying to accomplish Y? How would I do that?". It sounds like you're asking: "How do I manipulate DOM elements in the page using Javascript?". That is far too broad for StackOverflow. Please make your question much more specific. Commented Aug 3, 2014 at 2:35
  • OK, I'll edit. Thanks. Commented Aug 3, 2014 at 2:39
  • Check out railscasts.com/episodes/136-jquery-ajax-revised Commented Aug 3, 2014 at 2:45
  • If you include the actual generated HTML (what the browser sees - you can get it with View/Source on the relevant web page) rather than the Rails template, then those of us who know HTML and javascript (a very large audience of potential helpers) can help you rather than only those people who understand your rails template (a much smaller audience). Commented Aug 3, 2014 at 2:59

2 Answers 2

1

What you need is AJAX. If you look at docs it says

JavaScript can also make requests to the server, and parse the response. It also has the ability to update information on the page. Combining these two powers, a JavaScript writer can make a web page that can update just parts of itself, without needing to get the full page data from the server. This is a powerful technique that we call Ajax.

For this to work you first need to change your form to have remote: true option which basically allows your forms values to be submitted by js. Your form will look like

<%= form_for @todo_list, remote: true do |f| %>
  ...
  // form fields
<% end %>

<div id= "some-id"> // to target this div to render your partial from js
  <%= render 'show'  %>  // it doesn't make sense to have your partial inside form and 
</div>

it should be outside your form

In your create action you need to have a respond_to block

def create
  @todo = Todo.new(todo_params)
  respond_to do |format| 
    if @todo.save
      format.js{}  # this will allow rails to look for create.js.erb in your views/controller directory where you can use your js to render your partial or append newly created todo
    else
      format.html{render "new"}
    end
  end
end

In your create.js.erb template use your js to render partial or append your newly created item

$("#some-id").html("<%=j render "show" %>");

OR

Instead of rendering whole partial again what you can do it just append your newly created todo in your table by js

$("#table-id").append("<%=j render partial: "todo", locals: {f: @todo} %>");

Here your _todo.html.erb will contain

<tr>
  <td><%=f.name %></td>
  <td><%=f.description%></td>
  <td><%=f.is_public  %></td>
</tr>

For details checkout Working with javascript in rails

UPDATE

If you look at your partial you have

<table id="list" border="1" >
  <% @todo_lists.each do |f|%> // your @todo_lists is a collection of todos 
    <tr>
      <td><%=f.name %></td>
      <td><%=f.description%></td>
      <td><%= link_to new_task_path %></td> // you don't need to specify method here link_to by default uses a get request
      <td> <%= link_to 'Destroy', f, confirm: 'Are you sure?', method: :delete%></td>
    </tr>
  <% end %>
</table>

Change your controller method to this:

def create
  @todo_list = ToDoList.new(params[:to_do_list])
  @todo_list.member_id = current_member.id
  respond_to do |format|
    if @todo_list.save
      @todo_lists = ToDoList.all // get a collection of todos to render in your partial
      format.js{}
      format.html { redirect_to @todo_list, notice: 'Lista ' +@todo_list.name+ ' foi criada com sucess!' }
      format.json { render json: @todo_list, status: :created, location: @todo_list }
    else
      format.html { render action: "new" }
      format.json { render json: @todo_list.errors, status: :unprocessable_entity }
    end
  end
end

And in your create.js.erb file

$("#show_list").html('<%=j render partial: "to_do_lists/show", locals: { todo_lists: @todo_lists } %>'); //You don't have an element with id = todos. You have it with show_lists so change that

Also render your partial after closing your form. It doesn't make sense to show todos inside a form

Sign up to request clarification or add additional context in comments.

4 Comments

For some reason your solution also is returning me a 500 inspect the browser, not even this partial updating of the list:(.I'm trying to solve your solution and the friend above seem to be the best to take..
@Amancio This is not the entire solution. I just gave you a demo on how ajax works. Can you edit your question and post what you have tried?
ya, sure, I'll edit. have those who prefer git if you want, go up there too. But basically I did what was suggested.
@Amancio read edgeguides.rubyonrails.org/… to get a better understanding of how ajax works :)
1

I want that when the submit button is clicked, the partial list is updated with the new newly created task, while also keeping the others that were created before.

This would indeed be achieved with ajax

--

Ajax

Ajax (Asynchronous Javascript And XML) is basically a way to send pseudo-requests to your backend from JS.

Since you're new to Rails, let me explain the idea of it being stateless. Stateless applications (which constitutes most built using the HTTP protocol) treat each request as "new" - not storing any personalized details for use each time

This means that if you want to send a request to Rails without reloading your page, you'll need to both send and receive data, appending it to your already loaded page. This is what Ajax does.

--

Rails

What you're asking is relatively simple

You need to do the following:

  1. Send your form data to Rails using ajax
  2. On the Rails backend, capture the event, perform functionality & respond
  3. On your front-end, "listen" to the response & append to the page

There are a number of ways to achieve this. For simplicity's sake, I'll detail the most direct way to achieve it (this uses the Rails UJS driver):

#app/views/todo_lists/new.html.erb
<%= form_for(@todo_list), remote: true do |f| %>

This will capture your form's submit action & send the update to the required URL. This means you'll be able to capture the request in your controller & then handle it in the backend:

#app/controllers/todo_lists_controller.rb
Class TodoListsController < ApplicationController
   def create
      @todo_list = TodoList.new(todo_params)
      @todo.save

      respond_to do |format|
         format.js #-> loads /views/todo_lists/create.js.erb
         format.html
      end
   end
end

This will allow you to call the following:

#app/views/todo_lists/create.js.erb
$("#todos").html('<%=j render partial: "todo_lists/your_partial", locals: { todo_lists: @todo_lists }" %>');

This will call your JS for the user - appending the request to your view; making it appear like no refresh was needed :)

4 Comments

this ("#todos") should be the id of the div that calls the partial in my first partial _to_do_lists? for some reason is not updating the list:(
It should be the ID of the element containing the table, sorry. Would you like me to add the code for you?
I edited my original post with the tips I received and put a div where I call this partial list referenced in this div js.erb file. Please add the code, do not know why, but it is not updating the list:(. Thanks
Your help was very useful, thank you! We managed to make it work: D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.