I don't know which is the best or right way to make something, so I'm looking for suggestions.
Stage:
I am using Rails 3 to make some web app.
Imaging two models: Canvas and Zone.
A canvas object intents to be a square in which there will be four zone's (north, south, east and west).
Each zone has these attributes: text:string and rgb_color:string.
I want to render a HTML form in which user must capture a Canvas registry, in other words, in this form will have four groups of zones fields.
Problem:
How can I capture all of them in one HTML form?
If I would have only one zone I can do something like this:
<%= form_for(#zone) do |f| %>
<div class="field">
<%= f.label :text %><br />
<%= f.text_field :text %>
</div>
<div class="field">
<%= f.label :rgb_color %><br />
<%= f.text_field :rgb_color %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But how should I do it with four zones?
You can do this with accepts_nested_attributes_for and fields_for:
Model:
class Canvas < ActiveRecord::Base
has_many :zones
accepts_nested_attributes_for :zones
end
Controller:
class CanvasesController < ApplicationController
def new
#canvas = Canvas.new
4.times { #canvas.zones.build }
end
end
View:
<%= form_for(#canvas) do |f| %>
<%= f.fields_for :zones do |zone| %>
<div class="field">
<%= zone.label :text %><br />
<%= zone.text_field :text %>
</div>
<div class="field">
<%= zone.label :rgb_color %><br />
<%= zone.text_field :rgb_color %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
For more details and explanation have a look at this:
Nested Model Form Part 1
Nested Model Form Part 2
Related
In my application I have a page for editing all users however I would like it so that I only have to click one submit button to update the users' details but at the moment I can only edit one and click the 'Update User' button to change that one user's details.
_form.html.erb
<%= form_tag edit_user_path do |form| %>
<% #users.each do |user| %>
<%= form_for user do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :sunday %><br>
<%= f.text_field :sunday %>
</div>
<div class="field">
<%= f.label :monday %><br>
<%= f.text_field :monday %>
</div>
<div class="field">
<%= f.label :tuesday %><br>
<%= f.text_field :tuesday %>
</div>
<div class="field">
<%= f.label :wednesday %><br>
<%= f.text_field :wednesday %>
</div>
<div class="field">
<%= f.label :thursday %><br>
<%= f.text_field :thursday %>
</div>
<div class="field">
<%= f.label :friday %><br>
<%= f.text_field :friday %>
</div>
<div class="field">
<%= f.label :saturday %><br>
<%= f.text_field :saturday %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
<%= submit_tag "Submit" %>
<% end %>
As you can see that submit_tag at the bottom of the code does absolutely nothing however I would like it to update all the users' details. Any help would be greatly appreciated!
Against:
<%= form_for user do |f| %>
use:
<%= fields_for "users[]", user do |f| %>
After that you will get in the controller parameters values for each user_id:
"users"=>{"user_id1"=>{"attr1"=>"value1"}, "user_id2"=>{"attr1"=>"value1"}
Also to make possible updating a collection of object a way to do it is to add an action to the UsersController like this:
def update_collection
# Update users here
end
and update routing in config/routes.rb:
resources :users do
collection do
match 'update_collection', via: [:put, :patch]
end
end
and use proper url in the main form:
<%= form_tag update_collection_users_path, method: :put do |form| %>
A beginner in Ruby here!
I am trying to create a Soundcloud clone on ruby.
When I try to upload an audio file i get the error:
1 error prohibited this song from being saved:
Audio has contents that are not what they are reported to be
controller: song.rb
class Song < ActiveRecord::Base
belongs_to :user
has_attached_file :audio,
:url => "/assets/:class/:id/:attachment/:basename.:extension",
:path => ":rails_root/public/assets/:class/:id/:attachment/:basename.:extension"
validates_attachment :audio,
:content_type => { :content_type => ["audio/mpeg", "audio/mp3"] },
:file_name => { :matches => [/mp3\Z/] }
_form.html.erb
<%= form_for(#song) do |f| %>
<% if #song.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#song.errors.count, "error") %> prohibited this song from being saved:</h2>
<ul>
<% #song.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :audio %><br>
<%= f.file_field :audio%>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Audio:</strong>
<%= #song.audio.url %>
</p>
<p>
<strong>Title:</strong>
<%= #song.title %>
</p>
<%= link_to 'Edit', edit_song_path(#song) %> |
<%= link_to 'Back', songs_path %>
In my experience with this problem, image data in the mp3's metadata (i.e. - an album cover) was the critical factor. The image data triggers the content type spoofing mechanism and the record will fail validation. To remove the image metadata, I opened the mp3 with Audacity, and exported it to a new file, which will not include image metadata because Audacity doesn't seem to include this automatically.
I am using gem 'country_select', github: 'stefanpenner/country_select' in my gem file and in my form i have defined it like this:
<%= form_for(#account_detail) do |f| %>
<div class="field">
<%= f.label :city %><br>
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label :zip %><br>
<%= f.number_field :zip %>
</div>
<div class="field">
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :country %><br>
<%= f.country_select("account_detail", "country") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
on submit its giving error ActionView::Template::Error (wrong number of arguments (4 for 0)):
Which gem is best to show all countries?
I would use:
<%= f.country_select :country %>
If you like to prioritize some countries in the select pass them in in an array:
<%= f.country_select :country, {priority_countries: %w(<COUNTRY CODE i.e. US>), prompt: 'Select Country'} %>
You can add class: 'your-class' and id or whatever just as with any other field if you like. Hope it helps.
This should do!
<%= f.country_select :country, { priority_countries: ["GB", "US"], selected: "GB" } %>
I have solved this issue by adding this method in my model:
def country_name
country = ISO3166::Country[country_code]
country.translations[I18n.locale.to_s] || country.name
end
and in view change given line :
<%= f.country_select("account_detail", "country") %>
to this:
<%= f.country_select :country, format: :with_alpha2 %>
Hope this will help to someone else who will face this problem.
I'm using Carrierwave with Rails to upload and display images. I've been using it for articles and other parts of my site without a problem but for some reason it's not working for the model I just created. I can see the space for the image tag rendered without the image and then disappear quickly when the page loads. Nothing like this has occurred until I tried it with this model which isn't much different from my other models:
class Ad < ActiveRecord::Base
validates_numericality_of :zip_code
validates_presence_of :image, :advertiser
mount_uploader :image, ImageUploader
end
My create and index actions:
def create
#ad = Ad.new(params[:ad].permit(:image, :advertiser, :zip_code))
if #ad.save
redirect_to :back
else
flash[:error] = "Invalid input"
redirect_to :back
end
end
def index
#new_ad = Ad.new
#ads = Ad.all.reverse
end
My _new partial:
<div id="card">
<%= form_for #new_ad, html: {multipart: true, "data-ajax" => false} do |f| %>
<p>
<%= f.file_field :image %>
</p>
<p>
<%= f.text_field :advertiser, placeholder: "Advertiser" %>
</p>
<p>
<%= f.text_field :zip_code, placeholder: "Zip code" %>
</p>
<p>
<%= f.submit "Upload" %>
</p>
<% end %>
</div></br>
My index view:
<%= render "new" %>
<% for ad in #ads %>
<div id="card">
<div align="center">
<%= image_tag ad.image %>
<%= ad.advertiser %>
<%= ad.zip_code %>
</div>
</div></br>
<% end %>
As per my understanding problem is with your index view.
<%= image_tag ad.image %>
you are using ad.image this will return you imageuploader object not image path. So instead of using ad.image you can use ad.image.url(relative path) or ad.image.current_path(absolute path)
<%= image_tag ad.image.url %>
I created a sample app to play around with rails. I'm having trouble with the page format of my site when a page is rendered. The text fields moves out of place from it's previous position and the format is just ruined. Please see the site and just click the button and you'll see what I'm talking about.
Here is my controller:
class BooksController < ApplicationController
def new
#books = Book.new
end
def show
end
def create
#books = Book.new(book_params)
if #books.save
redirect_to new_book_path, :flash => { :success => "Book Added"}
else
render 'new'
end
end
def update
end
def edit
end
def destroy
end
private
def book_params
params.require(:book).permit(:title, :author, :genre, :no_of_items)
end
end
here my views/new.html.erb
<h1>Books#new</h1>
<p>Find me in app/views/books/new.html.erb</p>
<% flash.each do |key, value|%>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
<div class="form-horizontal">
<div class="control-group row-fluid form-inline">
<%= form_for #books do |f| %>
<%= render 'shared/error_message' %>
<p>
<div class="fields">
<%= f.label :title, "Title:"%>
<%= f.text_field :title %><br/><br/>
</div>
<div class="fields">
<%= f.label :author, "Author:" %>
<%= f.text_field :author %><br/><br/>
</div>
<div class="fields">
<%= f.label :no_of_items, "No. of Items:" %>
<%= f.text_field :no_of_items%><br/><br/>
</div>
<div class="fields">
<%= f.label :genre, "Genre:" %>
<%= f.text_field :genre%><br/><br/>
</div>
<p>
<%= submit_tag "Add book"%>
</p>
</p>
<% end %>
</div>
</div>
Can anyone help me tell what's happening and a fix for this? Thanks.
ANSWER:
Hi everyone thanks for the advises but I found out through Rails 3: "field-with-errors" wrapper changes the page appearance. How to avoid this? thread how to fix this problem. I can't answer my question within 6 hours so I'll answer this after the restriction has ended.
Each label and input is being wrapped in an additional div element with the class field_with_errors.
You could give the field_with_errors class an display: inline-block; property, or try formatting your form like the example in the Bootstrap docs.
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="inputEmail">Email</label>
<div class="controls">
<input type="text" id="inputEmail" placeholder="Email">
</div>
</div>
</form>
http://getbootstrap.com/2.3.2/base-css.html#forms
There is some unnecessary formatting you've added to the new.html.erb. One of rails' strength is the minimal amount of work created with helpers such as form_for
<h1>Books#new</h1>
<p>Find me in app/views/books/new.html.erb</p>
<% flash.each do |key, value|%>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
<div class="form-horizontal">
<div class="control-group row-fluid form-inline">
<%= form_for #books do |f| %>
<%= render 'shared/error_message' %>
<%= f.label :title, "Title:"%>
<%= f.text_field :title %>
<%= f.label :author, "Author:" %>
<%= f.text_field :author %>
<%= f.label :no_of_items, "No. of Items:" %>
<%= f.text_field :no_of_items %>
<%= f.label :genre, "Genre:" %>
<%= f.text_field :genre %>
<%= f.submit "Add book" %>
<% end %>
</div>
</div>
first you don't align your input fields correctly, i see some margin-left (for the first filed is 60px, for the second is 45px ...) so i suggest you to remove this css margin-left for your input and add this to your label:
label {
width:150px; /*Or however much space you need for the form’s labels*/
float:left;
}
this is a quick tutorial about how to align your fields
then after you submit your form with empty fields, rails will trigger some errors, and wrap each field (also label) which has error with div class="field_with_errors like this: <div class="field_with_errors"><input type="text"..... /></div> : this div will return your field to the next line so if you would that your fields still inline you just add this css to your css file :
.field_with_errors{
display: inline;
}
I found out through this thread how to fix this problem.