neanderslob
neanderslob

Reputation: 2703

Keep changes on reload if validation fails

I'm working with validations in rails, stuff like:

validates_presence_of :some_field

I've noticed that if the validation fails, all changes are overwritten with existing values from the database. This makes some sense, as the page is basically being reloaded (as I gather from my development log), however this increases the risk of user error/frustration, as a single error in one field will require the hapless fellow to re-enter the changes he made to all fields.

My question: How can I get rails to reload the data that was just submitted if validation fails? That way, the user can correct the mistake without needing to re-enter the rest of his revisions.

Thanks for any advice.

Edit: My update method, as requested, is as follows:

def update
    @incorporation = Incorporation.find(params[:id])
    @company = @incorporation.company
    begin
        @company.name="#{params[:company][:names_attributes].values.first["name_string"]} #{params[:company][:names_attributes].values.first["suffix"]}"
    rescue NoMethodError
        @company.name="Company #{@company.id} (Untitled)"
    end
    if @company.update(company_params)
        redirect_to incorporations_index_path
    else
        redirect_to edit_incorporation_path(@incorporation)
    end
end

Full disclosure regarding my controller: the above update is from my incorporations_controller even though I'm updating my Company model. Company has_one :incorporation. I did this because, in the larger context of my app, it made my associations much cleaner.

Upvotes: 3

Views: 1746

Answers (2)

Richard Peck
Richard Peck

Reputation: 76784

To add to the correct answer, you can clean up your code quite a bit:

def update
    @incorporation = Incorporation.find params[:id]

    respond_to do |format|
      if @incorporation.update company_params
        format.html { redirect_to({:action => "index"})}
      else
        format.html { render :edit }
        format.json { render json: @incorporation.errors, status: :unprocessable_entity }
      end
    end
end

If you're using accepts_nested_attributes_for, you definitely should not hack the associated objects on the front-end.

You should look up fat model, skinny controller (let the model do the work):

#app/models/company.rb
class Company < ActiveRecord::Base
  before_update :set_name
  attr_accessor :name_string, :name_suffix

  private

  def set_name
    if name_string && name_suffix
      self[:name] = "#{name_string} #{name_suffix}"
    else
      self[:name] = "Company #{id} (Untitled)"
    end
  end
end

This will allow you to populate the name of the `company. To edit your nested/associated objects directly is an antipattern; a hack which will later come back to haunt you.


The key from the answer is: render :edit

Rendering the edit view means that your current @company / @incorporation data is maintained.

Redirecting will invoke a new instance of the controller, overriding the @incorporation, hence what you see on your front-end.

Upvotes: 1

Lymuel
Lymuel

Reputation: 574

Update your controller to this

def update
    @incorporation = Incorporation.find(params[:id])
    @company = @incorporation.company
    begin
        @company.name="#{params[:company][:names_attributes].values.first["name_string"]} #{params[:company][:names_attributes].values.first["suffix"]}"
    rescue NoMethodError
        @company.name="Company #{@company.id} (Untitled)"
    end
    respond_to do |format|
        if @company.update(company_params)
            format.html { redirect_to({:action => "index"})}
        else
            format.html{render :edit}
            format.json { render json: @incorporation.errors, status: :unprocessable_entity }
        end
    end
end

Upvotes: 2

Related Questions