blupt
blupt

Reputation: 13

Passing data via instance variable from the controller to the view, what am I doing wrong?

I'm trying to write my first custom controller action and am running into a NoMethodError.

The scenario: PostsController, with the standard 7 actions scaffolding generates.

I want the index page to show a single post instead of displaying a list of them. Each post has a "visible" checkbox. I created a named_scope called visible in the Post model with:

named_scope :visible, :conditions => ["visible =?", true]

and applied it along with .last to the index action in my controller:

def index
  @post = Post.visible.last
  (format stuff)
end

Edited my index view to remove the iterating over an array part so it just shows @post.title, etc. So now my index just displays the last record in the Posts table with True on visible.

I then want to create an all posts archive page, that shows all of them regardless of visibility, so I created a new controller action:

(edited for clarity)

def archives
  render :layout => 'posts'
  @posts = Post.find(:all)

  respond_to do |format|
    format.html
    format.xml { render :xml => @posts }
  end
end

created a new named route:

map.archives 'posts/archives', :controller => 'posts', :action => 'archives'

and a view named archives.html.erb underneath views/posts.

Archives.html.erb looks exactly like the standard index.html.erb template that scaffolding creates, with a

<% for post in @posts %>
  <tr>
    <td><%=h post.title %></td>
    ...
  </tr>
<% end %>

When I view this page in my browser, I get an error of

NoMethodError in Posts#archives

You have a nil object when you didn't expect it, you might have expected an isntance of Array, the error occurred while evaluating nil.each,

pointing to the line in the view (app/views/posts/archives.html.erb) that says

<% for post in @posts %>

so my understanding is that the data from the instance variable in the controller action archives isn't passing into the view, so instead of an array of posts, I've got nil. I'm looking for a reason why, and what it is I'm doing wrong.

(Of course, if I put

<% @posts = Post.find(:all) %>

before the

<% for post in @posts %>

in the view, then it works just fine, but I know this is not what I should do.

All help, links to tutorials to straighten me out, etc is much appreciated.

(I'm doubtful it matters with something this simple, but I'm using Rails 2.2.2 and Ruby 1.8.6.)

Upvotes: 0

Views: 1311

Answers (1)

A.Ali
A.Ali

Reputation: 749

This is correct:

  respond_to do |format|
    format.html { render :layout => 'posts' }
    format.xml { render :xml => @posts }
  end
end

@posts was not recognized by the view because it was instantiated after you rendered the view. 'render' should be the last executed line in an action.

Upvotes: 2

Related Questions