Re-initialising page after validation fails

Hi,

I have a controller and two of the actions in it are as follows:

  # GET /bids/new
  # GET /bids/new.xml
  def new
    @bid = Bid.new
    @auctionItems = AuctionItem.find(:all)

    respond_to do |format|
      format.html # new.html.erb
      format.xml { render :xml => @bid }
    end
  end

  # GET /bids/1/edit
  def edit
    @bid = Bid.find(params[:id])
  end

  # POST /bids
  # POST /bids.xml
  def create
    @bid = Bid.new(params[:bid])

    respond_to do |format|
      if @bid.save
        flash[:notice] = 'Bid was successfully created.'
        format.html { redirect_to(@bid) }
        format.xml { render :xml => @bid, :status => :created,
:location => @bid }
      else
        format.html { render :action => "new" }
        format.xml { render :xml => @bid.errors, :status =>
:unprocessable_entity }
      end
    end
  end

The new.html.erb contains this line:

<%= f.select(:auction_item_id, @auctionItems.collect {|i| [ i.title,
i.id ]

Which populates a select with the auctionItems fetched in the new
action.

If validation fails in the "create", I get an error something like:

NoMethodError in Bids#create

Showing app/views/bids/new.html.erb where line #8 raised:

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.collect

Extracted source (around line #8):

5:
6:
7: <p>
8: <%= f.select(:auction_item_id, @auctionItems.collect {|i| [ i.title,
i.id ] }, { :include_blank => true }) %>
9: </p>
10:
11: <p>

I am guessing this is because the auctionItems is not repopulated for
this request.

So, does this:

        format.html { render :action => "new" }

Mean it only displays new.html.erb, and not re-runs the "new" action?

What is the best way to ensure that @auctionItems gets repopulated. I
suppose I modify create like this:

  def create
    @bid = Bid.new(params[:bid])

    respond_to do |format|
      if @bid.save
        flash[:notice] = 'Bid was successfully created.'
        format.html { redirect_to(@bid) }
        format.xml { render :xml => @bid, :status => :created,
:location => @bid }
      else
        @auctionItems = AuctionItem.find(:all)
        format.html { render :action => "new" }
        format.xml { render :xml => @bid.errors, :status =>
:unprocessable_entity }
      end
    end
  end

But it doesn't seem the best practice. Is there a better way to do
this? I have seen some mention of "Helpers" - is this something they
could be used for?

Many thanks,
Andrew.

If validation fails in the "create", I get an error something like:

NoMethodError in Bids#create

Showing app/views/bids/new.html.erb where line #8 raised:

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.collect

Extracted source (around line #8):

5:
6:
7: <p>
8: <%= f.select(:auction_item_id, @auctionItems.collect {|i| [ i.title,
i.id ] }, { :include_blank => true }) %>
9: </p>
10:
11: <p>

I am guessing this is because the auctionItems is not repopulated for
this request.

So, does this:

    format\.html \{ render :action =&gt; &quot;new&quot; \}

Mean it only displays new.html.erb, and not re-runs the "new" action?

Yep - all that does is render the selected template. The idea is that
that the form will be redisplayed with the contents of @bid.

What is the best way to ensure that @auctionItems gets repopulated. I
suppose I modify create like this:

def create
@bid = Bid.new(params[:bid])

respond\_to do |format|
  if @bid\.save
    flash\[:notice\] = &#39;Bid was successfully created\.&#39;
    format\.html \{ redirect\_to\(@bid\) \}
    format\.xml  \{ render :xml =&gt; @bid, :status =&gt; :created,

:location => @bid }
else
@auctionItems = AuctionItem.find(:all)
format.html { render :action => "new" }
format.xml { render :xml => @bid.errors, :status =>
:unprocessable_entity }
end
end
end

But it doesn't seem the best practice. Is there a better way to do
this? I have seen some mention of "Helpers" - is this something they
could be used for?

This is a very typical use of helpers - you could put this code into
app/helpers/bid_helper.rb:

def auction_select(form)
  form.select(:auction_item_id, AuctionItem.all.collect {|i|
[ i.title,
i.id ] })
end

and then call it from your views as:

<%= auction_select(f) %>

It tidies things up, and then when you need to select an auction
someplace else, it can be reused.

BTW, mixed-case variable names are nonstandard in Rails - normally,
you use underscored names (@auction_items rather than @auctionItems).
No real difference, but it looks odd to experienced programmers.

--Matt Jones

Thanks Matt - that sounds like the solution I am after. Also thanks for
the advice re variable names. I am coming to Ruby from Java and have a
bit to learn in that regard. :slight_smile:

Matt Jones wrote: