Search code examples
ruby-on-railsrubyruby-on-rails-5.2fields-for

nested attributes prefilled child data with empty parent


I am stuck in a situation where I need to give prefilled data for the fields_for with an empty form. Let me explain with an example

Association

class User < ApplicationRecord
  has_one :account, foreign_key: :user_id

  accepts_nested_attributes_for :account
end

Class Account
  belongs_to :user
end

Now I have a form on the dashboard index page.

<%= form_for @account, :url => account_path, html: { class: "abc" } do |f| %>
  <%= f.hidden_field :user_id, :value => current_user.id %>
  <%= f.fields_for :user do |user| %>
    <div class="medium-4 columns">
      <label>First Name</label>
      <%= user.text_field :first_name , class: 'xyz', data: {input: 'someinput'} %>
    </div>
    <div class="medium-4 columns">
      <label><b>Last Name<span class="invalid_message"> This field is required</span></b></label>
      <%= user.text_field :last_name, class: 'xyz', data: {input: 'someinput'} %>
    </div>
  <% end %>

  <div class="medium-4 medium-offset-2 columns">
    <label>Phone Number</label>
    <%= f.text_field :phone_number, class: 'xyz', data: {input: 'someinput'} %> 
  </div>
<% end %>

Controller

 class AccountsController < ApplicationController

      def create
        @account = Account.new(account_params)
        if @account.save
          render json: {status: 'successfull'}
        else
          render json: {error: "#{@account.errors.full_messages}"}, status: 400
        end
      end

      private
      def account_params
        params.require(:account).permit(:phone_number, :user_id, user_attributes: [:first_name, :last_name])
      end
    end

    class DashboardController < ApplicationController

      def index
        ##I will always have current_user. For the account it will be blank for the first time
        if current_user.account.blank?
          @account = Account.new
        else
          @account = current_user.account
        end
      end
    end

Parameters

{"utf8"=>"✓", "authenticity_token"=>"asdasdadadadadsadadadadadQpy0tA82asdaalAgJsUcNk1i/kGETfZqnuQA==", "account"=>{"user_id"=>"123", "user"=>{"first_name"=>"sd", "last_name"=>"ad"}, "phone_number"=>"1212"}}

There are two issues
1) First and Last name are not getting prefilled
2) Params are not going correctly. It should be account_attributes instead of account in the parameter.

Note: In my case, @account will be blank for the first time but still user object(which is the current user) has first_name and last_name already wHich I need to prefill in the fields_for. Also, I need a way to update the first and last name

Can anyone tell me where I am doing wrong


Solution

  • Finally, I found the solution. I was doing a couple of things wrong

    1) In form_for it should be @user instead of @account
    2) Then in the controller this form will always send it to update action instead of create. The reason is I will always have current_user so rails will automatically check if the object(current_user) exists so instead of sending to create it will send to update action
    3) Lastly, when working with one to one assocation we need to load the parent object and build the child object.

    <%= form_for @user, :url => user_path, html: { class: "abc" } do |f| %>
        <div class="medium-4 columns">
          <label>First Name</label>
          <%= f.text_field :first_name , class: 'xyz', data: {input: 'someinput'} %>
        </div>
        <div class="medium-4 columns">
          <label><b>Last Name<span class="invalid_message"> This field is required</span></b></label>
          <%= f.text_field :last_name, class: 'xyz', data: {input: 'someinput'} %>
        </div>
      <%= f.fields_for :account do |account_info| %>
        <div class="medium-4 medium-offset-2 columns">
          <label>Phone Number</label>
        <%= account_info.text_field :phone_number, class: 'xyz', data: {input: 'someinput'} %> 
        </div> 
      <% end %>
    <% end %>
    
    
    class UsersController < ApplicationController
    
          def update
            if current_user.update(account_params)
              render json: {status: 'successfull'}
            else
              render json: {error: "#{@account.errors.full_messages}"}, status: 400
            end
          end
    
          private
          def account_params
            params.require(:account).permit(:first_name, :last_name, account_attributes: [:phone_number])
          end
        end
    
        class DashboardController < ApplicationController
    
          def index
            ##I will always have current_user. For the account it will be blank for the first time
            if current_user.account.blank?
               @account = current_user
               @account.build_account
            end
          end
        end