Petros Kyriakou
Petros Kyriakou

Reputation: 5343

How to dynamically load data in Rails 4

So i have this page that has the functionality of creating a new course and listing all courses on the same page.

What i want,is when i click on the id of the courses to dynamically load all lessons associated with that id ion a div below that.

this is the index.html.erb of courses

<% provide(:title, 'All courses') %>
<h1>All Courses </h1>
<p><%= link_to "Back", admin_path, class: "btn-submit" %></p>

<div class="text-center">
  <%= form_for [:admin, @course] do |f|%>
  <%= render 'shared/error_messages', object: f.object %>
  <p>
    Create a new course
  </p>

    <%= f.label :title%>
    <%= f.text_field :title%>

    <%= f.label :description%>
    <%= f.text_area(:description, size: "24x2") %>

    <%= f.label :duration%>
    <%= f.number_field(:duration, step: 0.5) %>

    <%= f.label :category%>
    <%= f.text_field(:category) %>

    <%= f.label :tag%>
    <%= f.text_field(:tag) %>


    <%= f.submit "Create Course", class: "btn-submit" %>
  <% end %>
</div>

<table class="display responsive no-wrap text-center" id="datatableadmin">
  <thead>
    <tr>
      <th>id</th>
      <th>Title</th>
      <th>Description</th>
      <th>Duration</th>
      <th>Category</th>
      <th>Tag</th>
      <th>Deletion</th>
    </tr>
  </thead>
  <tbody>
    <% @courses.each do |c| %>
      <tr>
        <td><%= c.id %></td>
        <td><%= best_in_place c, :title, url: "courses/#{c.id}", cancel_button: 'X' %></td>
        <td><%= best_in_place c, :description, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :duration, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :category, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :tag, url: "courses/#{c.id}" %></td>
        <td><%= link_to "Remove", admin_course_path(c), method: :delete, data: { confirm: "Are you sure?" }%></td>
      </tr>
    <% end %>
  </tbody>
</table>
<div class="text-center">
  <small style="color:red;">Edit by clicking the fields and pressing enter. Cancel by clicking the X button</small>
</div>

so basically if you look down where it says c.id, i want the functionality to call all lessons associated with that id when clicking on that id.

Upvotes: 2

Views: 1537

Answers (3)

Cyril Duchon-Doris
Cyril Duchon-Doris

Reputation: 13949

I believe the simplest for an AJAX solution is to add a link_to to every ID and add the relevant server side code as following.

<tbody>
    <% @courses.each do |c| %>
      <tr>
        <td><%= link_to(c.id, course_lessons_path(c), remote: :true) %></td>

routes.rb

resources :courses do
  resources :lessons
end

lessons_controller.rb

  def index
    find_lessons
    respond_to do |format|
      # Your other formats
      format.js
    end
  end

  def find_lessons
    if params[:course_id]
      @lessons = Course.find(params[:course_id]).lessons
    else
      # Error or @lessons = Lesson.all
    end
  end

lessons/index.js

var where = $("ul#where-to-load-lesson-id")
where.empty() # Reset list
<% @lessons.each do  |lesson| %>
  # Simple working code
  $('.lessons').append('<li><%= lesson.title %></li>');

  # Advanced code for nested for loops that could use debugging
  # To use if you have nested properties (`lesson has_many exercises`)
  var li = $("<li>", { 
    'class': 'lesson', 
    'html': '<%= lesson.content %>' }
  ).appendTo(where)
  var ex_list = $("<ul>", { 'class': 'exercise-list' }).appendTo(li)
  <% lesson.exercises.each_with_index do |ex, i| %>
      ex_list.append('<li><%= link_to("exercise #{i}", exercice_path(ex)) %></li>');
  <% end %>
<% end %>

Upvotes: 2

Petros Kyriakou
Petros Kyriakou

Reputation: 5343

Following up on Cyril DD response this is what correctly worked as there where some issues with Cyrils code.

in my courses/index.html.erb

<td><%= link_to(c.id, admin_course_lessons_path(c), remote: :true) %></td>

at the bottom i have
<ul class="lessons"></ul>

in my routes.rb

resources :lessons do
  resources :courses
end

in my lessons_controller.rb

def index
    find_lessons
    respond_to do |format|
      format.html
      format.js
    end
  end

  def find_lessons
       if params[:course_id]
         @lessons = Course.find(params[:course_id]).lessons
       else
         @lessons = Lesson.all
         @lesson = Lesson.new
       end
   end

in my index.js.erb file inside admin/lessons/ folder

$('.lessons').empty();
<% @lessons.each do |l| %>
$('.lessons').append('<li><%= l.title %></li>');
<% end %>

Upvotes: 0

dreamingblackcat
dreamingblackcat

Reputation: 991

Since Cyril DD has ajax solution. I will write pre-fetch solution here. It's recommended to use only when there is a few lessons to fetch,usually not more than 2 digit number. I guess courses aren't that long.

<% @courses.each do |c| %>
      <tr>
        <td class="course_id" data-id="<%= c.id %>">
          <%= c.id %>         
        </td>
        <td><%= best_in_place c, :title, url: "courses/#{c.id}", cancel_button: 'X' %></td>
        <td><%= best_in_place c, :description, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :duration, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :category, url: "courses/#{c.id}" %></td>
        <td><%= best_in_place c, :tag, url: "courses/#{c.id}" %></td>
        <td><%= link_to "Remove", admin_course_path(c), method: :delete, data: { confirm: "Are you sure?" }%></td>
      </tr>
<% end %>
///other markups.....
<%= @courses.each do|c| %>
<div id='<%= "lessons_by_course_#{c.id}"%>' class='someClassWithDisplayNone'>
  <%= render partial: "lessons", {locals: {lesson: c.lessons}} %>
</div>
<% end %>

Then in the javascript

$(function(){
  $(".course_id").on("click", function(){
    var id = $(this).data("id");
    $("#lessons_by_course_" + id).removeClass("someClassWithDisplayNone");
  });
})();

Of course, this soultion assumes some things such as the existence of a partial lessons for rendering lessons(you can write direct markup without partial as well) and the existence of one to many relationship between course and lessons. Otherwise, I believe all code is self explantory.

Upvotes: 3

Related Questions