Conforming Rails form into Formtastic Equivalent - ruby-on-rails

I have a Rails application where I am using Active Admin as the backend. Active Admin uses Formtastic and I am having trouble converting one of the input fields in my _form.html.erb for posts.
Here is the form...
<div class="container">
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :author %>
<%= f.collection_select :blog_id, Blog.all, :id, :name, prompt: "Select a blog" %>
</div>
<div class="field">
<p>
Categories:<br>
<%= hidden_field_tag "post[category_ids][]", nil %>
<% Category.all.each do |category| %>
<%= check_box_tag "post[category_ids][]", category.id, #post.category_ids.include?(category.id), id: dom_id(category) %>
<%= label_tag dom_id(category), category.name %>
<% end %>
</p>
</div>
<div class="field">
<%= f.hidden_field :status, value: "Draft" %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body, class: "redactor", id: "redactor" %>
</div>
<div class="field">
<%= f.label :published_on %><br />
<%= f.date_select :published_on %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
</div>
Here is the portion that I am having trouble converting to formtastic specific code. It comes from Ryan's screencast on HABTM Checkboxes.
<div class="field">
<p>
Categories:<br>
<%= hidden_field_tag "post[category_ids][]", nil %>
<% Category.all.each do |category| %>
<%= check_box_tag "post[category_ids][]", category.id, #post.category_ids.include?(category.id), id: dom_id(category) %>
<%= label_tag dom_id(category), category.name %>
<% end %>
</p>
</div>
Here is the formcode that is in the admin/post.rb file for Active Admin. If I use this I continue to get a no method error for category. I know that the way it is currently listed is incorrect.
form do |f|
f.inputs "Details" do
f.input :blog, hint: "Select a blog"
f.input :title, hint: "Enter blog post title"
f.input :published_on, as: :date, include_blank: false, hint: "Select a date", :prompt => {:day => "Day", :month => "Month", :year => "Year"}, :start_year => Time.now.year
f.input :status, as: :select, collection: ["Draft", "Published"], include_blank: false, hint: 'Select "Draft" to save and post later, and "Publish" to post now'
f.input :categories, as: :check_boxes
f.input :body, input_html: { class: "redactor", id: "redactor" }
end
f.buttons
end
If anyone has any input I am most appreciated. I have looked at the formtastic documentation and watched Ryan's screencasts, but am still having issues converting.
Thanks!
EDIT: ABLE TO RESOLVE-SOLUTION BELOW
I was able to make this work after getting a little more familiar with Formtastic and Active Admin. Below is the relevant code in the form, show, and index views in Active Admin.
ActiveAdmin.register Post do
index do
column "Post Title", :title
column :blog
column :published_on
column :status
column "Category", :categories do |post|
post.categories.collect { |cat| cat.name }.join(", ")
end
default_actions
end
show do
h5 "Created by #{post.blog.name} on #{post.created_at.strftime('%B %-d, %Y')}"
h5 "Categories: #{post.categories.collect { |cat| cat.name }.join(", ")}"
h5 "Current Status: #{post.status}"
h5 "Published On Date: #{post.published_on.strftime('%B %-d, %Y')} - (will only be posted if marked with Publish as status)"
div do
simple_format post.body
end
end
form do |f|
f.inputs "Details" do
f.input :blog, hint: "Select a blog"
f.input :title, hint: "Enter blog post title"
f.input :published_on, as: :date, include_blank: false, hint: "Select a date", :prompt => {:day => "Day", :month => "Month", :year => "Year"}, :start_year => Time.now.year
f.input :status, as: :select, collection: ["Draft", "Published"], include_blank: false, hint: 'Select "Draft" to save and post later, and "Publish" to post now'
f.input :categories, as: :check_boxes, collection: Category.all
f.input :body, input_html: { class: "redactor", id: "redactor" }
end
f.buttons
end
end

