14

I have a helper method in my dashboard_helper.rb that looks like this:

  def show_number_of_comments(node)
    if node.comments_count == 1
      "#{node.comments_count} Comment"
    else
      "#{node.comments_count} Comments"
    end
  end

In my regular dashboard#index view, I call it like this:

<h4 class="card-comments-title"><%= show_number_of_comments(node) %></h4>

But I would like to update that rendered element via AJAX whenever a new comment is added, so in my comment#create.js.erb, I would like to reference that helper method but when I try this, it doesn't work:

$('#<%= @card_id %> .card-comments-title').html('<%= show_number_of_comments(@node) %>');

But when I do this, it works:

$('#<%= @card_id %> .card-comments-title').html('<%= @comment_count %> Comments');

The issue with the latter is that it doesn't handle pluralization.

What's the best way to approach this?

Edit 1

When I say it doesn't work, this is what I mean:

NoMethodError at /nodes/5/comments
==================================

> undefined method `show_number_of_comments' for #<#<Class:0x007fbd3715e5b8>:0x007fbd3715d4d8>

app/views/comments/create.js.erb, line 5
----------------------------------------

Also, the @node object is declared in my Comments#Create like this:

  def create
    @node = Node.find(params[:node_id])
    @comment = current_user.comments.new(comment_params)
    @comment.node = @node
    @card_id = params[:card_id]
    @comment_count = @node.comments_count + 1
    current_user.events.create(action: "commented", eventable: @comment)

    binding.pry

    respond_to do |format|
      if @comment.save and @node.save
        format.js
      else
        format.js
      end
    end
  end

When I halt execution via binding.pry as above, and I try to do @node, I get the value I expect:

[1] pry(#<CommentsController>)> @node
=> #<Node id: 5, name: "Reverse Crunches", family_tree_id: 1, user_id: 1, media_id: 5, media_type: "Video", created_at: "2015-07-25 05:49:51", updated_at: "2015-07-25 21:05:34", circa: nil, is_comment: nil, cached_votes_total: 0, cached_votes_score: 0, cached_votes_up: 0, cached_votes_down: 0, cached_weighted_score: 0, cached_weighted_total: 0, cached_weighted_average: 0.0, cached_user_tag_list: nil, cached_num_user_tags: 0, cached_tagged_user_names: [], comments_count: 3>

Edit 2

Sometimes it just fails. It doesn't output any error to either the console or my server log, it just replaces the .card-comments-title with a blank value.

marcamillion
  • 32,933
  • 55
  • 189
  • 380

6 Answers6

5

So I figured out what I wanted to do in my specific case, i.e. pluralize comment count within my JS ERB, but I haven't figured out how to use a helper_method from within my JS ERB - so this answer doesn't really answer this question.

However, I am documenting this here in case someone else has a similar issue and my solution helps them.

In my JS ERB, all I did was used the Rails method pluralize. I completely forgot about it until I typed up this question and it works like a charm.

Here is the code snippet for my create.js.erb:

$('#<%= @card_id %> .card-comments-title').html('<%= pluralize(@comment_count, "Comment") %>');

Works like a charm, but this still doesn't answer my question about referencing/using helper methods here.

marcamillion
  • 32,933
  • 55
  • 189
  • 380
  • 2
    im not sure if this is answered or not. just want to answer because now im curious as my helpers works for me in js.erb. i think your @node must not be available in your create.js.erb file that is why it is not working for you. even if you call pluralize, pluralize is still a helper method which is being called. if your '@node' is not present node.comments_count must be crashing. – Athar Jul 19 '15 at 17:18
  • 1
    Yep, helpers can most definitely be called in erb, does not matter if it is a `js` or `html`. I think the same as @Athar : `@node` was most likely nil. – nathanvda Jul 25 '15 at 17:55
  • @Athar I believe you may have the right answer. Write it up as an answer and I will accept it. You are right because `pluralize` does work (which is a Rails helper). – marcamillion Jul 26 '15 at 01:07
  • @Athar So I tested your idea, and it turns out that `@node` is actually being set right. I updated the question with more debugging details. – marcamillion Jul 26 '15 at 01:34
  • well as the issue persist i'm not posting answer. the second scenario which i think of is that it is unable to call the dashboard_helper methods from comments#create.js.erb which should not be possible though and i'm unfortunately unable to replicate this. if i were you, i will try to move this method to `comments_helper` or `application_helper` first and call, to confirm that the issue is with method or helper file.? if method works from there then the issue is with the dashboard helper file.. where is this file present.? `app/helpers/dashboard_helper` .? – Athar Jul 26 '15 at 07:44
5

You can also try to avoid using backend if no localisation required:

var comentsNumber = function(num){
  return (num == 0 || num > 1) ? num + " Comments" : num + " Comment"
};
var domObject = $('#<%= @card_id %> .card-comments-title');

Basically you can easily update an object via the AJAX call and trigger content to reload:

var htmlContent = comentsNumber(<%= @comment_count %>);
domObject.html(htmlContent);
Anatoly
  • 15,298
  • 5
  • 53
  • 77
3

A possible better solution to display singular/plural text would be via i18n. You could try something like following:

# config/locales/en.yml

en:
  comments:
    zero: No comments
    one: 1 comment
    other: %{count} comments

Then in your view, you'd use it as:

$('#<%= @card_id %> .card-comments-title').html("<%= t('comments', count: @node.comments_count) %>");

On the problem with helpers not working: Given that you have dashboard_helper.rb in app/helpers/ directory, your helper should work. With the problem you've described where the following line works:

$('#<%= @card_id %> .card-comments-title').html('<%= @comment_count %> Comments');

and the following doesn't:

$('#<%= @card_id %> .card-comments-title').html('<%= show_number_of_comments(@node) %>');

You might find a better answer if you had the output of show_number_of_comments(@node) perhaps using console.log. It is likely that this output needs to be escaped using escape_javascript. So, although not a concrete solution, I'd suggest you to try:

$('#<%= @card_id %> .card-comments-title').html('<%= j show_number_of_comments(@node) %>');
vee
  • 38,255
  • 7
  • 74
  • 78
  • I tried escaping it, with `escape_javascript`, but no dice. – marcamillion Jul 26 '15 at 01:06
  • Also, the better way to achieve this is just using the Rails helper method `pluralize`, which uses I18n I believe. When I try doing `console.log("@Node is: <%= j show_number_of_comments(@node) %>");` that prints out: `@Node is: 8 Comments` where `8` is the number of comments BEFORE I submitted this last one, so in theory that should be spitting out `...9 Comments` rather than `8`. – marcamillion Jul 26 '15 at 01:58
  • i18n added `pluralize` has not been released yet! It's only on the [master branch](https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/text_helper.rb#L228), not on [4.2 stable](https://github.com/rails/rails/blob/4-2-stable/actionview/lib/action_view/helpers/text_helper.rb#L215). The next theoritical behavior you expect is not happening becuase you've already queried `@node` earlier. If you reassign `@node` with the query, you will get `9 Comments`. – vee Jul 26 '15 at 03:51
  • 1
    After reading your edits, I would suggest that you build `comment` via `@node` and not through `current_user`. So, `@node.comments.new(comment_params)` and assign `current_user` to it. This would allow you to use `@node.comments.length` instead of the extra variable `@comment_count` as well. – vee Jul 26 '15 at 04:02
  • Remember that I have to increment the comments count anyway I take it, and `@node.comments_count` is coming directly from a column, not a ruby method - so I am not sure this suggestion would be more efficient. i.e. the question is, is `@node.comments.length` more efficient than `@node.comment_count` when `comment_count` is a `counter_cache`? I doubt it. – marcamillion Jul 26 '15 at 05:37
  • Ahh, I don't think you'd need to increment the comments count if you have node `has_many :comments`. For performance reasons you may have different implementation. This would probably be better tested with a benchmark test. My suggestion was more to avoid the extra method `show_number_of_comments` when if you use `counter_cache` you have it given by AR! – vee Jul 26 '15 at 05:43
1

Edit: For Rails 3.1+, the following is only true if

config.action_controller.include_all_helpers = false

Your DashboardHelper is included in the DashboardController. Your .js.erb seems to belong to something like CommentsController.

You will either have to move your helper method to your ApplictionController or include the helper using helper DashboardHelper in the controller you want to use it in.

amiuhle
  • 2,673
  • 1
  • 19
  • 28
  • What doesn't work? The `config.action_controller...` or the fact that the code in `DashboardHelper` is only included in the `DashboardController`? I feel like you may be onto something here. – marcamillion Jul 26 '15 at 01:36
  • According to the docs, in Rails 3.1 and later, all helpers are included by default. But just out of curiosity, what happens if you duplicate the helper in `CommentsController`? – amiuhle Jul 26 '15 at 10:37
0

You could try pluralize:

$('#<%= @card_id %> .card-comments-title').html('<%= pluralize(@comment_count, 'Comment') %>');

It should work in both places.

I missed your own followup.

I'd suggest moving helpers that you need in multiple contexts to the application_helper.rb file. Maybe not the best practice but it would do the job.

chad_
  • 3,749
  • 2
  • 22
  • 22
0

Try using j short for escape_javascript.

$('#<%= @card_id %> .card-comments-title').html('<%= j show_number_of_comments(@node) %>');
daslicious
  • 1,535
  • 10
  • 18