Rails nested User Profile fields with Devise not retaining input - ruby-on-rails

I am trying to add forms fields to my Devise user registration view. This has been achieved and the validation runs when I submit the form. However, if I get any form errors, the input data does not re-populate the form field as per the original form although I can see the correct values in my little debugger I've added to the development views.
Here is my view:
<h2>Sign up</h2>
<% resource.build_profile %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :username %> <i>(this cannot be changed so choose wisely)</i><br />
<%= f.text_field :username %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<%= f.fields_for :profile do |profile_form| %>
<div><%= profile_form.label :full_name %><br />
<%= profile_form.text_field :full_name %></div>
<div><%= profile_form.label :birth_date %><br />
<%= profile_form.date_select :birth_date, start_year: Time.now.year, end_year: Time.now.year - 80, order: [:day, :month, :year], prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' } %></div>
<div><%= profile_form.label :gender %><br />
<%= profile_form.select :gender, { "Male" => '0', "Female" => '1' } %></div>
<div><%= profile_form.label :postcode %><br />
<%= profile_form.text_field :postcode %></div>
<div><%= profile_form.label :description, "About you" %><br />
<%= profile_form.text_area :description %></div>
<% end %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>
I don't have a controller code to show because it uses the Devise code inside the Gem. What am I doing wrong / not doing?

You are rebuilding the profile every time with build_profile, it means that when the form in going to be redisplayed, it's using a new instance of profile, not the one with errors. Just change that code so it only builds the profile if one does not exist:
resource.build_profile unless resource.profile
Something like that should work.

Carlos' answer helped me solve this exact issue.
resource.build_profile unless resource.profile

If your user model has a reference to the profile model, try passing
<%= f.fields_for resource.profile do |profile_form| %>
Right now, you're passing a reference to a model, but the form wants an instance.

Related

Custom field tag in devise registration view

I want to add custom field tag in devise registration view but that field doesn't exist in users table. I just want user select some value in that field and i get information about selected value in params at the time of sign up. Can anyone suggest me how to do this?
Devise_registration_view:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :type, 'Role' %>
<%= f.select :type, %w{Admin Teacher Student Guardian}, :prompt => 'Select', required: true, placeholder: 'Roles' %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
You can use attr_accessor for User model which will help you to get and set value for attribute which is not there in table, Please refer this link. attr_accessor_details
updating answer for adding custom validation for attribute created by attr_accessor
attr_accessor :test_attribute
validate :check_test_attribute
def check_test_attribute
errors.add(:test_attribute, "is missing") if test_attribute.blank?
end

Registration Form: Unable to Hide Field

In creating a new application I have used marklocklear example (https://github.com/marklocklear/devise_multi_tentant) for assistance in creating a multi-tenant environment. It works well.
The next thing I wanted to do I thought would be simple but has turned into quite the niggle for me.
All I wanted to do is rather than having the initial admin user have to enter an organization name (which is not necessary in my application), I wanted to hide that field.
Sign Up Form (that works):
<h2>Sign up</h2>
<% resource.organization ||= Organization.new %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<%= f.fields_for :organization do |org| %>
<div><%= 'Organization or Company Name' %><br />
<%= org.text_field :name %></div>
<% end %>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>
So, I did this to try to hide the field:
<%= f.fields_for :organization do |org| %>
<%= f.hidden_field :name %>
<% end %>
But I get an error.
The baffling thing to me is that if I leave the field in, but do not enter a value everything still works properly -- this is unexpected. Since (as part of my troubleshooting) I removed the field completely -- and got an error message stating the form could not be processed because the organization part was blank.
Any assistance would be appreciated.
Max: Thank you. As you know, that worked like a charm. The field is now hidden and the form submits properly. Thanks.
<%= f.fields_for :organization do |org| %>
<%= org.hidden_field :name %>
<% end %>

form_for in devise How it works?

rails g devise:views command generated that view
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :username %><br />
<%= f.text_field :username, :autofocus => true %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
My question is how does it work " form_for(resource, : as=>resource_name, ..."
resource is simple a record (or object) - with respect to Devise, it's usually something called User or similar. The rest of the parameters for form_for are options, detailed in the form_for docs.
I notice that both the :email and :username are calling for the email_field, which is a mistake.

Sign In/Up Form in different migrations

I have a devise users table with a fully functioning sign in/up form.
What I was wondering was how to have that users form appear on a different table.
For example
users/sign_in works perfectly
but
I want to have that form appear on movies/index
Ive tried adding the form code to the movies/index but i get this error
undefined local variable or method `resource' for #<#<Class:0x00000102cbf0b8>:0x00000103bb6d78>
This is the sign in form
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div><%= f.label :email %>
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %>
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render "devise/shared/links" %>
Thanks!
You can generate the views (I understand you already did that) and you can override the controllers, or, in your case, you can watch the controllers of Devise, take the code that you need, and in your view call a partial (from the Devise views).
I did something like that a few months ago, but what I did (that I don't fully suggest but I haven't find a better way) was to take the code from the Devise views, and copy the code in another view with some modifications:
<%= form_for(User.new, :as => "user", :url => session_path("user"), :remote => true) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<div><%= f.submit "Sign in" , :class=>"blue_submit_degradiant", :id =>"sign_in_user"%></div>
<% end %>
It works, but the right way is to have a #user instead of User.new

Devise edit account information

When I go to the edit_user_registration_path provided by Devise I have fields such as email and username pre-filled.
I want to create custom user profile page with the similar form for edit user information containing additional fields of dependent model. And I want fields be pre-filled.
How should I do it properly?
The default form provided by Devise:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :username %><br />
<%= f.text_field :username %></div>
<div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, :autocomplete => "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %></div>
<div><%= f.submit "Update" %></div>
<% end %>
Use nested attributes
In your devise user model, if you have other dependent models, you can add a relationship between those models, like has_many and belongs_to. The do this:
Devise user model:
accepts_nested_attributes_for :name_of_other_model
Then in your form you can use fields_for. fields_for docs
Here's also a great railscast of this here

Resources