I have been struggeling with a nested form for a while and I dont know why it is not working.
The form updates/creates the venue without complaint, but not the associated venue_image.
Problem: I want to create/update a model and one of its associated models at the same time. Please note that the controllers are namespaced under "admin"
_form.haml
.col-md-12
= form_for([:admin, venue], html: { class: 'form-horizontal', multipart: true }) do |f|
- if venue.errors.any?
.col-md-12.alert.alert-danger{role: "alert"}
%h2
= pluralize(venue.errors.count, "Error")
%ul
- venue.errors.full_messages.each do |message|
%li= message
.form-group
= f.label :name, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :name, class: 'form-control'
.form-group
= f.label :category, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :category, class: 'form-control'
.form-group
= f.label :description, class: 'col-md-2 control-label'
.col-md-10
= f.text_area :description, rows: 5, class: 'form-control'
.form-group
= f.label :street, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :street, class: 'form-control'
.form-group
= f.label :zip, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :zip, class: 'form-control'
.form-group
= f.label :city, class: 'col-md-2 control-label'
.col-md-10
= f.text_field :city, class: 'form-control'
.form-group
= f.label :homepage, class: 'col-md-2 control-label'
.col-md-10
= f.url_field :homepage, class: 'form-control'
%h3 Add images
= f.fields_for(:venue_image, html: { class: 'form-horizontal', multipart: true }) do |vi|
.form-group
= vi.label :name, class: 'col-md-2 control-label'
.col-md-10
= vi.input :name, label: false, class: 'form-control'
.form-group
= vi.label :venue_id, class: 'col-md-2 control-label'
.col-md-10
= vi.input :venue_id, label: false, class: 'form-control'
.form-group
= vi.label :default, class: 'col-md-2 control-label'
.col-md-10
= vi.input :default, as: :radio_buttons, label: false, class: 'form-control radio radio-inline'
.form-group
= vi.label :image_file, class: 'col-md-2 control-label'
.col-md-10
= vi.file_field :image_file, label: false, class: 'form-control'
.actions
= f.submit 'Save', class: 'btn btn-primary pull-right'
= link_to 'Cancel', :back, class: 'btn btn-default'
venues_controller
class Admin::VenuesController < Admin::BaseController
before_action :set_venue, only: [:show, :edit, :update, :destroy]
def index
#venues = Venue.all
end
def show
end
def new
#venue = Venue.new
#venue.venue_images.build
end
def edit
end
def create
#venue = Venue.new(venue_params)
if #venue.save
redirect_to admin_venue_path(#venue), notice: 'Venue was successfully created.'
else
render :new
end
end
def update
if #venue.update(venue_params)
redirect_to admin_venue_path(#venue), notice: 'Venue was successfully updated.'
else
render edit_admin_venue
end
end
def destroy
#venue.destroy
redirect_to admin_venues_url, notice: 'Venue was successfully destroyed.'
end
private
def set_venue
#venue = Venue.find(params[:id])
end
def venue_params
params.require(:venue).permit(:name, :category, :description, :street, :zip, :city, :homepage,
venue_image_attributes: [:name, :default, :image_file])
end
end
venue_images_controller
class Admin::VenueImagesController < Admin::BaseController
def new
image = VenueImage.new
render locals: { image: image }
end
def create
# TODO: Remove # if possible
#image = VenueImage.new(venue_images_params)
if #image.save
redirect_to admin_venue_path(#image.venue.id), notice: 'Image was successfully created.'
else
render admin_new_venue_path
end
end
private
def venue_images_params
params.require(:venue_image).permit(:name, :default, :image_file, :venue_id)
end
end
routes
namespace :admin do
resources :venues do
resources :venue_images
end
resources :users
end
Thanks in advance for any help! Please let me know if you need more of the code.
Seems like you have a has_many relation with the Venue(i.e, has_many :venue_images), then this line
= f.fields_for(:venue_image, html: { class: 'form-horizontal', multipart: true }) do |vi|
should be
= f.fields_for(:venue_images, html: { class: 'form-horizontal', multipart: true }) do |vi|
And your venue_params method should be like below
def venue_params
params.require(:venue).permit(:id, :name, :category, :description, :street, :zip, :city, :homepage, venue_images_attributes: [:id, :name, :default, :image_file])
end
Notice the plural venue_images and also I added :id to venue_params for Update to work correctly.
Related
Im trying to submit a nested form when you create a school, you can also create a admin with it. When I try to save the form with the default value of owner set to true and status set to active, it saves the form but doesnt save those values in the database
class SchoolsController < ApplicationController
before_filter :authenticate_admin!, except: [:index, :new, :create]
def show
#school = School.friendly.find(params[:id])
end
def new
#school = School.new
#admin = #school.admins.build(:owner => true, :status => "active")
end
def create
#school = School.new(school_params)
if #school.save
redirect_to new_admin_session_path
else
render 'new'
end
end
def edit
#school = School.find_by_slug(params[:id])
end
def update
#school = School.find_by_slug(params[:id])
if #school.update_attributes(school_params)
redirect_to :action => 'show', :id => #school
else
render :action => 'edit'
end
end
def team
#teams = Admin.all
end
private
def school_params
params.require(:school).permit(:school_name, :latitude, :longitude, :radius, admins_attributes: [ :first_name, :last_name, :email, :password, :password_confirmation, :image ])
end
end
class School < ActiveRecord::Base
has_many :admins
accepts_nested_attributes_for :admins
extend FriendlyId
friendly_id :school_name, use: :slugged
def should_generate_new_friendly_id?
school_name?
end
# Validation
validates :school_name, presence: true, uniqueness: true
validates :latitude, presence: true
validates :longitude, presence: true
validates :radius, presence: true, numericality: { only_integer: true }
end
%nav.navbar.navbar-default.navbar-fixed-top{role:"navigation"}
%div.container
%div.navbar-header
%button.navbar-toggle.collapsed{"data-toggle" => "collapse", "data-target" => "#navbar", "aria-expanded" => "false", "aria-controls" => "navbar"}
%span.sr-only Toggle Navigation
%span.icon-bar
%span.icon-bar
%span.icon-bar
%a.navbar-brand{"href" => "/", "id"=> "brand"} QuickAlert
%div#navbar.collapse.navbar-collapse
%ul.nav.navbar-nav.main-menu
%li.active
%a{"href" => "#"} Home
%li
%a{"href" => "#about"} About
%li
%a{"href" => "#contact"} Contact
%div.container-fluid
%div.row
%div.school-create
- if #school.errors.any?
%ul
- #school.errors.full_messages.each do |msg|
%li
= msg
%div#map-check
%h2.header School Info
= form_for #school do |f|
= f.label :school_name, "School Name"
= f.text_field :school_name, :class => 'form-control'
= f.label :latitude, "Latitude"
= f.text_field :latitude, :class => 'form-control', :id => "latitude"
= f.label :longitude, "Longitude"
= f.text_field :longitude, :class => 'form-control', :id => "longitude"
= f.label :radius, "Radius"
= f.text_field :radius, :class => 'form-control', :id => "radius"
%div.admin-fields
%h2.header Admin Info
= f.fields_for :admins do |ff|
= ff.label :first_name
= ff.text_field :first_name, :class => 'form-control'
= ff.label :last_name
= ff.text_field :last_name, :class => 'form-control'
= ff.label :email
= ff.text_field :email, :class => 'form-control'
= ff.label :password
= ff.password_field :password, :class => 'form-control'
= ff.label :password_confirmation
= ff.password_field :password_confirmation, :class => 'form-control'
= ff.file_field :image
= f.submit :class => 'submit-button btn btn-primary'
Im trying to edit a field so that it goes in the database as the owner boolean being set to true when the user creates the account. I get the error undefined method `owner
= form_for #school do |f|
= f.label :school_name, "School Name"
= f.text_field :school_name, :class => 'form-control'
= f.label :latitude, "Latitude"
= f.text_field :latitude, :class => 'form-control'
= f.label :longitude, "Longitude"
= f.text_field :longitude, :class => 'form-control'
= f.label :radius, "Radius"
= f.text_field :radius, :class => 'form-control'
= f.fields_for :admins do |ff|
= ff.label :email
= ff.text_field :email, :class => 'form-control'
= ff.label :password
= ff.password_field :password, :class => 'form-control'
= ff.label :password_confirmation
= ff.password_field :password_confirmation, :class => 'form-control'
= f.submit :class => 'submit-button'
def new
#school = School.new
#school.admins.build
#school.admins.owner = true
end
def create
#school = School.new(school_params)
if #school.save
redirect_to :action => 'show', :id => #school
else
render 'new'
end
Because #school.admins is a collection of multiple admins, so it doesn't have the owner method, that exists on the individual admin objects within that collection.
When you call #school.admins.build it returns a new instance of Admin, but you're not doing anything with that instance. You need to put it in a variable, and then assign a value to its owner attribute. That will look like this:
#school = School.new
#admin = #school.admins.build
#admin.owner = true
But the build method takes an attributes argument, so you can shorten that to this:
#school = School.new
#admin = #school.admins.build(owner: true)
Don't forget that you'll also need to save this object at some point.
In my application I have form:
.row
= simple_form_for #post, html: { class: 'form-horizontal' },
wrapper: :horizontal_form do |f|
= f.error_notification
.col-md-8
= f.input :text, as: :prepend
= f.input :active
= f.button :submit
and custom input:
class PrependInput < SimpleForm::Inputs::Base
def input
template.content_tag(:div, class: 'col-md-12') do
template.content_tag(:div, class: 'input-group') do
template.concat cal_addon
template.concat #builder.text_field(attribute_name, input_html_options)
end
end
end
def cal_icon
"<i class=\"fa fa-calendar\"></i>".html_safe
end
def cal_addon
template.content_tag(:span, class: 'input-group-addon') do
template.concat cal_icon
end
end
def input_html_options
{class: 'form-control datepicker margin-top-none'}
end
end
Now I want to pass additional attributes to this option "as" and use them in this custom input, is there any possibility to do this thing?
My form creates a question and answers. For true and false I currently have one answer being submitted that is the correct answer. I am trying to figure out how to make another answer with the same attributes except have the opposite value, either True or False.
Since the answers are created with the question I'm not sure what to do. I was thinking of the controller having something like, #question.answers.build(question: params[:content]) but am lost. Is there a #question.answers.build.where(content: (#question.content.opposite)) or something? Any help aprreciated
The controller:
def new_tf
#question = Question.new
#question.answers.build
end
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
def question_params
params.require(:question).permit(:content, :question_type, :category, :product_id, :active, :user_id, answers_attributes: [ :content, :correct, :question_id ] ).
merge user_id: current_user.id
end
The form:
<h1>New True/False Question</h1>
<%= link_to 'Back', questions_path %>
<%= form_for #question, url: new_tf_question_path(#question) do |f| %>
<%= render 'shared/error_questions' %>
<%= f.label :content, "Question" %><br>
<%= f.text_field :content, class: "input-lg" %>
<%= f.label :category %><br>
<%= f.select :category, [ ["IP Voice Telephony", "ip_voice"], ["IP Video Surveillance", "ip_video_surveillance"], ["IP Video Telephony", "ip_video_telephony"], ["Enterprise Gateways", "enterprise_gateways"], ["Consumer ATAs", "consumer_atas"], ["IP PBX", "ip_pbx"] ], {prompt: "Select Category"}, class: "input-lg" %>
<%= f.label :product_id %><br>
<%= f.collection_select :product_id, Product.all, :id, :name, {prompt: "Select a product"}, {class: "form-control input-lg"} %>
<%= f.label :active %><br>
<%= f.check_box :active %>
<%= f.fields_for :answers do |builder| %>
<%= render 'tf_answers', :f => builder %>
<% end %>
<%= f.select :question_type, [["True False", "TF"]], {class: "form-control input-lg"}, style: "visibility: hidden" %>
<%= f.submit "Create Question", class: "btn btn-lg btn-primary", style: "margin-top: 45px;" %>
<% end %>
The _tf_answers.erb.rb
<%= f.check_box :correct, {checked: true, style: "visibility: hidden"} %>
<%= f.label :content, "Answer" %>
<%= f.text_field :content, :value => 'True', :readonly => true, :class => "input-lg", :id => "answer" %>
<%= button_tag "Toggle True/False", :id => "toggle", :class => "btn btn-small btn-inverse", :type => "button" %>
<script>
function checkAnswer() {
var answer = $('#answer').val();
if ('False' == answer) {
$("#answer").val('True');
} else {
$("#answer").val('False');
}
}
$(document).ready(function(){
$('#toggle').click(function () {
checkAnswer();
});
});
</script>
I ended up creating a new answer after the first one saved.
if #question.save
#answer = Answer.find_by_question_id(#question.id)
if #answer.content == "True"
Answer.create(content: "False", question_id: #answer.question_id, correct: false)
end
if #answer.content == "False"
Answer.create(content: "True", question_id: #answer.question_id, correct: false)
end
Ta-da!
Why doesn't your model automatically create both answers? Extend the save method in your model and you won't have to have this logic in your controller. It probably belongs there anyway.
Recently, I decided to split my User Form into an Edit & Account Form.
User Edit Form
-Name
-Username
-AboutMe
User Account Form
-Email
-Password
-Password Confirmation
The routes work fine and everything gets updated accordingly. But for some reason, when my User Model validates the Email Presence & it Fails, it renders the Edit Form with the appropriate Error Messages as oppose to the Account Form.
How can I set up my Update Method in my Controller to know which form to Render with the appropriate error messages?
Model
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :username
validates :username, :presence => true,
:length => { :maximum => 15 },
:format => { :with => VALID_UNAME_REGEX },
:uniqueness => { :case_sensitive => false }
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }, :if => :password #only validate if password changed!
validates :password_confirmation, presence: true, :if => :password
end
Views
Edit View
<%= form_for #user, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="statictitle">Your Profile</div>
<%= f.text_field :username, placeholder: "Username..", :class => "form-control" %>
<%= f.text_field :name, placeholder: "Name", :class => "form-control" %>
<%= f.text_area :bio, placeholder: "About yourself in 160 characters or less...", class: "textinput" %>
<%= f.submit "Update Profile", class: "btn btn-primary" %><br>
<% end %>
Account View
<%= form_for #user, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="statictitle">Your Account</div>
<%= f.text_field :email, placeholder: "Email", :class => "form-control" %>
<%= f.password_field :password, placeholder: "Password", :class => "form-control" %>
<%= f.password_field :password_confirmation, placeholder: "Password Confirmation", :class => "form-control" %>
<%= f.submit "Update Account", class: "btn btn-primary" %><br>
<% end %>
Controller
class UsersController < ApplicationController
def edit
#user = User.find_by_username(params[:id])
end
def account
#title = "Account"
#user = User.find_by_username(params[:id])
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to user_url
else
render 'edit' ###I can't seem to figure out how to render the correct form
end
end
end
Routes
resources :users do
member do
get :account
end
end
You can check the HTTP Referer in this case which will tell you where the request came from and accordingly render your view.
for eg:
if URI(request.referer).path == edit_user_path
render :edit
else
render :account
end