简体   繁体   中英

Rails: including JS only if a certain condition is met

I want to include a piece of JS on every page, but only if session is set a certain way.

I've got a sidebar with a timer button in it. The sidebar ( layouts/_sidebar.html.erb ) is included on every page's layout once the user logs in.

Click the timer button once, and the timer starts. LogsController#start_from_button records the click event time in session[:timer_start] .

Click the button again, and LogsController#finish_from_button records the second click time in session[:timer_finish] . Then it renders a form for a new Log , with START and FINISH fields pre-filled with the session data. Finally, it clears the above session values.

Unfortunately, upon page refresh, the timer resets completely. I don't want that to happen. I want it to keep going until the user clicks the button a second time.

My first fix was checking the timer status in ApplicationController before every action. Then, if session[:timer_start] isn't nil , I might include the requisite JS to make the timer continue from session[:timer_start] . But the only way I know to do that is pretty ugly:

# application_controller.rb

before_action :check_timer_status
...
def check_timer_status
  if session[:timer_start]
    # include page-specific JS
  end
end

I also would like the templates to display as normal.

A) If this seems hacky, what's a better way of accomplishing this, or

B) If it's not too hacky, how might I be able to include the page-specific JS from that controller action?

To answer you question - you can use content_for(:include_js){ @include_js} in view and

<% yield(:include_js) %> 
  <% javascript_include_tag ..... %>
<% end %>

in your main layout head section. Where is @include_js - boolean of your specific condition.

But I believe you shouldn't exclude js. Because js loads once and then browser uses cached copy. Use cookies to keep timer_start instead and read it by your js script.

Here is how you can do it.

  1. Get the jquery-stopwatch library.

  2. Put it in your public/assets/javascripts directory

  3. Add it to your application layout by putting :

    <%= javascript_include_tag 'jquery.stopwatch' %>

  4. You have to store the time your timer is stopped in your database. Just fire an event to record the Time.now somewhere in the database.

  5. Make a helper method in your application_helper. You are trying to record the milliseconds elapsed since the timer had started. You can send the milliseconds elapsed to the database by putting the button in a form or through AJAX. It's up to you.

    def time_elapsed @time_now = Time.now @time_timer_stopped = #this is where you query your db and pull out the time the timer has been stopped. return (@time_now - @time_timer_stopped)*1000 #returns the miliseconds passed after you stopped the time end

    1. Put this script somewhere where the time_elapsed method is reachable

    $('#someDiv').stopwatch({startTime: <%= time_elapsed %>}).stopwatch('start') </script>

Voila, you have your stopwatch.

I ended up using cookies, and checked for them exclusively on the client side. Cookies were preferable to localStorage in my case because down the development road I may want to check cookie values server-side, and localStorage won't let me do that.

// Every page change, check if user has previously
// clicked the timelog start button but not the 
// timelog finish button.
// If so, start the timer from when the user first
// clicked the button.
if(isCookie('start_time') || $start_time) {
    timerRunningDisplay();
    activateTimer();            
}

// When user clicks timelog start button,
// start timer and save the click time in cookies.
  $(document).on("click", '#start-timelog', function(e) {
    e.preventDefault();
    timerRunningDisplay();
    activateTimer();
    setCookie('start_time', $start_time, 7);
    });

// Sets timer running, updated every second.
// Start is when user first clicked 'start' button.
function activateTimer() {
    if(isCookie('start_time')) {
        x.continueTimer();
    }
    else {
        x.start();
    }
    clocktimer = setInterval(update, 1000);
}

The FINISH button POSTS to the finish_from_button controller action using AJAX:

  def finish_from_button
    query_string = { 
      start_time: params[:start_time], 
      finish_time: params[:finish_time] 
    }.to_query
    render js: %(window.location.href='#{new_timelog_path}?#{query_string}')
  end

And the #new action is rendered.

  def new
    @timelog = Timelog.new
    @timelog.start_time = params[:start_time]
    @timelog.end_time   = params[:finish_time]
  end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM