Reject creation of nested attribute if checkbox checked - ruby-on-rails

I have a form in my rails app that accepts nested attributes. However, what I want to do is for rails to reject the creation of the nested model if a checkbox (outside the model itself) is checked.
Any idea on how to pass an attribute to the :reject_if option of the accepts_nested_attributes_for in the model from the controller?
Thank you very much in advance.
EDIT:
My controller looks like this:
def new
#course = Course.new
#course.course_template = CourseTemplate.new
end
def create
#course = Course.new(course_params)
#course.user = current_user
if #course.save
flash[:success] = t(".new_course_created_succefully")
redirect_to courses_path
else
render 'new'
end
end
And the form:
<%= form_for #course do |f| %>
<%= render 'shared/error_messages', error_model: #course %>
<div class="form-group has-feedback mb">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group has-feedback mb">
<div class="checkbox c-checkbox needsclick">
<label class="needsclick">
<%= check_box_tag "template", "1", false, {class: "needsclick"} %>
<span class="fa fa-check"></span>Is Template?
</label>
</div>
</div>
<%= f.fields_for :course_template do |ff| %>
<div class="form-group has-feedback mb">
<%= ff.label :name %>
<%= ff.text_field :name %>
</div>
<% end %>
<% end %>

send that checkbox as a parameter from the form and put the build operation inside an if statement. No need to bother with the reject_if
You need to handle your create and build operations separately. so instead of passing your model all attributes, youll pass the model the model attributes, and the association, the nested attributes
# controller
course = Course.new(course_params.reject{|attrib| attrib == :course_template_attributes})
unless params[:skip_create]
course.course_templates.build(course_params[:course_template_attributes]
end
...
what you need to do is conditionally create the course_templates, so you can just pass Course.new all your course_params because that creates both the course and the templates, which needs to be done separately.
Note I'm shorthanding with that reject statement up there. you can either manually add in the various params or better yet create another method with strong params and whitelist only the model attributes (not including the course_template_attributes)
additionally. the params[:skip_create] is whatever the parameter is for that checkbox that decides whether or not you want to create the templates

Related

How can I render option field from other database table in rails?

I have a model Responsibility with has one text field of responsibility. Other model is Stage, in Stage form field there is a text_field responsibility I want to render an option list from responsibilities table how can i do that in rails?
routes.rb
resources :projects do
resources :responsibilities
resources :stages
end
stage _form.html.erb
<%= form_with(model: stage, url: [#project, stage], local: true) do |form| %>
<div>
<%= form.label :responsibility, :class=>"required" %>
<%= form.text_field :responsibility %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
How can I render responsibilities list as an option in stage responsibility form field?
what is tried is:
stage _form.html.erb
<div>
<%= form.label :responsibility %>
<%= select_tag "colors", #responsibilities , multiple: true %>
</div>
stages_controller.rb
def new
#stage = Stage.new
#project = Project.find(params[:project_id])
#responsibilities = #project.responsibilities
end
I was able to render form but in responsibility field none of the responsibility was accessed as option.
select_tag accepts as a second parameter string that contains options as a string.
Rails provides some helper methods that are useful for generation of those <option>
tags
options_from_collection_for_select, options_for_select
If you inspect with your browser's developer tools the html code of the <select> tag you will see something like this:
<select name="colors[]" id="colors" multiple="multiple">
#<Responsibility::ActiveRecord_Relation:0x00007f3f72cc7eb0>
</select>
This is because select_tag calls to_s method of #responsibilities collection.
The correct way of creating select_tag would looks something like this:
<%= select_tag "colors", options_from_collection_for_select(#responsibilities, :id, :name) , multiple: true %>
There is another way to build select field using the FormBuilder method collection_select. It might look something like this:
<div>
<%= form.label :responsibility %>
<%= form.collection_select :responsibility, #responsibilities, :id, :name, prompt: true %>
</div>
I hope this answer will be useful.

Why isn't my controller taking the param value from the form?

I've been looking for a solution to this for quite sometime now but I cannot wrap my head around on how to solve this. I'm new to Rails/Ruby programming and would appreciate any direction on this problem.
I have a form that takes in values and creates a Task object. Each Task belongs_to a Lead and a Lead has_many Tasks.
The path for a new Task takes in the lead_id as one of the parameters.
/accounts/:account_id/reps/:rep_id/leads/:lead_id/tasks/new
But my goal is to also allow the user to use the form to create a Task for any lead. So, ideally, he should be allowed to select a lead from a dropdown and that lead_id needs to travel through to the CREATE method.
Here's my form:
<%= simple_form_for(#task, url: account_rep_lead_tasks_path) do |f| %>
<%= f.error_notification %>
<div class="field">
<div class="control">
<div class="select">
<%= f.collection_select :lead_id, #leads, :id, :name %>
</div>
</div>
</div>
<div class="field">
<div class="control">
<%= f.input :task_type, required:true, autofocus:true, input_html: {class: "input"}, wrapper: false, label_html: {class: "label"} %>
</div>
</div>
<div class="field">
<div class="control">
<%= f.input :description, required:true, autofocus:true, input_html: {class: "textarea"}, wrapper: false, label_html: {class: "label"} %>
</div>
</div>
<div class="field">
<div class="control">
<%= f.button :submit, class: "button is-primary" %>
</div>
</div>
By default, I render the new Task form for the latest Lead. Suppose the latest Lead created has an ID of 21, here's what shows in the URL:
http://localhost:3000/accounts/1/reps/2/leads/21/tasks/new
When I submit the form however, even though I'm selected a different lead via the collection_select input in the form for lead_id, the Task isn't being created for the selected Lead but being created for lead with id = 21.
The server logs show that the parameters that are being sent to the CREATE method are:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sKwctK492JH01yXilEI7jSSmv53ggiJuRBH/9Ate2AS+Lx53CO5BqgIOh44uvhFi6MPJ+Og1EY0U8m2BzqC9eA==", "task"=>{"lead_id"=>"16", "task_type"=>"Hello", "description"=>"Is it me you're looking for?"}, "commit"=>"Create Task", "account_id"=>"1", "rep_id"=>"2", "lead_id"=>"21"}
As you can see, there's a lead_id = 16 (which means that the form is taking the input correctly) but also another lead_id = 21 is present for which the Task is being created.
I want the Task to be created for lead_id = 16. Any idea on what I should be doing?
Here's my Task create controller action
def create
#account = current_account
#rep = current_account.reps.find(params[:rep_id])
#lead = #rep.leads.find(params[:lead_id])
#task = #lead.tasks.build(new_task_params)
if #task.save
redirect_to account_rep_lead_task_path(#rep.account_id,#rep.id,#lead.id,#task)
else
render 'new'
end
end
Try below code
def create
#account = current_account
#rep = current_account.reps.find(params[:rep_id])
#lead = #rep.leads.find(params[:task][:lead_id])
#task = #lead.tasks.build(new_task_params)
if #task.save
redirect_to account_rep_lead_task_path(#rep.account_id,#rep.id,#lead.id,#task)
else
render 'new'
end
end
You'll find lead_id under params[:task] hash. Like: params[:task][:lead_id]
You are getting 2 different lead_id because, first, you have url parameter lead_id which is defined in your routes:
/accounts/:account_id/reps/:rep_id/leads/:lead_id/tasks/new
And Second, it is from your form. So if you want to get lead_id from your form, you can use:
params[:task][:lead_id]
else
params[:lead_id] which is defined in your URL Parameter.

Rails: Accept optional nested Image; allow to add later if nil

I have a simple Article model associated with an Image model. The relationship is currently one-to-one (might change later but is fine for now).
Everything works fine when I create an Article from scratch and add an Image to it. Here's the catch: I would like the Image to be optional at creation, but also I would retain the option to add an Image at a later stage.
However, I am not sure how to handle that through the edit action. I have tried this:
def edit
#article = Article.find(params[:id])
if #article.image.nil?
#article.image = Image.new
end
render 'articles/edit'
end
... which results in:
ActiveRecord::RecordNotSaved in Admin::ArticlesController#edit
Failed to save the new associated image.
The form currently looks like this:
<%= f.fields_for :image do |image| %>
<div class="form-group">
<%= image.label :image, "Article image" %><br/>
<%= image_tag(#article.image.path.thumb.url) %>
<%= image.file_field :path %>
</div>
<div class="form-group">
<%= image.label :caption %>
<%= image.text_field :caption, class: 'form-control' %>
</div>
<div class="form-group">
<%= image.label :credits %>
<%= image.text_field :credits, class: 'form-control' %>
</div>
<% end %>
How can I accomplish optional nested images that can be added through the edit form later on?
Thanks!
The simplicity of Rails never ceases to amaze me. Here's how I solved it by adding one line (#image = ...):
def edit
#article = Article.find(params[:id])
#image = #article.image || #article.build_image
render 'articles/edit'
end
... inspired by Can't update my nested model form for has_one association

Rails - Multiple entries for a single attribute

I am making an app in Rails 4. I use Simple Form.
I have a profile model and a qualifications model.
The associations are:
profile.rb
belongs_to :profile
qualifications.rb
has_many :qualifications
I have a form in my profile views, which includes a part of a form from my qualifications view.
profiles#form
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="intpol2">
Your professional qualifications
</div>
<%= render 'qualifications/form', f: f %>
</div>
Qualifications#form
<%= simple_fields_for :qualification do |f| %>
<div class="form-inputs">
<div class="row">
<div class="col-md-6">
<%= f.input :title, :label => "Your award" %>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
</div>
<div class="col-md-6">
<%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
</div>
</div>
Users may have more than one degree. I want to add a field that is a button which says 'add another qualification' and then a new set of the qualification form fields is available.
I found this post which tries to do something slightly different. I don't want 10 blank sets of the form field (it will make the form look too long).
Creating multiple records for a model in a single view in Rails
Is there another way to achieve this?
You'll be looking for a gem called cocoon; you can also watch this Railscast (Nested forms) which is woefully outdated but still explains the structure very well.
The pattern is very simple, but requires some extra parts:
Have an ajax button which calls the controller
The controller needs to return a form and built fields_for
You'll use JS to append the new fields_for to the original form
The biggest problem is the id of your new fields_for - new implementations of this pattern use child_index: Time.now.to_i
I've written about this here.
Here's a new version:
Ajax
Firstly, you need an "Add Qualification" button, which links to your controller through ajax:
#app/views/profiles/_form.html.erb
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="intpol2">Your professional qualifications</div>
<%= render 'qualifications/form', f: f %>
</div>
</div>
<%= button_to "+", new_profile_path, method: :get %>
<% end %>
Controller
This will go through the new controller method, which we should be able to manage to return the specific response for the ajax request:
#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
respond_to :js, :html, only: :new
def new
#profile = Profile.new
#profile.qualifications.build
respond_with #profile #-> will invoke app/views/profiles/new.js.erb
end
end
Response
Once your new.js.erb has fired, we need to build the new HTML form, extract the fields_for and append it to your view:
#app/views/profiles/new.js.erb
var fields_for = $("<%=j render "profiles/form" %>").html(); //-> might need tweaking
$(fields_for).appendTo("#new_profile.form-inputs");
child_index
You should also change your qualifications/form to include the child_index:
#app/views/qualifications/form.html.erb
<%= simple_fields_for :qualifications, child_index: Time.now.to_i do ...
Child index is meant to denote the index of the fields_for elements. In our case (since we're making all new records), it doesn't matter. Using Time.now.to_i ensures a totally unique id each time.
Finally, you need to make sure you're calling:
<%= simple_fields_for :qualifications ... %>
... plural
It seems like that you have to use nested form. You have to try your link tutorial because I will use this too. For another tutorial you can use this as reference nested_forms-rails-4.2.
I hope this help you.

Rails - Failing to pass parameters from New to Create in the controller

This is an excerpt from a view -
<%= #user.id %> is the user id
<%= #book.id %> is the book id
<div class="field">
<%= f.select :contribtype, options_for_select(Contribution::CONTRIB_TYPES) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have passed the parameters for the user and the book from previous views, such that I have
http://localhost:3000/contributions/new?book_id=1&user_id=5
as the URL for the form. The correct user_id and book_id are showing up on the page.
I have the following in the controller -
def new
#user = User.find_by_id(params[:user_id])
#book = Book.find_by_id(params[:book_id])
#contribution = Contribution.new(params[book_id: #book.id, user_id: #user.id])
end
def create
#contribution = Contribution.new(contribution_params)
....
... but the user_id and book_id are not being captured in the object when it is created. I don't get any error, the data is simply not being set in the new object. Should I by passing parameters in the create action differently?
I'd use hidden fields as a quick fix:
#contribution = Contribution.new
in html:
<%= f.hidden_field :user_id, value: #user.id %>
<%= f.hidden_field :book_id, value: #book.id %>
be sure to permit those fields in your contribution_params

Resources