How to set field automatically in Rails - ruby-on-rails

I have a simple feedback form to allow the Users send the feedback. Controller for it:
class FeedbacksController < ApplicationController
expose(:feedback, attributes: :feedback_params)
def create
ApplicationMailer.customer_feedback(feedback).deliver_now if feedback.save
respond_with(feedback, location: root_path)
end
private
def feedback_params
params.require(:feedback).permit(:email, :message, :name, :phone)
end
end
Here I'm using expose gem.
And the view for it:
= simple_form_for(feedback, url: feedback_path) do |f|
legend
= t(".any_questions")
.form-inputs
= f.input :name
= f.input :email
= f.input :phone
= f.input :message, as: :text
.form-actions
= f.button :submit, t(".submit")
I want the next: to check somewhere if current user present(logged in to application) If yes = then set the email field using user.email (when he visit the feedback form -> email should be already filled in. If user not logged in to site then this field should be empty. Where should I put this logic? Thanks!

The simple form documentation explains how simple form is very easy to customize according to your needs. Assuming you are using devise for user authentication (if you're not, simple_form is designed to work with devise so you should check it out), then you can user current_user to access the currently logged in user.
I think simple_form's input_html would help you here:
= f.input :email, input_html: { value: "#{current_user.email if current_user}"}

Related

Using Role Model and simple_forms to assign roles

First off, I am using Devise, Cancan, Role Model, and simple_form. Everything seems to work with permissions and whatnot. What I can't seem to do is create a form so that I can assign roles to Users.
Here is what I have so far:
<%= simple_form_for(#profile.user) do |f| %>
<%= f.input :roles_mask, as: :check_boxes, collection: User.valid_roles, :checked => [ 'member' , true] %>
<%= f.button :submit %>
This shows the check boxes properly, and the last part flags one as true. I'm not sure how to get it to flag the roles that are supposed to be flagged. I chose one at random for testing Also, if I send it to update, nothing seems to happen. I updated like I normally would limiting my params to just the roles_mask variable.
def update
if #user.update(params.require(:user).permit(:roles_mask))
flash[:notice] = "Permissions updated."
redirect_to profile_path(#user.profile_id)
else
render 'edit'
end
I have no clue what I am doing wrong. Even if I could just get them to update. Not showing the current roles isn't a huge deal.
You don't need to directly access roles_mask property, just assign roles property with the array of roles. Example:
= f.input :roles, collection: User.valid_roles, as: :check_boxes, checked: #user.roles, label_method: proc {|l| User.human_attribute_name(l) }
And don't forget to permit that form value for strong parameters:
params.require(:user).permit(roles: [])

Fill form with two values

I have user/registrations/new.html.haml
- title 'User Registration'
.vcentered .signup-box
- if #user.authorizations.any? %h1 We just need a little more information...
- else %h1 We just need a little bit of information...
= form_for #user, url: user_registration_path, html: { class: 'new-user form-default' } do |f|
- if #user.name.blank?
= f.text_field :name, class: class_with_errors(#user, :name),
title: title_with_errors(#user, :name),
placeholder: 'Full Name'
- if #user.email.blank? || #user.errors.any?
= f.text_field :email, class: class_with_errors(#user, :email),
title: title_with_errors(#user, :email),
placeholder: 'Email'
- if #user.authorizations.empty?
= f.password_field :password, class: class_with_errors(#user, :password),
title: title_with_errors(#user, :password),
placeholder: 'Password'
= f.fields_for :profile do |p|
= p.hidden_field :username, value: ()
= p.text_field :place, class: class_with_errors(#user.profile, :place),
title: title_with_errors(#user.profile, :place),
id: 'profile-place',
placeholder: 'Type in your closest city...'
= p.hidden_field :city_id, id: 'profile_city_id'
.tip
E.G. "New York, New York, United States" or "Barcelona, Cataluña, Spain"
.note
Ps - We need this information to show you data relevant to your location.
We don't share it with anyone. If you cannot find your city, please type
the largest city closest to you.
= f.submit 'Sign up', class: 'submit'
Due to some association issue, I want to fill the value of
p.hidden_field :username, value: ()
with name and id.
For example if user fill name as 'Name Surname' and user id is '55' then username should be NameSurname55.
Please suggest code. Thanks in advance
My usercontroller looks like:
class Users::RegistrationsController < Devise::RegistrationsController
layout 'land'
#before_action :set_username
def create
params[:user][:profile_attributes].delete(:place)
super do |resource|
UserMailer.welcome(resource).deliver
aroundyoga_user = User.find_by(email: 'aroundyoga#gmail.com')
resource.follow!(aroundyoga_user) && aroundyoga_user.follow!(resource)
RegistrationWorker.perform_async(resource.id)
end
end
protected
def after_sign_up_path_for(resource)
welcome_path
end
end
Your :username input is hidden, so I assume user should not be able to edit username. Then you better remove this field from form at all and do all logic on server-side:
def create
params[:user][:profile_attributes].delete(:place)
super do |resource|
# Next 2 lines
resource.profile.username = resource.name.gsub(/\s+/, "") + resource.id.to_s
resource.profile.save
UserMailer.welcome(resource).deliver
aroundyoga_user = User.find_by(email: 'aroundyoga#gmail.com')
resource.follow!(aroundyoga_user) && aroundyoga_user.follow!(resource)
RegistrationWorker.perform_async(resource.id)
end
end
Edit: updated code according to information you provided. #pkrawat1 solution is better.
Add this in your profile model
before_create :generate_username
def generate_username
self.username = self.user.name.underscore + self.user.id.to_s
end
looks like you have used accept_nested_attributes in background.
so may be you can use callback in profile model like:
after_create: update_profile_username
def update_profile_username
username = user.name + user.id
save
end
I would avoid revealing IDs to the public, but If you insist:
Creating an object from params in the controller is as easy as writing
mo = MyObject.new params[my_object]
in your controller. If you need to change some attributes you would just do the following:
mo.name = mo.name + mo.id
remember that in both cases you have to call save to persist changes.
The biggest difficulty in your case is that if the user hasn't been persisted yet (i.e. it's a new object created using User.new) it hasn't been assigned an ID yet. But there's also an answer for that here
The easiest way might be:
class Profile < ActiveRecord::Base
after_create :update_name
def update_name
name = name + id
save
end
end

Rails 4 NOT updating nested attributes

Issue: Instead of updating nested attributes, they are being created on top of the existing nested attributes when I hit the #update action of the associated features_controller.rb
Likely Cause: I think the problem lies in my lack of understanding in Rails' form_for. I think the breakdown is in my views, how I render the persisting nested attributes, and/or how I fail to specify the nested attribute's id, causing it to simply create a new one
feature.rb
class Feature < ActiveRecord::Base
...
has_many :scenarios
accepts_nested_attributes_for :scenarios,
allow_destroy: true,
reject_if: :all_blank
...
end
features_controller.rb
def update
...
project = Project.find(params[:project_id])
#feature = Feature.find(params[:id])
if #feature.update_attributes(feature_params)
# checking feature_params looks good...
# feature_params['scenarios'] => { <correct object hash> }
redirect_to project
else
render :edit
end
end
...
private
def feature_params
params.require(:feature).permit(:title, :narrative, :price, :eta, scenarios_attributes[:description, :_destroy])
end
_form.html.haml (simplified)
= form_for [#project, #feature] do |f|
...
- if #feature.new_record? -# if we are creating new feature
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
- else -# if we are editing an existing feature
= f.fields_for :scenarios do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
I'm sure there's a nicer way to achieve the if #feature.new_record? check. I'm also using a few Javascript hooks to create dynamic nested attribute forms (which I've left out), heavily influenced by Railscast #196 Nested Model Form (revised)
I would love a really nice Rails-y implementation of dealing with these sorts of nested forms.
Try adding :id to the :scenario_attributes portion of your feature_params method. You only have the description field and the ability to allow a destroy.
def feature_params
# added => before nested attributes
params.require(:feature).permit(:id, :title, :narrative, :price, :eta, scenarios_attributes => [:id, :description, :_destroy])
end
As #vinodadhikary suggested, you no longer need to check if feature is a new record, since Rails, specifically using the form_for method, will do that for you.
Update:
You don't need to define if #feature.new_record? ... else in your form. It will be taken care by Rails when you use form_for. Rails checks if the action is going to be create or update based on object.persisted?, so, you can update your form to:
= form_for [#project, #feature] do |f|
...
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
As #Philip7899 mentioned as a comment in the accepted answer, allowing the user to set the id means that they could "steal" children records belonging to another user.
However, Rails accepts_nested_attributes_for actually checks the id and raises:
ActiveRecord::RecordNotFound:
Couldn't find Answer with ID=5 for Questionnaire with ID=5
Basically the ids are looked for in the children association (again, as said by #glampr). Therefor, the child record belonging to another user is not found.
Ultimately, 401 is the response status (unlike the usual 404 from ActiveRecord::RecordNotFound)
Follows some code I used to test the behaviour.
let :params do
{
id: questionnaire.id,
questionnaire: {
participation_id: participation.id,
answers_attributes: answers_attributes
}
}
end
let :evil_params do
params.tap do |params|
params[:questionnaire][:answers_attributes]['0']['id'] = another_participant_s_answer.id.to_s
end
end
it "doesn't mess with other people's answers" do
old_value = another_participant_s_answer.value
put :update, evil_params
expect(another_participant_s_answer.reload.value).to eq(old_value) # pass
expect(response.status).to eq(401) # pass
end
In conclusion, adding the id to the permitted params as stated above is correct and safe.
Fascinating Rails.

Validate field is unique compared to another field in same form

Say I have two fields in a new or edit form:
<%= f.text_field :email %>
<%= f.text_field :parent_email %>
How, in my model, can I validate that parent_email is different from email? The exclusion option seems like it might work, but I can't figure out how to access the email field's value within the model. Do I need to implement this in the controller instead?
validates :parent_email, exclusion: self.email # doesn't work, nor does :email
The following should work (but I guess there are cooler solutions out there):
class User
validate :email_differs_from_parent_email
private
def email_differs_from_parent_email
if email == parent_email
errors.add(:parent_email, "parent_email must differ from email")
end
end
end

add checkbox with simple_form without association with model?

How I can add checkbox with simple_form without association with model?
I want to create checkbox which will handle some javascript events, but don't know?
Maybe I miss something in documentation?
Want't to use similar like following:
= simple_form_for(resource, as: resource_name, url: session_url(resource_name), wrapper: :inline) do |f|
.inputs
= f.input :email, required: false, autofocus: true
= f.input :password, required: false
= f.input :remember_me, as: :boolean if devise_mapping.rememberable?
= my_checkbox, 'some text'
You can add a custom attribute to the model:
class Resource < ActiveRecord::Base
attr_accessor :custom_field
end
Then use this field as block:
= f.input :custom_field, :label => false do
= check_box_tag :some_name
Try to find "Wrapping Rails Form Helpers" in their documentation https://github.com/plataformatec/simple_form
You could use
f.input :field_name, as: :boolean
The command proposed by huoxito does not work (at least not in Rails 4). As far as I figured out the error is caused by Rails trying to look up the default value for :custom_field, but because this field does not exists this lookup fails and an exception is thrown.
However, it works if you specify a default value for the field using the :input_html parameter, e.g. like this:
= f.input :custom_field, :as => :boolean, :input_html => { :checked => "checked" }
This question comes first on google with no appropriate answer.
Since Simple Form 3.1.0.rc1 there is a proper way of doing it explained on the wiki: https://github.com/plataformatec/simple_form/wiki/Create-a-fake-input-that-does-NOT-read-attributes
app/inputs/fake_input.rb:
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
template.text_field_tag(attribute_name, nil, merged_input_options)
end
end
Then you can do <%= f.input :thing, as: :fake %>
For this specific question you have to change the second line of the method to:
template.check_box_tag(attribute_name, nil, merged_input_options)
For versions prior 3.1.0.rc1 admgc gave a solution that is adding the missing method merge_wrapper_options:
https://stackoverflow.com/a/26331237/2055246
Add this to app/inputs/arbitrary_boolean_input.rb:
class ArbitraryBooleanInput < SimpleForm::Inputs::BooleanInput
def input(wrapper_options = nil)
tag_name = "#{#builder.object_name}[#{attribute_name}]"
template.check_box_tag(tag_name, options['value'] || 1, options['checked'], options)
end
end
then use it in your views like:
= simple_form_for(#some_object, remote: true, method: :put) do |f|
= f.simple_fields_for #some_object.some_nested_object do |nested_f|
= nested_f.input :some_param, as: :arbitrary_boolean
i.e. The above implementation supports nested fields correctly. Other solutions I've seen do not.
Note: This example is HAML.
Here is another variation:
= f.label :some_param, class: "label__class" do
= f.input_field :some_param, class: "checkbox__class"
Label text

Resources