user4932805
user4932805

Reputation:

Models Logic in View--Rails

My Article model has_many instances of my Comment model, which has a text attribute.

However, in my view, when I call through iteration article.comments.last.text I get undefined method error.

I must say that when I call it through the console, it does return the text attribute.

The log's only relevant response to this error is:

ActionView::Template::Error (undefined method `text' for nil:NilClass):

View code:

- @articles.each do |article|
    .article
        %comment= article.comments.last.text 

Upvotes: 1

Views: 54

Answers (4)

mpospelov
mpospelov

Reputation: 1549

First thing to mention is that you have N+1 problem here. You are querying your database on each article to get all it's comments. It's may slowdown your system.

I suggest the next approach for this solution.

  1. Define new relation in Article

    class Article < ActiveRecord::Base
        has_one :last_comment, -> { where(created_at: :desc) },  class_name: 'Article'
    end
    
  2. Load this relation in your controller

    def your_action
      # you can continue querying as I show you with 'where' and 'your_scope', the 'includes' is a must to 
      @articles = Article.includes(:last_comment).where(...).your_scope 
      ...
    end
    
  3. Then in your view just use the next code

    - @articles.each do |article|
      .article
         - if article.last_comment.present?
           %comment= article.last_comment.text
    

Upvotes: 1

Richard Peck
Richard Peck

Reputation: 76774

To add to the accepted answer, the problem is defined with the error:

--

undefined method `text' for nil:NilClass

It means you're calling text on a class / variable / data-set which isn't populated (nil).

As mentioned, the way to do this is to evaluate whether an article has any .comments. Whilst .try(:x) is the best way to do it, the more verbose way is to use conditional logic:

%comment= article.comments.last.text if article.comments.any?

--

it does return the text attribute

Maybe the comment exists, but it isn't associated to article.

Calling article.comments only calls the comments associated to article (through their respective foreign keys). If the comment is not associated to article, it won't appear in the collection.

Thus, if you're checking whether text exists for a comment, you also need to make sure the comment is associated with article. A simple way to do this is through the Rails Console:

$ rails c
$ article = Article.first
$ comment = Comment.first
$ article.comments << comment

Upvotes: 1

Bijendra
Bijendra

Reputation: 10053

You should do some defensive coding when trying something like this. article.comments.last.text. There will always be a possibility when article.comments is blank. It returns an empty array [].So when you execute something like .last.text. It will break the code throwing error.

You can check something like article.comments.present? and then access the last comment.

Upvotes: 1

Dheeresha
Dheeresha

Reputation: 797

Update your view code to following. I hope it works for you.

- @articles.each do |article|
    .article
        %comment= article.comments.last.try(:text)

Upvotes: 3

Related Questions