How to prefill nested form fields - ruby-on-rails

I have a simple nested form using the 'nested_form' gem.
It looks like this:
<%= nested_form_for #user do |f| %>
<%= f.text_field :username, :size => 25 %>
<%= f.fields_for :teams do |team_form| %>
<%= team_form.label :team_name, 'Name of your team' %>
<% end %>
<%= f.submit :value =>'submit' %>
<% end %>
Now I want to prefill the fields in my new-action. While it's easy to fill the username-field with #user.username = "someone" I have no idea how to access the first nested field "team_name" in the nested "team_form".
In the html the field looks like this:
<input id="user_teams_attributes_0_team_name" type="text" name="user[teams_attributes][0][team_name]">
Any ideas how to prefill this nested field?

Typically build can be used for this in your controller (as it doesn't cause a save on the #user object), appending new Team instances to the :teams collection on the #user object. In your action
#user = User.new
#user.teams = [ Team.build(...) ]
where ... contains the default attributes for #user.teams.first that will be displayed in the nested form.

Related

Rails, use a form field to set something on the user registration (devise)

I would really like to add a form field "invite_code" to the user sign up form, but I don't know how to add the invite_code to the controller so that the application knows how to look for it?
The form in the sign up on the template would read:
<% form_for User.new do |f| %>
<span>Email:</span> <% f.email %><br>
<span>Name:</span> <% f.name %><br>
<span>Invite Code:</span> <% f.invite_code %><br>
<% end %>
The "invite_code" isn't part of the database or anything, but in the user registration model, I want to put a:
before_save :invite_promo
def invite_promo
if #invite_code.present? && #invite_code == "special_person"
self.special_key = true
end
end
Is there an easy way to look for form fields in the template using the model or controller?
So sorry...I'm new to Rails. Thank you so much in advance!
You need to define a virtual attribute invite_code in User model:
attr_accessor :invite_code
Your form should look as follows:
<%= form_for User.new do |f| %>
<span>Email:</span> <%= f.email_field :email %><br>
<span>Name:</span> <%= f.text_field :name %><br>
<span>Invite Code:</span> <%= f.text_field :invite_code %><br>
<% end %>
if you don't want to create a field inside your table, you can use hidden field in view like, <%= hidden_field_tag :invite_code, 'code' %>

Ruby on Rails access whole params hash

I have an application that deals with user rotas - and I'm currently adding the ability for admin approvals. If the user updates their own rota the params hash looks something like:
Parameters: {id:1, role_id: 1, team_id:1, rota: [startDate: 01/01/2014, endDate:02/02/2014]}
and these are submitted using a form with:
<%= form_for [#team,#role,#rota] do |f| %>
form code
<% end %>
We need to access the attributes outside the rota: object but currently can't find a way to as:
params.require requires you to pass an object in.
My team members have decided to add hidden fields to submit the attributes within the rota object but that seems redundant seeing as they are quite clearly there, we just can't find a way to access them, and ideas?
I was talking about something like
def user_rota_params
params.require(:user)
.permit(:role_id, :team_id, :rota => [:startDate, :endDate])
end
Then, for your nested attributes you could use fields_for.
Your form, thus, should look something this (omitting labels and keeping it basic):
<%= form_for #user do |f| %>
<%= f.text_field :role_id %>
<%= f.text_field :team_id %>
<%= f.fields_for :rota do |ff| %>
<%= ff.date_field :startDate %>
<%= ff.date_field :endDate %>
<% end %>
<% end %>

How to submit fields other than model fields

I need to write a view that returns the model plus additional fields that is not part of the model.
In the controller I should access these like:
def create
#post = Post.new(params[:post])
#stuff = params[:stuff]
How can I write the view?
you can use fields_for helper
form_for #post do |f|
f.text_field :title
fields_for :stuff do |stuff_form|
stuff_form.text_field :name
end
end
remember, its simply fields_for not f.fields_for. if you use f.fields_for it will give the stuff_form fields as part of your post object params
Use form helpers, thus:
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body, size: "60x12" %>
<%= text_field_tag :stuff %>
<%= f.submit "Create" %>
<% end %>
For most types of input field, there's both the f.field_type and field_type_tag helpers. The former are for fields that are part of the model, whereas the latter are simply additional fields that will be present in the params. So the above would give you a params hash like this:
{:post => {:title => 'the title', :body => 'the contents of the post'}, :stuff => 'contents of stuff field'}
You can add field tags that are not part of your form which will add to the params hash.
in haml...
= text_field_tag :stuff, "what you want in stuff"
= hidden_field_tag :stuff, "or if you wanted a hidden field to show stuff"
will result in params hash...
stuff=>"what you want in stuff"

RoR : Mongoid and form create hash

Simple question for Rails gurus. Why I do have to use the following statement to insert a new Mongoid document : params[:video][:description] in the following create method of my VideosController? Why I can't use the params[:description] from the POST form? If I use it, the value becomes nil.
def create
#video = Video.new(
:title => params[:video][:title],
:description => params[:video][:description]
)
if #video.save
render 'success'
else
render 'error'
end
end
Here is the Video.rb class :
class Video
include Mongoid::Document
field :title, type: String
field :description, type: String
validates_presence_of :title
validates_presence_of :description
acts_as_url :title
end
And finaly the form view :
<%= form_for #video do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<p/>
<%= f.label :description %>
<%= f.text_field :description %>
<%= submit_tag("Enqueue video") %>
<% end %>
I don't quite get why the form input are video[description] and not just description as expected :
<label for="video_title">Title</label>
<input id="video_title" name="video[title]" type="text" />
<p/>
<label for="video_description">Description</label>
<input id="video_description" name="video[description]" type="text" />
When you are using form_for:
Creates a form that allows the user to create or update the attributes
of a specific model object.
In your case, Video model. To understand Rails convention:
<%= form_for #video do |f| %>
...
<%= f.text_field :description %>
...
Which results in an html tag whose name attribute is video[description]. This means that when the form is submitted, the value entered by the user will be available in the controller as params[:video][:description].
The params variable is an instace of ActiveSupport::HashWithIndifferentAccess, like a Hash with a small difference, according to documentation:
This class has dubious semantics and we only have it so that people
can write params[:key] instead of params[‘key’] and they get the same
value for both keys.
Your params is something like:
{"utf8"=>"✓",
"_method"=>"post",
"authenticity_token"=>"xxx",
"video"=>
{"description"=>"Video desc"}
}
Where "video" or :video is one of the keys of the Hash. So, params[:video] is equivalent to params.fetch("video") which value is {"description"=>"Video desc"}. As you can see the value is another Hash. Finally to get the value of the description you have to params[:video][:description] (The Rails way) or params.fetch("video").fetch("description"), which value is "Video desc".
fetch is a Ruby method of Hash: "Returns a value from the hash for the given key."
Taking this into account:
Video.new(params[:video]) = Video.new(:description => "Video desc") = Video.new(:description => params[:video][:description])
It's easier to use conventions, but for sure you can have params[:description] (just in case):
<%= form_for #video do |f| %>
...
<%= text_field_tag :description %>
...
Note that I'm using text_field_tag instead of f.text_field. In this case the html tag name will be description in the params Hash you will receive { "description" => 'Video desc" }...
Take a look to Rails API documentation to understand different helpers, etc. And also review your server's log.
If you want to use video[:description]. Create your form like this
<%= form_for #video do |f| %>
....
<p/>
<%= f.label :description %>
<%= f.text_field :description, :name => "description" %>
....
<% end %>
Rails form_for helper name the input elements such that it becomes easy to push them into model attributes in one go like this
#video = Video.new(params[:video])
so that you don't have to do it like the way you have done
#video = Video.new(
:title => params[:video][:title],
:description => params[:video][:description]
)

