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: