Ruby on Rails: render form on a shared folder - ruby-on-rails

I want to put the form on a shared folder for DRY the code and render this form for [:admin, :posts] and :posts.
So I create a folder and put the form on app/views/shared/_form.html.slim
- if params[:admin][:posts]
post = [:admin, #post]
- if params[:posts]
post = #post
= simple_form_for(post, :html => { :multipart => true }) do |f|
= f.association :categories, label: "Select the Categories:", as: :check_boxes , collection: #categories.map{|c| [c.name, c.id]}, include_hidden: false
= f.input :title
= f.input :subtitle
- if #post.attachment.present?
.attachment
p
= image_tag(#post.attachment.thumb.url, alt: 'Image', class: "img-responsive img-thumbnail")
= f.check_box :remove_attachment
| Remove image
br
.text-center
small
sample
= "File_size #{number_to_human_size(#post.attachment.size)}"
= f.input :attachment, as: :file, label: "File"
= f.input :attachment_cache, as: :hidden
= f.input :remote_attachment_url, label: "Enter URL to an image"
= f.input :content, size: "150x150"
= f.button :submit, class: 'btn-primary'
and then on
app/views/admin/post/new.html.slim and app/views/posts/edit.html.slim
I added the render:
== render 'shared/form', post: #post
So I tried to run and I have this error:
ActionView::Template::Error (undefined method `[]' for nil:NilClass):
1: - if params[:admin][:posts]
2: post = [:admin, #post]
3: - if params[:posts]
4: post = #post
This is just an idea that I have. it's ok for do this in this case or forget to do this?
routes:
rake routes | grep post
Running via Spring preloader in process 2187
admin_posts POST /admin/posts(.:format) admin/posts#create
new_admin_post GET /admin/posts/new(.:format) admin/posts#new
admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
nullify_posts_admin_user PATCH /admin/users/:id/nullify_posts(.:format) admin/users#nullify_posts
root GET / posts#index
published_posts GET /posts/published(.:format) posts#published
draft_posts GET /posts/draft(.:format) posts#draft
recent_posts GET /posts/recent(.:format) posts#recent
publish_post PATCH /posts/:id/publish(.:format) posts#publish
unpublish_post PATCH /posts/:id/unpublish(.:format) posts#unpublish
posts GET /posts(.:format) posts#index
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update

I see that what you're trying to do is figure out whether you're in the admin section or the standard post section and change the url of the form that way.
This is a bit of a non-standard way of doing it.
Generally speaking, what I've seen is that only the body of the form is in the shared form section, but the action is saved in the outside partial so eg:
app/views/admin/post/new.html.slim:
= simple_form_for(post, :html => { :multipart => true }) do |f|
== render 'shared/post_form', f: f, post: post
app/views/shared/_post_form.html.slim:
= f.association :categories, label: "Select the Categories:", as: :check_boxes , collection: #categories.map{|c| [c.name, c.id]}, include_hidden: false
= f.input :title
= f.input :subtitle
- if post.attachment.present?
.attachment
p
= image_tag(post.attachment.thumb.url, alt: 'Image', class: "img-responsive img-thumbnail")
= f.check_box :remove_attachment
| Remove image
br
.text-center
small
sample
= "File_size #{number_to_human_size(post.attachment.size)}"
= f.input :attachment, as: :file, label: "File"
= f.input :attachment_cache, as: :hidden
= f.input :remote_attachment_url, label: "Enter URL to an image"
= f.input :content, size: "150x150"
= f.button :submit, class: 'btn-primary'
Note: if you pass post as a local variable into a form, you should use it with post not #post (because #post completely ignores the local variable you passed in and goes back to whatever came from the controller, in which case why bother passing in the local variable?)
You should also probably never call another variable post inside the template... as it overrides the old post variable and is then gone. Name it something different eg in this case, if you REALLY wanted to use the template the way you have been, you could call it post_url_params

So following the idea of the #Taryn East, I put this and work's:
app/views/admin/posts/new.html.slim
= title("New Post")
.header
h1 New Post
== render 'form', post: #post
app/views/admin/posts/edit.html.slim
= title("Edit Post", #post.title)
.header
h1 Edit Post
== render "form", post: #post
app/views/admin/posts/_form.html.slim
= simple_form_for([:admin, post], :html => { :multipart => true }) do |f|
== render "shared/post_form", f: f, post: post
app/views/posts/_form.html.slim
= simple_form_for(post, :html => { :multipart => true }) do |f|
== render "shared/post_form", f: f, post: post
app/shared/_post_form.html.slim
= f.association :categories, label: "Select the Categories:", as: :check_boxes , collection: #categories.map{|c| [c.name, c.id]}, include_hidden: false
= f.input :title
= f.input :subtitle
- if post.attachment.present?
.attachment
p
= image_tag(post.attachment.thumb.url, alt: 'Image', class: "img-responsive img-thumbnail")
= f.check_box :remove_attachment
| Remove image
br
.text-center
small
sample
= "File_size #{number_to_human_size(post.attachment.size)}"
= f.input :attachment, as: :file, label: "File"
= f.input :attachment_cache, as: :hidden
= f.input :remote_attachment_url, label: "Enter URL to an image"
= f.input :content, input_html: { rows: '15' }
= f.button :submit, class: 'btn-primary'

Related

Rails 5 Survey client side answers

I have a model MyQuiz that belongs to Quiz through Questions and also Belongs to User
Quizzes are created on models: Quiz and Question. So the model MyQuiz is the one that records the answers
I'm using SimpleForm so I came up with this form bellow for MyQuiz
= simple_form_for(#my_quiz) do |f|
= f.error_notification
- #questions.each do |question|
.form-group
%p= question.title
= f.input :answer, as: :radio_buttons
= f.input :question_id, as: :hidden, :input_html => { :value => question.id }
= f.input :user_id, as: :hidden, :input_html => { :value => current_user.id }
.form-group
= f.button :submit, class: "btn btn-primary pull-right"
my_quizzes_controller.rb
def new
#quiz = Quiz.find(params[:quiz_id])
#questions = #quiz.questions.all
#my_quiz = MyQuiz.new
end
def create
#my_quiz = MyQuiz.new(my_quiz_params)
if #my_quiz.save
redirect_to user_path(#user), notice: "Thank you taking the quiz!"
else
render "new"
end
end
private
def my_quiz_params
params.require(:my_quiz).permit(:question_id, :user_id, :answer)
end
The problem with this form is that it is not set properly to serialize the answers. So it acts as if all the answers are the same. Even the radio buttons are behaving the same way
I'm not quite sure on what I should do here. I also tried a hacky solution with simple_fields_for but it did not change the outcome.
Thanks in advance
UPDATE: With Suggestion and Another try
So the suggestion was to MyQuiz form as a nested form
something like this:
= simple_form_for #user do |g|
= g.error_notification
- g.simple_fields_for #my_quiz do |f|
- #questions.each do |question|
.form-group
%p= question.title
= f.input :answer, as: :radio_buttons
= f.input :question_id, as: :hidden, :input_html => { :value => question.id }
= f.input :user_id, as: :hidden, :input_html => { :value => current_user.id }
.form-group
= g.button :submit, class: "btn btn-primary pull-right"
I tried and still had the same problem with the radio button.
I'm thinking that it might be an unecessary call into UserModel create/update.
I've been playing around with an option like this:
= form_tag my_quizzes_path do
- #questions.each do |question|
%p= question.title
= radio_button_tag "my_quizzes[#{question}][answer]", '0'
= radio_button_tag "my_quizzes[#{question}][answer]", '1'
= hidden_field_tag "my_quizzes[][question_id]"
= hidden_field_tag "my_quizzes[][user_id]"
= submit_tag "Submit"

One simple_form_for both New and Edit methods

Trying to find a way to use one simple_form_for for two functions- new and edit.
= simple_form_for #news_item, :url => url_for(:action => 'create', :controller => 'news_items'),
:method => 'post' do |f|
= f.input :title, input_html: { class: 'title-input' }
= f.input :contents, input_html: { class: 'contents-input' }
.actions
= f.submit 'Save', class: 'btn btn-success'
Currently, this form will create new form every time hit submit button but can I send this to :url => url_for(:action => 'update') as well?
Try this:
= simple_form_for #news_item, url: (#news_item.new_record? ? news_items_index_path(#news_item) : news_items_path(#news_item)), method: (#news_item.new_record? ? :post : :patch) do |f|
Or something like that, you get the picture...

Extract search functionality to Form Object in Rails

In my Rails application I have simple search functionality. I want to extract to Form Object but don't know how to do. I have search form which looks like this:
.row
= horizontal_simple_form_for :cars, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.location_address, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.location_address, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
Cars controller:
class CarsController < ApplicationController
skip_authorization_check
def index
#cars = Car.search(params)
end
def show
end
end
And class method in Car model which search correct cars:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:cars][:return_date], params[:cars][:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:cars][:car_class])
.cars_at_both_locations(params[:cars][:handover_location], params[:cars][:return_location])
end
Now I'm trying to extract this to Form Object. I've created a file search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
end
But now I don't know how to handle my params to this form object. Thank's in advance.
I wish I could help you with the Form Object stuff, but I need to learn more about classes & modules
I can help you with the search functionality, as we've done it before here
Here's the code we used:
#View
<%= form_tag search_path, :method => :post, :id => "SearchForm" do %>
<%= text_field_tag :search, params[:search], placeholder: 'Search your favourite products or brands', :autocomplete => :off %>
<%= image_submit_tag 'nav_bar/search.png' %>
<% end %>
#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]
#app/controllers/products_controller.rb
def search
#products = Product.search(params[:search])
respond_to do |format|
format.js { render :partial => "elements/livesearch", :locals => {:search => #products, :query => params[:search]} }
format.html { render :index }
end
end
Notice the form_tag we used?
Simple form does not work with form_tag currently (it requires an object) - we just send the data with a GET request to the controller & that then sends the data to the Product model
I think your problem will be caused by the use of your SearchForm object. You only need this because your use of simple form means you have to pass an object. Problem being this is not necessary for search
A better way will be to use a standard form_tag, and send the request directly to your controller. This will allow you to process the data as params, which you'll be able to send directly to your Car model
--
I can write some code specific to you if you want
I found solution on my own.
Cars controller:
def index
#search_form = SearchForm.new(params[:search_form])
#cars = #search_form.submit(params[:search_form])
end
search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
def submit(params)
Car.search(params)
end
end
Search form in view:
.row
= horizontal_simple_form_for SearchForm.new, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.name, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.name, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
search method in car model:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:return_date], params[:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:car_class])
.cars_at_both_locations(params[:handover_location], params[:return_location])
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