I was able to construct the Formtastic version as shown below. It took a little more understanding of Formtastic and then making minor adjustments for Active Admin.
Below is the relevant code in the form, show, and index views in Active Admin.
ActiveAdmin.register Post do
index do
column "Post Title", :title
column :blog
column :published_on
column :status
column "Category", :categories do |post|
post.categories.collect { |cat| cat.name }.join(", ")
end
default_actions
end
show do
h5 "Created by #{post.blog.name} on #{post.created_at.strftime('%B %-d, %Y')}"
h5 "Categories: #{post.categories.collect { |cat| cat.name }.join(", ")}"
h5 "Current Status: #{post.status}"
h5 "Published On Date: #{post.published_on.strftime('%B %-d, %Y')} - (will only be posted if marked with Publish as status)"
div do
simple_format post.body
end
end
form do |f|
f.inputs "Details" do
f.input :blog, hint: "Select a blog"
f.input :title, hint: "Enter blog post title"
f.input :published_on, as: :date, include_blank: false, hint: "Select a date", :prompt => {:day => "Day", :month => "Month", :year => "Year"}, :start_year => Time.now.year
f.input :status, as: :select, collection: ["Draft", "Published"], include_blank: false, hint: 'Select "Draft" to save and post later, and "Publish" to post now'
f.input :categories, as: :check_boxes, collection: Category.all
f.input :body, input_html: { class: "redactor", id: "redactor" }
end
f.buttons
end
end

Related

Simple Form simple_fields_for only show last record

