2

I feel really dumb and ashamed. Just started learning jQuery and got stuck at the first day. Basically, I am building all of this in ruby on rails, and I have a list of clients with basic info like name, address, phone number, etc. In the picture below is my page layout (yes, I know.. born artist).

enter image description here

In the left table, I display all the clients I have in database, first column represents client's id and the second column shows their names. In the right table (it is a partial), I display full client's info. My objective is this: by clicking on the row, on the left table, the right table would update and show the info related to that client I selected. Here is what I tried:

<script>
$('#table_clients tbody').on('click', 'tr', function() {
   $("#container_info").replaceWith(
      '<%= j render :partial  => "client_info", :locals =>  {:client => 1} %>'
   );
});
</script>

Notice that I manually specified the client's ID. My question is, how can I identify which row I selected and pass the ID to the erb fragment ?

I can get the ID by calling this:

$(this).find(':nth-child(0)').text();

But dont know how to pass it inside erb fragment. What is the work around for this ? There are probably a better way than using replaceWith method, it was the first idea I got.

Volodymyr Balytskyy
  • 577
  • 1
  • 7
  • 19
  • Add a `data-*` tag to your tr, or a class, which will contain the id of the client (presuming your table is generated from the db), so you can refer to it. – Andrew Brown Feb 07 '16 at 20:51
  • Ok. I am familiar with the data-* tag. But imagine I have it, for each row. (data-client_1, data-client_2, data-client_3, etc..) What then ? how can I pass the id to the partial ? sorry, I must not seeing it clearly. – Volodymyr Balytskyy Feb 07 '16 at 20:58
  • I was suggesting something like, for each row (being `tr`, `li`, etc) add **data-id=$row_id** (from `php` side), and then on click read it back as `var client_id = $(this).data('id');`. Maybe I am not giving a good advise.. – Andrew Brown Feb 07 '16 at 21:02
  • that is a smart advice, really. I will even probably implement this. But from rails side, I am not sure how to pass that id. Getting the ID is easy, I have my way and yours way (thanks for that), but cant figure out how to pass it to erb thing. – Volodymyr Balytskyy Feb 07 '16 at 21:08
  • On `Ruby` side you got me completely, I don't know anything about it. Sorry. – Andrew Brown Feb 07 '16 at 21:10

3 Answers3

1

One solution is to do an ajax request to the backend fetching the partial in question.

Controller: app/controllers/clients_controller.rb

class ClientsController < ApplicationController
  #
  # GET /clients/:id/info
  #
  def info
    render partial: "client_info", locals: { client: params[:id] }
  end
end

Route: config/routes.rb

resources :clients do
  member do
    get :info
  end
end

View: app/clients/index.html.erb

<ul>
  <% @clients.each do |client| %>
    <li class="click" data-client-url="<%= info_client_path client.id %>">
      Click me <%= client.id %>
    </li>
  <% end %>
</ul>

<div class="side-panel">
  Waiting for data
</div>

Javascript: app/assets/javascripts/application.js

$(function(){
  $(".click").click(function(){
    $(".side-panel").load($(this).get("client-url"));
  });
});

It will load the url from the li element into the sidebar. You just have to adjust the code to your controller and view to get it to work.

Linus Oleander
  • 17,746
  • 15
  • 69
  • 102
1

