Ruby on Rails - Can't access the updated model from Slim View - ruby-on-rails

I have a view for editing my model. I am using Slim views.
My model_name is 'cupcake'
= simple_form_for [:admin, :order, record] do |f|
.row
.col-md-6
label for="_kind" Kind
= f.select :kind, [['Clipart', :clipart], ['Image', :image], ['Message', :message]], { selected: record[:kind] }, value: record[:kind], class: 'form-control', :input_html => { :name => "cupcake[:kind]" }
- if #record[:kind] == 'image'
.col-xs-12
= f.input :image, as: :shrine_file
- elsif #record[:kind] == 'clipart'
.col-md-6
label for="_clipart" Clipart
= f.select :clipart_id, options_for_select(#cliparts.map { |c| [c['name'], c['id'], {'value' => c['id']}] }, selected_key = selected_clipart(record).id), { selected: selected_clipart(record).name }, class: 'form-control', :input_html => { :name => "cupcake[:clipart_id]" }
- else
.col-md-6
= f.input :text_line_style, collection: ["Three Line Text","Single Line Text"], checked: record['first_line'] ? 'Three Line Text' : "Single Line Text", as: :radio_buttons, item_wrapper_class: "inline"
.row
.col-xs-12
hr
button.btn.btn-success.btn-lg
= fa_icon('check')
| Save
- redirect_location = [:admin, :cliparts]
= link_to redirect_location, class: 'btn btn-primary btn-lg'
= fa_icon('chevron-left')
| Back
.col-xs-12
- if f.object.created_at.present?
p.help-block Created at: #{f.object.created_at}
- if f.object.updated_at.present?
p.help-block Updated at: #{f.object.updated_at}
I want to update the view based on the kind selected. Currently, it is not updating the view if I select a different kind. When I try to access cupcake[:kind], it is showing undefined error. Any way to access the cupcake object? I looked for it in the f object. But, I couldn't find it.
Anyone, please help?

Related

Why dropdown list items do not show at my simple_form_for collection code when I render the page?

I am trying to insert a simple_form drop down list in my page but I am having troubles with showing the list items (Two email addresses - conatct#test.com and support#test.com). When I click the drop down list arrow they simply do not show as they were not there.
.reveal.doubt id="doubt-material-#{material.id}" data-reveal=true
button.close-button data-close=true aria-label="Close reveal" type="button"
span aria-hidden="true" ×
h5 #{t 'students.materials.index.questions.button'}
p #{t 'students.materials.index.questions.form_explanation'}
= simple_form_for [:student, trail, component, material, material_student, doubt], html: { id: "doubt" }, remote: true do |f|
= f.input :recipient, collection => %w(contact#test.com, support#test.com), label: "#{t 'students.materials.index.questions.form_send_email'}"
= f.input :question, as: :text, label: true, label: "#{t 'students.materials.index.questions.form_message'}" , input_html: { rows: "2" }
= f.submit "#{t 'students.materials.index.questions.form_send_button'}", class: 'button primary-button-active'
When I inspect the code it seems it is working but I can not see the email options anywhere.
The code line I have added is:
= f.input :recipient, collection: %w(contact#test.com, support#test.com), label: "#{t 'students.materials.index.questions.form_send_email'}"
What is wrong here ?
I believe your collection needs the name and value to be set for each option. You could try: [["contact#test.com", "contact#test.com"], ["support#test.com", "support#test.com"]]. Or use the Rails options_for_select helper method:
= f.input :recipient, collection => options_for_select(%w[contact#test.com contact#test.com])

How do you create partially pre-filled forms with Rails, Simple_Form, and Cocoon?

I have a form to collect CV information from Users. In it, I need to gather what languages they speak and their proficiency level with each. I want to have 3 languages that are required, already present on the form, and have the option to allow the User to add additional languages. Cocoon works great for allowing them to add languages on their own, but, how can I get 3 language instances already in the form?
cvs/_form.html.haml
= simple_form_for(#cv, url: user_cvs_path) do |f|
...
%hr
.row
%strong Please indicate known languages and ability level in each:
#languages
= f.simple_fields_for :languages do |language|
= render 'language_fields', f: language
.links
= link_to_add_association 'Add Language', f, :languages, class: 'btn btn-success btn-sm'
...
cvs/_language_fields.html.haml
.row.nested-fields
= f.input :name, wrapper_html: { class: 'col-sm-4' }
= f.input :read, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= f.input :write, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= f.input :speak, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= f.input :listen, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= link_to_remove_association "Remove Language", f
In it's present state, this section of the form looks like this:
But what I want is this:
Based on Pablo's answer, I've added this to my controller:
def new
#cv = #user.build_cv
#cv.languages.build(name: 'English')
#cv.languages.build(name: 'Cantonese')
#cv.languages.build(name: 'Mandarin')
end
That get's me this far:
Now I just need to figure out how to make the languages 'Fixed' so they can't be changed and I need to get rid of the "Remove Language" links that Cocoon supplies.
You must create three languages in the controller:
def new
#cv = #user.build_cv #or current_user.build_cv
3.times do { #cv.languages.build }
end
To get 3 specific languages, use this in the controller instead:
def new
#cv = #user.build_cv
#cv.languages.build(name: 'English')
#cv.languages.build(name: 'Cantonese')
#cv.languages.build(name: 'Mandarin')
end
Then to display those three in the form, make them non-editable and non-removable, change the form to this:
= f.simple_fields_for :languages do |language|
.row
= language.input :name, disabled: true, wrapper_html: { class: 'col-sm-4' }
= language.input :read, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= language.input :write, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= language.input :speak, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
= language.input :listen, collection: #language_levels, wrapper_html: { class: 'col-sm-2' }
.links
= link_to_add_association 'Add Language', f, :languages, class: 'btn btn-success btn-sm'

boolean input_filed calls immediately the method on Rails

The project which I am working in, is developed on Rails using haml markup to views. There is a view with a simple form like this:
= simple_form_for #message, url: [:admin, #request, #message], html: { class: 'form vertical-form} do |f|
= f.input :text, as: :text, input_html: { class: 'form-control', rows: 5 }
%br
= f.input :link_url, input_html: { class: 'form-control'}
%br
- if #message.has_picture_image?
= f.label :image
=link_to #message.picture_image, target: "_blank" do
= image_tag #message.picture_image(:thumb)
= f.file_field :image, class:'imagen-button'
= f.input_field :remove_picture, as: :boolean, inline_label: 'Remove'
%br
.form-actions
= f.submit(t('accept'), class: 'btn btn-large btn-primary')
= link_to(t('cancel'), [:admin, #message.request, #message], class: 'btn btn-large btn-danger')
and in Message model there is the bellow method:
def remove_picture
self.picture.destroy
end
The input_field is used to check if I want to remove the message image if it exists. I understood that input_filed gives me the option to check it so that when I click on accept button, it call the method remove_picture in the Message model. But, before the browser deploys the form, it rise the next error:
undefined method `to_i' for #<Picture:0x007f7675162b58>
Extracted source (around line #39):
37: = image_tag #message.picture_image(:thumb)
38: = f.file_field :image, class:'imagen-button'
39: = f.input_field :remove_picture, as: :boolean, inline_label: 'Remove'
40: %br
41: .form-actions
42: = f.submit(t('accept'), class: 'btn btn-large btn-primary')
and if I reload the page, this time the form is deployed. I guess this is because in the first time, as the picture exists then immediatly the remove_picture is called and the picture removed, and when I reload the form, as the picture already does not exist, the form is shown.
Obviously I am undestanding wrongly the input_field usage.
SimpleForms input_field is a helper which binds an input to a model attribute. It does not create a box which calls your method when the box is ticked! But rather it will call your remove_picture method when it rendering the form.
In some cases like checkboxes you will want to bind inputs to attributes that are not saved in the database. We call these virtual attributes. They are just like any old Ruby attributes:
class Message < ActiveRecord::Base
attr_accessor :remove_picture
# since this method is destructive it should have a bang (!)
def remove_picture!
self.picture.destroy
end
end
You could use it like this:
class MessagesController < ApplicationController
def update
#message.update(update_params)
#message.remove_picture! if message.remove_picture
# ...
end
def update_params
params.require(:message).allow(:remove_picture)
end
end
But there is a better way:
class Message < ActiveRecord::Base
has_one :picture_image
accepts_nested_attributes_for :picture_image, allow_destroy: true
end
accepts_nested_attributes_for lets us create an image with picture_image_attributes and destroy an image with:
#picture.update(picture_image_attributes: { _destroy: true })
This is how we would set up the form:
= simple_form_for #message, url: [:admin, #request, #message], html: { class: 'form vertical-form} do |f|
= f.input :text, as: :text, input_html: { class: 'form-control', rows: 5 }
%br
= f.input :link_url, input_html: { class: 'form-control'}
%br
- if #message.has_picture_image?
f.simple_fields_for :picture_image do |pif|
= pif.label :image
= link_to #message.picture_image, target: "_blank" do
= image_tag #message.picture_image(:thumb)
= pif.file_field :image, class:'imagen-button'
= pif.input_field :_destroy, as: :boolean, inline_label: 'Remove'
%br
.form-actions
= f.submit(t('accept'), class: 'btn btn-large btn-primary')
= link_to(t('cancel'), [:admin, #message.request, #message], class: 'btn btn-large btn-danger')
And your strong parameters whitelist:
def update_attributes
params.require(:message).allow(
:text,
:link_url,
picture_image_attributes: [:image, :_destroy]
)
end

Limit simple_form_for associated records number in Ruby on Rails

I have a large psychological test of 251 question. Each user can complete that test many times. So I created Summary model to represent each completion. Each Summary has many Answers. For each Summary I created a form that represents collection of answers, using Slim templater and simple_form gem:
= simple_form_for(#summary) do |f|
= f.simple_fields_for :answers do |a|
.question
= a.input :question_id, as: :hidden
div= a.object.question.title
- if a.object.question.kind_of? SpiritualityQuestion
ol class="sortable"
- a.object.question.sortable_variants.each do |sortable_variant|
= content_tag_for :li, sortable_variant
= sortable_variant.title
= a.input :text_data, as: :hidden, input_html: { class: 'sortable_data' }
- elsif a.object.question.kind_of? MultilineQuestion
div Time remaining: <span class="time">60</span> s.
= button_tag 'Start', type: 'button', class: 'start_button btn btn-primary'
= a.input :text_data, label: false, input_html: { class: 'span8 timed_text', cols: '60', rows: '20', disabled: true }
- else
= a.association :variant, collection: a.object.question.variants, as: :radio, label: false
br
= f.input :user_id, as: :hidden
= f.input :psy_test_id, as: :hidden
.actions
= f.button :submit, value: 'Save', class: 'btn btn-large btn-success'
And I have related controller action:
#summary = Summary.where(completed: false, user: current_user, psy_test: PsyTest.first)
.includes(:answers => { :question => :variants })
.first_or_initialize
#summary.build_answers if #summary.new_record?
Summary.build_answers:
def build_answers
# Creating answers for questions
psy_test.questions.includes(:variants).each do |q|
answers.new(question: q)
end
end
Now I'm trying to make the test to be paginated, because it's very large and the form generates very slowly. So I want to add limit and offset to answers. Something like that:
= f.simple_fields_for answers.limit(#limit).offset(#offset) do |a|
How it can be made?
I've looked to do field_for source and found simple answer, which I wasn't able find in any guide:
= f.simple_fields_for :answers, #summary.answers.limit(#limit).offset(#offset) do |a|

Strong_parameters in view with mixed models

I am using strong parameters and when I try to save the following form I get the following message.
undefined method `household_params' for # '<'VisitsController:0x007fa88deec428'>'
I am confused because I am using the visits controller and not the households controller - The visit is associated with a household as shown below and I call the view with the following code:
= link_to 'New Visit', {controller: 'visits', action: 'new',
household_id: household.id, method: 'post'}, class: 'btn btn-primary'
The Form is:
%h3 Household: #{household.name}
%h4 Household Members: #{household.neighbors.count}
%h4 Visits: #{household.visits.count}
= simple_form_for visit do |f|
= f.input :visited_on, :label => 'Visited On', as: :date_picker, input_html: { class: 'span2' }
= f.input :starch, :label => false, collection: ['Beans','Rice','Potatoes'],selected: 'Beans'
= f.input :cereal, :label => false, collection: ['Cereal','Grits','Oatmeal']
= f.input :option1, :label => false, collection: ['Peanut Butter Jelly', 'Deserts','Baby Fromula'], prompt: 'Options'
= f.input :items_received, :label => 'Special Needs',input_html: {rows: 4, class: 'span9' }
= f.input :notes, :label => 'Notes',input_html: {rows: 4, class: 'span9' }
= f.button :submit, :class => 'btn-primary', :label=> 'Save'
The form works fine without the three lines that display information about the household
I am thinking strong_parameters is getting confused

Resources