proofexak
proofexak

Reputation: 57

NoMethodError for nil after rendering view in controller

So I have this in my view _form.erb:

<div class="form-group">
    <%= f.label :start_hour %><br>
    <%= f.select :start_hour, @select_hours.map {|value| [value, value]} %>
</div>

And this in edit.erb:

<%= render 'form' %>

And this in my controller

    def edit
        @user = current_user
        @employee = @user.employee
        @hour = @employee.working_hours.find(params[:id])
        @select_hours = Array.new
        for i in 0..12
          @select_hours.push("#{07+i}:00")
          @select_hours.push("#{07+i}:30")
        end
    end

And then my update in my controller

def update
    @employee = current_user.employee
    @hour = @employee.working_hours.find(params[:id])
    if @hour.update(working_hour_params)
      redirect_to employee_working_hours_path(@employee)
    else
      render :edit
    end
end

And here's my problem:

When I click update AND have wrong start_hour (custom validation, works when creating not editing), so @hour will not update. It renders again this view but with error that there is no method map for nil (so for @select_hours).

So how can I fix this?

Upvotes: 2

Views: 106

Answers (1)

Sebasti&#225;n Palma
Sebasti&#225;n Palma

Reputation: 33420

You can use a callback in your controller and set up @select_hours for those two actions, this way if the update fails, the value will be present, but you don't have to assign the variable twice, like:

before_action :set_select_hours, only: %i[edit update]
before_action :set_employee,     only: %i[edit update]
before_action :set_hour,         only: %i[edit update]

def edit; end

def update
  if @hour.update(working_hour_params)
    redirect_to employee_working_hours_path(@employee)
  else
    render :edit
  end
end

private

def set_select_hours
  @select_hours = (0..12).flat_map do |index|
    ["#{07 + index}:00", "#{07 + index}:30"]
  end
end

def set_employee
  @employee = current_user.employee
end

def set_hour
  @hour = @employee.working_hours.find(params[:id])
end

I think @employee can also be setted within a before callback.

I've added flat map to create and fill an array starting from the range, it's the "same" as before, just you don't need to initialize the array, use the for loop, and push the content for it.

Upvotes: 1

Related Questions