You can get the clicked tr's first td's innerText var id = $(e.currentTarget).children().first()[0].innerText; but you need to take the event in the callback like $('#table_clients tbody').on('click', 'tr', function(e) {

Full js fiddle: https://jsfiddle.net/dattaproffs/788tkheh/

But i'm not sure how this will work, maybe i misunderstand but isn't the '<%= j render :partial => "client_info", :locals => {:client => 1} %>' rendered on the server?

  • When I load the page, the right table is not there. But when I click on any row (left table), the right table appear with no problem. Of course, only showing the client's info by ID=1. – Volodymyr Balytskyy Feb 07 '16 at 20:56
  • If the rest of your code works then my changes will fetch the id and pass it through. – Fredrik Emanuel Berggren Feb 07 '16 at 20:57
  • Ok. I viewed your fiddle example, and by inserting id variable inside erb thing, I get an error "Couldn't find Client with 'id'=id". It dose not pass the id as a number.. it passes a string: "id" instead. I am a little confused, so I cant explain why this not work. ;/ – Volodymyr Balytskyy Feb 07 '16 at 21:17
1

When your starting out with javascript avoid falling for the temptation of mixing javascript and whatever server side language you are using.

You end up with an overcomplicated mess and tons of confusion about what is happening where. Its a pretty common misstake and there is no reason to feel ashamed.

Using AJAX as suggested by Oleander is a good alternative - but another classic way to setup sliders or tabs is by using ancor links.

So first lets setup the table:

<table id="table_clients">
  <thead>
   <tr>
     <td>id</td>
     <td>name</td>
   </tr>
  </thead>
  <tbody>
    <%= clients.each do |client| %>
      <tr>
        <td>
          <%= client.id %>
        </td>
        <td>
          <%= content_tag :a, client.name, href: "client-<%= client.id %>" %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

Note the a <%= content_tag :a, client.name, href: "client-<%= client.id %>" %> part. This will render something like:

<a href="#client-1">Acme Corp.</a>

So lets create the details on the right side:

<% if clients.any %>
<ul id="client-details">
  <%= clients.each do |client| %>
  <li id="client-<%= client-id %>">
    <%= client.details # or whatever %>
  </li>
  <% end %>
</ul>
<% end %>

Even without javascript the browser will now jump down to the linked tab. Progress!

So now lets enhance the behavior with javascript:

// hide everything but the first
$('#client-details li').not(':first').hide();

$('#table_clients').on('click', 'tr,a', function() {
  var id, target;

  // we don't know if the user clicked a link or a row
  if (this.tagName === 'A') {
    id = this.href;
  } else {
    id = $(this).find('a').attr('href'); 
  }

  // since the href is equal to the id we can use it as a selector
  target = $(id);
  target.show(); // show the target
  $('#client-details li').not(target).hide(); // hide the others
  return false; // prevents the view from jumping if the user clicked a link
});

Note that this is a special keyword in javascript that changes meaning depending on the concept. When you attach a click handler it is the element that the user clicked. $(this) gives us a new jQuery object with that element as the context.

max
  • 96,212
  • 14
  • 104
  • 165
  • ah, I see. Hide everything, but show only one. That is what I call good thinking. I believe in each loops, instead of "client.id" should be "c.id". Couple of questions: in "this.tagName === 'A'", why triple equal sign ? and why 'A' in upper case ? – Volodymyr Balytskyy Feb 07 '16 at 21:45
  • `===` is the the javascript strict equals. It means only if `"A"` is string that equals "A". Otherwise javascript does some pretty crazy typecasting. In this case it would not really matter but its a good practice to use `===` – max Feb 07 '16 at 21:47
  • 1
    Why 'A' in upper case? Because the `tagName` property for elements in the DOM is in uppercase. Don't ask me why. I think its because writing HTML in ALLCAPS was cool in the 90's. – max Feb 07 '16 at 21:48
  • http://stackoverflow.com/questions/359494/does-it-matter-which-equals-operator-vs-i-use-in-javascript-comparisons – max Feb 07 '16 at 21:51
  • "client.id" should be "c.id" - yeah I slipped up there. edited – max Feb 07 '16 at 21:53
  • Ok. in `"client-<%= client.id %>"` should be `"client-#{ client.id }"`. In jQuery, to select something by id you need this sign '#'. So, I had to change to this: `id = "#"+$(this).find('a').attr('href');` Now, my client's info shows every time I click on the row. Your solution was simple and clever, but with erros. Nevertheless, your solution helped. I will mark it as correct and post tomorrow my full solution. Thanks ! – Volodymyr Balytskyy Feb 07 '16 at 22:20
  • Yeah, I should have tested it through but the import part was the basic concepts. – max Feb 07 '16 at 22:34