Multiple forms for the same model in a single page

On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:
alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png
Here's the partial I use to generate this:
<div class="stand_alone annotation" data-id="<%= annotation.id %>">
<%= song_link(annotation.song, :class => :title) %>
<span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
<% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
<%= f.hidden_field :annotation_id %>
<%= f.hidden_field :created_by_id %>
<p style="margin-top: 1em">
<%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
</p>
<p>
<% if current_user %>
<%= f.hidden_field :email_address %>
<% else %>
<%= f.text_field :email_address, :example_text => "Your email address" %>
<% end %>
<%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
</p>
<% end %>
</div>
However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)
Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?
If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:
<%= form_for Feedback.new(...),
id: "annotation_#{annotation.id}_feedback"
namespace: "annotation_#{annotation.id}" do |f| %>
That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.
Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.
The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.
class Annotation < ActiveRecord::Base
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :annotation
end
You could then add as many forms as you want, one for each annotation. For example, this is what I tried:
<% form_for #a do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
<% form_for #b do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
And the quick and dirty controller for the above edit view:
class AnnotationsController < ApplicationController
def edit
#a = Annotation.find(1)
#a.feedbacks.build
#b = Annotation.find(2)
#b.feedbacks.build
end
def update
#annotation = Annotation.find(params[:id])
#annotation.update_attributes(params[:annotation])
#annotation.save!
render :index
end
end
I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):
<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
<%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
<%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>
I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.
Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)
I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.
If the first form is using
#address = Address.new
then
create_class('AddressNew', Address)
#address_new = AddressNew.new
Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.
For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:
Rails: Using form_for multiple times (DOM ids)

Resources