Add a JavaScript display to the Home page to count

2019-03-12 21:15发布

问题:

This exercise was a bit tricky. Figured I'd post my solution to see if anyone did it differently or if there's anyone who knows a better way.

I'm not sure on best practices for using the Asset Pipline .. for example, the correct order to put things in the application.js manifest file, or when to put things in lib versus app. I just put the following in lib to try getting it to work.


From Michael Hartl's Rails Tutorial 2nd ed Chapter 10, Exercise 7:

(challenging) Add a JavaScript display to the Home page to count down from 140 characters.

First, I read this post about jQuery Twitter-like countdowns, which provided the code to do it.

Next, I updated app/views/shared/_micropost_form.html.erb to look like this:

<%= form_for(@micropost) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <div class="field">
        <%= f.text_area :content, placeholder: "Compose new micropost..." %>
    </div>
    <%= f.submit "Post", class: "btn btn-large btn-primary" %>
    <span class="countdown"></span>

<% end %>

Then, I created a javascripts directory in lib and added file

lib/assets/javascripts/microposts.js

function updateCountdown() {
    // 140 is the max message length
    var remaining = 140 - jQuery('#micropost_content').val().length;
    jQuery('.countdown').text(remaining + ' characters remaining');
}

jQuery(document).ready(function($) {
    updateCountdown();
    $('#micropost_content').change(updateCountdown);
    $('#micropost_content').keyup(updateCountdown);
});

Finally, I added a tiny bit of CSS

app/assets/stylesheets/custom.css.scss

/* micropost jQuery countdown */

.countdown {
  display: inline;
  padding-left: 30px;
  color: $grayLight;
}

Finally, add directive to the app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require microposts
//= require_tree .

The FINAL RESULT looks like this http://grab.by/dbC6


Question: Would it be wrong to put the manifest lines after //= require_tree .

For example, this works but I'm not sure what the difference would be, versus adding the line above tree .

 //= require jquery
 //= require jquery_ujs
 //= require bootstrap
 //= require_tree .
 //= require microposts

回答1:

I think my solution posted here in SO is different enough from yours now that I can humbly post it as an answer.



回答2:

You can simplify it using CoffeeScript:

/app/assets/javascripts/microposts.js.coffee

updateCountdown = ->
  remaining = 140 - jQuery("#micropost_content").val().length
  jQuery(".countdown").text remaining + " characters remaining"

jQuery ->
  updateCountdown()
  $("#micropost_content").change updateCountdown
  $("#micropost_content").keyup updateCountdown

And as mentioned by jonyamo, you don't need to touch the application.js as the //= require_tree . already does the trick.



回答3:

I don't know why, but that solution only worked for me using coffee script. I tried implementing it with javascript, but it somehow didn't display anything: nor the countdown, nor the fixed part of the text "characters remaining".

So here's a recap of what I did.

Step 1 : create a app/javascripts/microposts.js.coffee file

updateCountdown = ->
  remaining = 140 - jQuery("#micropost_content").val().length
  jQuery(".countdown").text remaining + " characters remaining"

jQuery ->
  updateCountdown()
  $("#micropost_content").change updateCountdown
  $("#micropost_content").keyup updateCountdown

NB: Being that its placed in the app/javascripts folder, I didn't need to update the application.js file.

Step 2 : update the _micropost_form.html.erb partial:

<%= form_for(@micropost) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <div class="field">
        <%= f.text_area :content, placeholder: "Compose new micropost..." %>
    </div>
    <%= f.submit "Post", class: "btn btn-large btn-primary" %>
    <span class="countdown"></span>
<% end %>

step 3: implement a bit of css to the custom_css.css.scss file

/* micropost jQuery countdown */

.countdown {
  display: inline;
  padding-left: 10px;
  color: $grayLight;
}

Step 4: enjoy the result and be happy that it all works out :)



回答4:

My microposts.js.coffee uses the jQuery .css method to change the color of the characters remaining based on the value of the variable remaining to more closely mirror the behavior of twitter

updateCountdown = -> 
  remaining = 140 - jQuery("#micropost_content").val().length
  jQuery(".countdown").text remaining + " characters remaining"
  jQuery(".countdown").css "color", (if (140 >= remaining >= 21) then "gray")
  jQuery(".countdown").css "color", (if (21 > remaining >= 11) then "black")
  jQuery(".countdown").css "color", (if (11 > remaining)  then "red")

jQuery ->
  updateCountdown()
  $("#micropost_content").change updateCountdown
  $("#micropost_content").keyup updateCountdown

Thanks to all who answered before.



回答5:

I used Brett's code to get me through this exercise in the Rails Tutorial, though I have a minor change. I had problems with the text disappearing from the .countdown element when I navigated to a different page, then back to Home. With some minor research and help from another answer on SO I realized that the cause was Turbolinks. My change to Brett's code, binding to page:change event instead of to ready, is below. I hope this helps some other folks.

function updateCountdown() {
  // 140 is the max message length
  var remaining = 140 - jQuery('#micropost_content').val().length;
  jQuery('.countdown').text(remaining + ' characters remaining');
}

function micropostChange() {
  $('#micropost_content').change(updateCountdown);
}

function micropostKeyup() {
  $('#micropost_content').keyup(updateCountdown);
}

jQuery(document).ready(function($) {
  updateCountdown();
  micropostChange();
  micropostKeyup();
  jQuery(document).on('page:change', function($) {
    updateCountdown();
    micropostChange();
    micropostKeyup();
  });
});


回答6:

Here's my coffeescript version based on Adriano's solution. This ignores whitespace, doesn't involve adding empty divs into the view, and also adds an error class once you get to minus numbers.

updateCountdown = ->
  text =  jQuery('#micropost_content').val()
  text = text.replace(/\s/g, '');   
  remaining = 140 - text.length
  jQuery('.countdown').text remaining + ' characters remaining'
  jQuery('.countdown').addClass 'alert alert-error' if remaining < 0
  jQuery('.countdown').removeClass 'alert alert-error' if remaining > 0

jQuery(document).ready ->
  jQuery('#new_micropost').append '<span class="countdown">140 characters remaining</span>'
  jQuery('#micropost_content').change updateCountdown
  jQuery('#micropost_content').keyup updateCountdown
  return


回答7:

In order to avoid minus in character count down I added this limit to _micropost_form.html.erb partial, so it stops you at 140 characters:

<%= f.text_area :content, maxlength:140, placeholder: "Compose new micropost..." %>