Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 ajax load content in bootstrap tab

I have the same problem as described in this post.

However, I still find it hard to comprehend the solution.

The view:

/ Nav tabs
%ul.nav.nav-tabs
  %li.active
    =link_to "Tab1", "#tab1", html_options = { "data-toggle" => "tab" }
  %li
    =link_to "Tab2", "#tab2", html_options = { "data-toggle" => "tab" }
  %li
    =link_to "Tab3", "#tab3", html_options = { "data-toggle" => "tab" }

/ Tab panes
.tab-content
  .tab-pane.active#tab1
    %p Fetch content
  .tab-pane.active#tab2
    %ul.nav.nav-pills
      %li.active= link_to 'Overview', "#overview", html_options = { "data-toggle" => "tab"}
      %li= link_to 'Details', "#details", html_options = { "data-toggle" => "tab"}
      .tab-content
        .tab-pane.active#overview
          %p Fetch overview
        .tab-pane.active#details
          %p Fetch details
  .tab-pane.active#tab3
    %p Fetch content

The controller:

def show
  @data_tab1 = User.admin
  @data_tab2 = User.member
  @data_tab3 = User.moderator
end

I don't want those three sets of data loaded in each request. Such that when clicking the second tab and @data_tab2 loads.

I found this code that might be useful:

$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
  var target = $(e.target).attr("href") // activated tab
  if ($(target).is(':empty')) {
    $.ajax({
      type: "GET",
      url: "**WHAT SHOULD I PUT IN HERE**",
      error: function(data){
        alert("There was a problem");
      },
      success: function(data){
        $(target).html(data);
      }
  })
 }
})

SO, I want to know:

1. how to change the active state of each clicked tab

2. how to ajax load tab data

Ajax call with error.

[Error] TypeError: undefined is not a function (evaluating 'e.target.data("target")')
    (anonymous function) (1, line 3)
    dispatch (jquery.js, line 4642)
    handle (jquery.js, line 4310)
    trigger (jquery.js, line 4551)
    (anonymous function) (jquery.js, line 5261)
    each (jquery.js, line 384)
    each (jquery.js, line 137)
    trigger (jquery.js, line 5260)
    (anonymous function) (bootstrap.js, line 1048)
    next (bootstrap.js, line 1091)
    activate (bootstrap.js, line 1098)
    show (bootstrap.js, line 1043)
    (anonymous function) (bootstrap.js, line 1113)
    each (jquery.js, line 384)
    each (jquery.js, line 137)
    Plugin (bootstrap.js, line 1108)
    clickHandler (bootstrap.js, line 1137)
    dispatch (jquery.js, line 4642)
    handle (jquery.js, line 4310)

And I would like to give each tab-pane a partial, how do I dynamically change the partial name in the controller? E.g.

/ Tab panes
.tab-content
  .tab-pane.active#tab1
    = render "tab1"
  .tab-pane.active#tab2
    = render "tab2"
  .tab-pane.active#tab3
    = render "tab3"

Should I use different instance variable?

def show
  @data_tab = User.admin
  @data_tab = User.member if params[:tab] == "member"
  @data_tab = User.moderator if params[:tab] == "moderator"
end

OR

def show
  @data_tab1 = User.admin
  @data_tab2 = User.member if params[:tab] == "member"
  @data_tab3 = User.moderator if params[:tab] == "moderator"
end

Update:

It keeps rendering the same set of data as shown in the vid clip.

http://tinypic.com/r/28k5302/8

Please advise.

like image 568
The questioner Avatar asked Feb 12 '23 08:02

The questioner


1 Answers

  1. how to change the active state of each clicked tab

If you look at bootstrap tabs markup, it says:

You can activate a tab or pill navigation without writing any JavaScript by simply specifying data-toggle="tab" or data-toggle="pill" on an element

So your markup needs to be like this:

/ Nav tabs
%ul.nav.nav-tabs
  %li.active
    =link_to "Tab1", "#tab1", html_options = { "data-toggle" => "tab" }
  %li
    =link_to "Tab2", "#tab2", html_options = { "data-toggle" => "tab" }
  %li
    =link_to "Tab3", "#tab3", html_options = { "data-toggle" => "tab" }

/ Tab panes
.tab-content
  .tab-pane.active#tab1
    %p Fetch content
  .tab-pane#tab2
    %ul.nav.nav-pills
      %li.active
        = link_to 'Overview', "#overview", html_options = { "data-toggle" => "tab"}
      %li
        = link_to 'Details', "#details", html_options = { "data-toggle" => "tab"}
      .tab-content
        .tab-pane.active#overview
          %p Fetch overview
        .tab-pane#details
          %p Fetch details
  .tab-pane#tab3
    %p Fetch content

Note: You need to apply active class only on the tab you want to show.

  1. how to ajax load tab data

a. Create a custom route to which you want your ajax request to go to or you can just use the show action for it.

b. We need to add some data attribute so that we know which tab is being clicked. We can then target it in backend.

/ Nav tabs
%ul.nav.nav-tabs
  %li.active
    =link_to "Tab1", "#tab1", data: {toggle: "tab", target: "admin", href: "#{your_show_action_path}
  %li
    =link_to "Tab2", "#tab2", data: {toggle: "tab", target: "member", href: "#{your_show_action_path}
  %li
    =link_to "Tab3", "#tab3", data: {toggle: "tab", target: "moderator", href: "#{your_show_action_path}
/ Tab panes
.tab-content
  .tab-pane.active#tab1
    %p // this will be activated by default so you can load default content here
  .tab-pane#tab2
    %ul.nav.nav-pills
      %li.active
        = link_to 'Overview', "#overview", html_options = { "data-toggle" => "tab"}
      %li
        = link_to 'Details', "#details", html_options = { "data-toggle" => "tab"}
      .tab-content
        .tab-pane.active#overview
          %p Fetch overview
        .tab-pane#details
          %p Fetch details
  .tab-pane#tab3
    %p Fetch content

c. Use JS to fire your ajax request

$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
  var target = $(this).data("target");
  var href = $(this).data("href");
  $.ajax({
    type: "GET",
    url: href,
    data: {tab: target},
    dataType: "script"
  });
})

d. Now you can load tabs content depending on the tab variable send by ajax request.

def show
  @data_tab = User.admin
  @data_tab = User.member if params[:tab] == "member"
  @data_tab = User.moderator if params[:tab] == "moderator"
end

#show.js.erb
$(".tab-pane.active").html("your content here");

And I would like to give each tab-pane a partial, how do I dynamically change the partial name in the controller? Should I use different instance variable?

If content is same then you can use the same partial and same instance variable but if it's different then you can check params for the value of target variable and then accordingly render your partial in js.erb

Update:

1: How to dynamically change .tab-pane.active, as I would set member and moderator empty partials such that when I click "member" tab, the partial will be filled with ajax data and add an "active" class to that pane?

You don't need to add active class here because if you have bootstrap js in your app then it'll automatically set active class on the tab you'll click, checkout bootstrap docs

2:How can js.erb respond to different instance variables and render different partials?

When you click on a tab we get the data-target attribute and send it as params in the ajax request and then in js.erb file we are checking for these param values to render partials.

3.How to prevent previously clicked tab to send an ajax request?

Not sure what you mean here but previously clicked tab won't and shouldn't send ajax request. If you are clicking on 2 tabs quickly then that's normal to send 2 ajax requests as in the end it'll always show up the active tab.

like image 145
Mandeep Avatar answered Feb 13 '23 23:02

Mandeep