I have a nested simple form to edit a user. The user has a profile he/she can update, and a new record is written to the profile table.
simple_fields_for shows all profile records of a user due to the relationship user 1 to many profile records. However, I would like to only show the newest profile record in the form! How can I accomplish that?
<%= simple_form_for(#user) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :lang %>
<%= f.input :firstname %>
<%= f.input :lastname %>
<%= f.input :email %>
<%= f.input :born %>
<%= f.input :gender %>
<%= f.simple_fields_for :profile do |p| %> # the magic needed here
<%= p.input :postal_code %>
<%= p.input :core %>
<%= p.input :daytime %>
<%= p.input :style %>
<% end %>
<% if #is_new %>
<%= f.simple_fields_for :status do |s| %>
<%= s.input :entered, as: :hidden, input_html: { value: Time.current } %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Little late, but accepted answer is not 100% accurate. (Would just make this a comment, but no rep.) I would do this like so:
Assuming you have
class User < ActiveRecord::Base
has_many :profiles
...
def latest_profile
profiles.order(...) # query to get whatever record it is you want
end
end
your simple_fields_for can just be
<%= f.simple_fields_for :profiles, f.object.latest_profile do |p| %>
Note the pluralization of :profiles, and that we don't need to clutter the controller with an additional instance variable because you can access the object in the form. (Using the singular will work I believe, but you will not get params with a key in the form of :profiles_attributes, meaning even more code to account for unique params.)
The edit action:
class user > ApplicationController
...
def edit
#profile = #user.profile.order("saved DESC").first
end
...
end
The working form:
<%= simple_form_for(#user) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :lang %>
<%= f.input :firstname %>
<%= f.input :lastname %>
<%= f.input :email %>
<%= f.input :born %>
<%= f.input :gender %>
<%= f.simple_fields_for :profile, #profile do |p| %> # the magic needed here
<%= p.input :postal_code %>
<%= p.input :core %>
<%= p.input :daytime %>
<%= p.input :style %>
<% end %>
<% if #is_new %>
<%= f.simple_fields_for :status do |s| %>
<%= s.input :entered, as: :hidden, input_html: { value: Time.current } %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>

Rails nested form using Formtastic - can't get association to update

In a rails application I want to update a nested attribute from a user's edit page. In the console I can do this:
foo = User.first.memberships.first
foo.organization_id = 10
foo.save
This will update the organization ID. I want to do this in a form using formtastic. First, my User model includes:
accepts_nested_attributes_for :memberships
The form looks like this:
<%= semantic_form_for [:admin, #resource], role: "form" do |f| %>
<div class="form-group">
<%= f.input :email, label: "Email" %>
<%= f.input :avatar, as: :file, name: "Avatar" %>
<%= f.input :first_name, label: "First Name" %>
<%= f.input :last_name, label: "Last Name" %>
<%= f.input :admin, as: :boolean, label: "Admin" %>
<%= f.input :password, as: :password, label: "Password" %>
<%= f.input :password_confirmation, as: :password, label: "Password Confirmation" %>
</div>
<div class="form-group">
<%= f.inputs :for => :memberships do |input| %>
<%= f.input :organizations, :as => :radio, :collection => memberships_for_user %>
<% end %>
</div>
<div class="small-spacer"></div>
<%= f.submit "Save User", class: "btn primary" %>
<% end %>
The helper method for the collection is this:
def memberships_for_user
Organization.all
.sort_by { |o| o.name }
.map { |o| [o.name, o.id] }
end
The ensuing html for the nested attributes look like this:
<li class="choice">
<label for="user_organization_ids_10">
<input id="user_organization_ids_10" name="user[organization_ids]" type="radio" value="10" />Beer-Davis
</label>
</li>
How can i fix this so that this form updates the organizaiton_id on the Memberships join model?
Try
<%= f.inputs :for => :memberships do |mf| %>
<%= mf.input :organizations, :as => :radio, :collection => memberships_for_user %>
<% end %>

How to submit a form using simple_form_for when what's being submitted isn't part of the model

I'm trying to submit a form that contains fields for both an event and an invitation to that event. Here is my form:
<%= simple_form_for(#event) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :start_at %>
<%= f.input :end_at %>
<%= f.input :all_day %>
<%= f.hidden_field :owner_id, value: current_user.id %>
<% if false %>
<%= f.association :sitter, label_method: lambda { |s| "#{s.name}" }, collection: User.all %>
<%= f.association :group, label_method: lambda { |g| "#{g.group_name}" }, collection: Group.where(:owner_id => current_user.id) %>
<% end %>
<%= f.input :user_emails, as: :text %>
<%= form_tag event_invitations_path, :method => :post do %>_tag :user_emails %>
</div><div>
<%= label_tag "Your message:" %>
</div><div>
<%= text_area_tag :email_message %>
<% end %>
</div>
</br>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
</div>
<% end %>
This is (perhaps obviously) not working. :user_emails is NOT part of the event or invitation model as it's a list of emails that will be used to create invitations. Basically I merged two forms, one that was accepting the email/send invitation piece and one that was accepting the event information. I think I have my controller and model set up properly to take care of this but how do i submit this information as part of the same form without getting an "undefined_method" error (since user_emails doesn't belong to events). Let me know if you want to see my model/controller.
Use FormTagHelper
<%= label_tag "User", "Email"%>
<%= text_area_tag "user_emails", "example#example.com"%>
See the documentation for more options on text_area_tag and label_tag

Can't get nested_form to show validation errors

I haven't had a problem with validations before but this time I am having issues with nested_form validations. I am using Twitter Bootstrap and can get flash errors to show with, say, this:
def create
#recipe = current_user.recipes.new(params[:recipe])
if #recipe.save
redirect_to my_recipes_path, :notice => "Thanks #{current_user.name} Recipe sucessfully created."
else
render :action => 'new'
end
end
For my flash messages I use this in my app/layouts
<% flash.each do |name, msg| %>
<div class="alert alert-<%= name == :notice ? "success" : "error" %>">
<a class="close" data-dismiss="alert">×</a>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
</div>
<% end %>
So I thought I would try and just get one of the validators working, so my model
class Recipe < ActiveRecord::Base
belongs_to :user
delegate :name, :to => :user, :prefix => :user, :allow_nil => true
belongs_to :country
has_many :ingredients
has_many :preperations
has_many :favourites
validates_presence_of :dish_name
and my form
<%= nested_form_for #recipe do |f| %>
<div class="field_with_errors">
<%= f.label :dish_name, "Dish Name" %>
<%= f.text_field :dish_name, :placeholder => "Enter Dish Name" %>
</div>
<%= f.label :country_id, "Country Of Origin" %>
<%= f.collection_select(:country_id, Country.all, :id, :name, :prompt => 'Please select country') %>
<%= f.label :category, "Category" %>
<%= f.select :category, [['Starter'], ['Main Course'], ['Desserts'], ['Vegeterian']], {:include_blank => 'Please Select'} %>
<%= f.label :difficulty, "Difficulty Level" %>
<%= f.select :difficulty, [['Beginner'],['Intermediate'],['Expert']], {:include_blank => 'Please Select'} %>
<%= f.label :preperation_time, "Preperation Time (Mins)" %>
<%= f.select :preperation_time, [['15-30 Mins'],['30-60 Mins'],['60-120 Mins']], {:include_blank => 'Please Select'} %>
<%= f.fields_for :ingredients do |ing| %>
Ingredient<br>
<%= ing.text_field :ingredient_name , :placeholder => "Enter Ingredient Here" %><br>
<% end %>
<%= f.link_to_add "Add an Ingredient", :ingredients %><br>
<%= f.fields_for :preperations do |prep| %>
Preperation Step<br>
<%= prep.text_field :prep_steps , :placeholder => "Enter step Here" %><br>
<% end %>
<%= f.link_to_add "Add a step", :preperations %><br>
<%= f.label :description, "Description of Recipe" %>
<%= f.text_area :description, :size=> "60x10" %></br>
<%= f.file_field :avatar %><br>
<%= f.submit "Submit Recipe" %>
<% end %>
I am fairly new to Rails so I may have missed something fundamental, or is it because it is a nested form and it behaves differently?
Edit
Output of <%= flash debug %>:
--- !ruby/object:ActionDispatch::Flash::FlashHash
used: !ruby/object:Set
hash: {}
closed: false
flashes: {}
now:
It seems you're not actually providing your flash hash with any messages to work with. A quick solution might be something like:
def create
#recipe = current_user.recipes.new(params[:recipe])
if #recipe.save
redirect_to my_recipes_path, :notice => "Thanks #{current_user.name} Recipe sucessfully created."
else
flash[:error] = #recipe.errors.full_messages.to_sentence
render :action => 'new'
end
end

Nested rails form with checkboxes HASBTM

I have form partial that allows the user to enter the subject and message that will be included in the outbound email. I want to allow the users to select the recipients of the email from the contacts that are associated with the invoice that the email belongs to. The email recipients that are selected through the nested form are to be stored in a separate table.
class EmailRecipient < ActiveRecord::Base
attr_accessible :contact_id, :email_id
belongs_to :email
end
class Email < ActiveRecord::Base
attr_accessible :subject, :message, :invoice_id, :email_recipients_attributes
belongs_to :invoice
has_many :email_recipients
accepts_nested_attributes_for :email_recipients
end
<%= simple_form_for [:invoice, #email], html: {class: "form-horizontal"} do |f| %>
<%= f.error_notification %>
<% #invoice.contacts do |c|%>
<%= f.fields_for :email_recipients do |builder| %>
<%= builder.input :contact_id, :as => :check_boxes %>
<%= c.name %><br/>
<% end %>
<% end %>
<%= f.input :subject, :as => "string" %>
<%= f.input :message, :input_html => { :class => 'span7', :rows => 10 } %>
<div class="form-actions">
<%= f.button :submit, "Send Invoice", :class => 'btn-warning' %>
<%= link_to 'Cancel', invoice_path(#invoice), :class => 'btn' %>
</div>
<% end %>
This isn't the prettiest answer, so I would love to see a better solution, but it gets the job done until my skills improve.
In the controller for the parent form I added:
#invoice.contacts.each { |c| #email.email_recipients.build(contact_id: c.id) }
This builds the records for all contacts whether they are needed or not. Then in the form partial I modified the nested form:
<%= simple_form_for [:invoice, #email], html: {class: "form-horizontal"} do |f| %>
<%= f.error_notification %>
<% count = 0 %>
<%= f.simple_fields_for :email_recipients do |email_recipients_form| %>
<%= email_recipients_form.input :_destroy, as: :boolean, :label => false do %>
<%= email_recipients_form.check_box :_destroy, {}, "false", "true" %>
<% contact = #invoice.contacts.find(#email.email_recipients[count].contact_id) %>
<%= contact.name + " (" + contact.email + ")" %>
<% end %>
<%= email_recipients_form.input :contact_id, as: :hidden %>
<% count += 1 %>
<% end %>

Resources