Search code examples
ruby-on-railstwitter-bootstrapasset-pipelineruby-on-rails-5bootstrap-tour

How do I trigger & configure Bootstrap Tour based on the actions the user is on?


I am using Bootstrap Tour and it is very simple to setup.

These are the instructions:

// Instance the tour
var tour = new Tour({
  steps: [
  {
    element: "#my-element",
    title: "Title of my step",
    content: "Content of my step"
  },
  {
    element: "#my-other-element",
    title: "Title of my step",
    content: "Content of my step"
  }
]});

// Initialize the tour
tour.init();

// Start the tour
tour.start();

In my case, I have a Profiles#Index, Profiles#Show and Dashboard#Index -- all of which have different elements that I want to give the tour on. So I need to specify different elements for all actions/views which are different.

I also only want to trigger the tour under the following conditions:

  • The first time the user logs on (can be determined by current_user.sign_in_count < 2).
  • Only when the user first logs in, and not when they refresh the page.

I am using Devise, FYI.

I am now putting the default JS in my app/assets/javascripts/profiles.js. Should I move it elsewhere to be able to achieve my above conditions?

Edit 1

Actually, I thought it would work normally but I can't even get it to work at all.

I am on my Profiles#Show page and the tour doesn't trigger. This is how it is setup:

This is my application.html.erb:

<%= stylesheet_link_tag "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.11.0/css/bootstrap-tour.min.css", 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.11.0/js/bootstrap-tour.min.js", 'application', 'data-turbolinks-track': 'reload' %>

This is my application.js:

$(document).on('turbolinks:load', function() {
  var tour = new Tour({
    backdrop: true,
    steps: [
    {
      element: "#ratings-controls",
      title: "Ratings Controls",
      content: "Here you can rate this recruit so you can easily remember how they performed."
    },
    {
      element: "div.action-buttons a#favorite-btn",
      title: "Favorite",
      content: "Here you can easily favorite a player"
    },
    {
      element: "a.my-favorites",
      title: "My Favorites",
      content: "After you favorite a player, they automagically get added to your list of favorites -- which you can easily view here."
    }
  ]});

  // Initialize the tour
  tour.init();

  // Start the tour
  tour.start();
});

I initially had this in my app/assets/javascripts/profiles.js but when it stopped working, I moved it to application.js just to make sure something else wasn't overriding it.

In my Profiles#Show, I have all of those 3 elements for sure, as you can see in the rendered HTML below:

<div id="ratings-controls" class="profile-data">
  <table class="table table-condensed">
      <tbody>
      <tr>
          <td>
              <p>
                <button type="button" class="btn btn-success m-r-sm slider-step-value" id="slider-step-value-speed-wacky-dip-st-george-s-college-bcdda202-c498-4baa-867d-200662fc785b" data-speed-value="0">0</button>
                Speed
              </p>
              <div id="slider-speed" class="slider"></div>
          </td>
          <td>
              <p>
                <button type="button" class="btn btn-info m-r-sm slider-step-value" id="slider-step-value-tackling-wacky-dip-st-george-s-college-bcdda202-c498-4baa-867d-200662fc785b" data-tackling-value="0">0</button>
                Tackling
              </p>
              <div id="slider-tackling" class="slider"></div>
          </td>
      </tr>
      <tr>
          <td>
              <p>
                <button type="button" class="btn btn-primary m-r-sm slider-step-value" id="slider-step-value-dribbling-wacky-dip-st-george-s-college-bcdda202-c498-4baa-867d-200662fc785b" data-dribbling-value="0">0</button>
                Dribbling
              </p>
              <div id="slider-dribbling" class="slider"></div>
          </td>
          <td>
              <p>
                <button type="button" class="btn btn-warning m-r-sm slider-step-value" id="slider-step-value-passing-wacky-dip-st-george-s-college-bcdda202-c498-4baa-867d-200662fc785b" data-passing-value="0">0</button>
                Passing
              </p>
              <div id="slider-passing" class="slider"></div>
          </td>
      </tr>
      </tbody>
  </table>
</div>

<div class="action-buttons">
  <a class="btn btn-xs btn-success" id="favorite-btn-29" data-remote="true" rel="nofollow" data-method="post" href="/profiles/wacky-dip-st-george-s-college-bcdda202-c498-4baa-867d-200662fc785b/favorite"><i class='fa fa-thumbs-up'></i> Favorite</a>
</div>

<a class="my-favorites" href="/profiles?filter=favorites">
  <i class="fa fa-list"></i> My Favorites
</a>

Also, the Bootstrap Tour CSS & JS are being included in my rendered HTML.

So the first question is...how do I even get this working? Then I can get to the others.

Edit 2

I added some debugging statements to my application.js and these are the results.

This is how my application.js looks now:

$(document).on('turbolinks:load', function() {
  console.log('Turblinks Has Loaded -- This is Before Tour is Initialized.')
  var tour = new Tour({
    backdrop: true,
    steps: [
    {
      element: "#ratings-controls",
      title: "Ratings Controls",
      content: "Here you can rate this recruit so you can easily remember how they performed."
    },
    {
      element: "div.action-buttons a#favorite-btn",
      title: "Favorite",
      content: "Here you can easily favorite a player"
    },
    {
      element: "a.my-favorites",
      title: "My Favorites",
      content: "After you favorite a player, they automagically get added to your list of favorites -- which you can easily view here."
    }
  ]});

  // Initialize the tour
  tour.init();
  console.log('This is after Tour has been initialized.')

  // Start the tour
  tour.start();
  console.log('This is after Tour has been started.')
});


Turblinks Has Loaded -- This is Before Tour is Initialized.
bootstrap-tour.min.js:22 Uncaught TypeError: Cannot read property 'extend' of undefined(…)

What could be causing this and how do I further debug it? The error seems to be within the bootstrap-tour.min.js file.


Solution

  • New Answer

    After reading the Bootstrap Tour docs it sounds like your issue could be the way Bootstrap tour saves a cookie in your local storage to keep track of the user's progress on the tour. In other words the reason the tour won't start is because Bootstrap tour looks in your browsers local storage and thinks you are already done. So to fix this you have three options, depending on your exact circumstances:

    1. Force Bootstrap tour to NOT use storage

      // Instance the tour
      var tour = new Tour({
        storage: false,
        steps: [ ...]
      ]});
      

    or

    1. Force the tour to start

      // Start the tour
      tour.start(true);
      

    or

    1. Force the tour to restart

      // Restart the tour
      tour.restart();
      

    Original Answer

    I'm making some assumptions based on the comments I provided above and some debugging, and it looks like your javascript files are not included in the correct sequence or manner. This is further complicated by the recent Rails 5 changes to turbolinks. In the rapid prototyping world of rails this is when I turn to a lightweight gem for asset inclusion. In your case I would recommend:

    getting rid of:

    application.html.erb

    <%= stylesheet_link_tag "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.11.0/css/bootstrap-tour.min.css", 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag "https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.11.0/js/bootstrap-tour.min.js", 'application', 'data-turbolinks-track': 'reload' %>
    

    and installing the bootstrap-tour-rails gem like so:

    Gemfile

    gem 'bootstrap-tour-rails'
    

    application.js

    //= require bootstrap-tour
    

    application.css

    *= require bootstrap-tour