I'm doing backend for trello-clone app using Rails. I have board entity which has many column entities which are having many cards entities. I've made board-column part (using blog app example) and it works fine, but I can't understand how to make column-card part of that.
Method create in card controller is like:
def create
#board = Board.find(params[:board_id])
#column = #board.columns.find(params[:column_id])
#card = #column.cards.create(card_params)
end
I've made the form like this for adding cards for each column :
Form code:
<p>
<strong>Name:</strong>
<%= #column.name %>
</p>
<p>
<strong>Color:</strong>
<%= #column.background_color %>
</p>
<h2>Add a card:</h2>
<%= form_with(model: [#board, #column, #column.cards.build], local: true) do |form| %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
So my question is how to make normal entity adding for the second level of the many-to-many relationship?
Your question is quite ambiguous but I guess you probably are confused about this thing:
Add hidden_field for board_id like:
form.hidden_field :board_id, value: #board.id
Then access it in the controller update/create with something like params[:column][:board_id] (check params to be exact) and relate the column with board id.
You surely are missing this piece but you surely are missing more.
I would suggest using byebug gem and inspecting params hash. It will help you a lot because I think you need to inspect params which are confusing you.
Another way (not good but simpler) to
puts "*"*100
puts params
puts "*"*100
as first line of create action in controller to see how exactly your params are. (Go into console and find whatever is written between 2 lines of asterisks in your server console)
Good Luck!
Related
I am using the cocoon gem to try and achieve adding an object which belongs to another with nested fields. I have a 'user_resolution' which has many 'milestones'. I have set up the associations accordingly in both of these models. For some reason, milestones are failing to be created, however if I add one manually in the database I can successfully update it. I am able to dynamically add the fields and remove them using the cocoon gem but that is all. When I click 'add milestone' it redirects me to the show view of the user resolution and throws the success message saying user resolution has been updated, no errors are thrown but the milestone(s) is/are not created.
user_resolution.rb
has_many :milestones
accepts_nested_attributes_for :milestones, reject_if: :all_blank, allow_destroy: true
milestone.rb
belongs_to :user_resolution
I have set up the nested form within the edit view as for now I only want users to add a milestone to a resolution in the edit view.
user_resolutions/edit.html.erb
<%= form_for(#user_resolution) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%= f.fields_for :milestones do |milestone| %>
<%= render 'milestone_fields', f: milestone %>
<% end %>
<%= link_to_add_association 'Add Milestone', f, :milestones %>
<%= f.submit "Add Milestone" %>
<% end %>
_milestone_fields.html.erb
<div class="nested-fields">
<div class="field-row">
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div class="field-row">
<%= f.label :description, 'Name' %>
<%= f.text_area :description %>
</div>
<div class="field-row">
<%= f.label :severity, 'severity' %>
<%= f.check_box :severity %>
</div>
<div class="field-row">
<%= f.label :target_date, 'target_date' %>
<%= f.date_select :target_date %>
</div>
<%= link_to_remove_association 'Remove', f %>
</div>
The permitted parameters within the user resolutions controller also contain the following
milestones_attributes: [:id, :user_resolution_id, :name, :description, :target_date, :severity, :complete, :_destroy]
The milestones themselves have no views, they only have a model and a controller. The controller create action (which i'm unsure is required for nested forms) contains the standard following code
def create
#milestone = Milestone.new(milestone_params)
if #milestone.save
redirect_to user_resolutions_path,
:flash => { :success => "You successfully created a milestone" }
else
redirect_to new_milestone_path,
:flash => { :error => "Oops something went wrong. Try again." }
end
end
I've been as informative as I can but if you need anything else let me know. Thanks guys.
which i'm unsure is required for nested forms
You don't need a create action for milestones - they'll be populated from the user_resolutions#create controller action.
There are several things to look at with this. I'll detail some here. This won't be a specific answer, but may help point you in the right direction.
Firstly, you need to make sure you're receiving the correct params.
Cocoon does a great job building the nested form - you need to make sure it's obliging Rails' nested attribute structure.
To do this, you should right-click > view source.
In the f.fields_for section (it won't be called that in the HTML), you'll be looking for the equivalent to the following:
<input type="text" name="milestones_attributes[0][name]" value="">
The important thing to note is the name...
Each time you use a form, or any Rails view helper for that matter, you're really just building standard HTML. form_for just creates an HTML form, and thus any params contained within it need to adhere to a certain structure for Rails to recognize the params.
The f.fields_for elements will typically be called x_attributes[:id][:param] - this is passed to Rails, which cycles through each [:id] to determine the number of nested params to add.
You need to check the source for the above naming structure. If you see it, that's good. If not, it means you haven't built your form properly.
Secondly, you need to make sure your objects are being built in the controller.
I'm not sure how Cocoon does this, but essentially, each time you use f.fields_for, you have to build the associated object before:
def new
#user_reservation = UserReservation.new
#user_reservation.milestones.build #-> this is what makes f.fields_for work
end
If the first step shows incorrect element naming, it means your associative objects are not being built (which is why they're not being recognized).
To test it, you should build the associative objects in the new method, before sending.
Finally, you'll want to post your params.
These tell you in explicit detail what Rails is doing with the nested attributes, allowing you to determine what's happening with them.
Sorry for the long-winded answer. You'll not have received any answers anyway, so I felt it prudent to give you something.
I'm new to Ruby on Rails. There are two models in my project: room and guest. The association is "room has_many guests" and "guest belongs to room".
I have separated views for manage rooms and guests. Rooms don't require "guests" value on creation. However, I want to create new guests and assign it to certain room at the same time. What will be the proper way to do it? How do I transfer the input from web and match the entities in database.
The code is pretty much the same as "Getting Started with Rails". In the tutorial, they add "comments" in the "article" view and use "comment" as a sub-resource of "article". In my case, I treat the two models equally and want to manage them in separated views.
Update:
I used the collection_select and try to work with my guest_controller.
<%= form_for :guest, url: guests_path do |f| %>
<% if #guest.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#guest.errors.count, "error") %> prohibited this guest from being added:
</h2>
<ul>
<% #guest.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :phone %><br>
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :room%><br>
<%= f.text_field :room %>
</p>
<p>
<%= f.label :room %><br>
<%= f.collection_select(:room_id, Room.all, :id, :title) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', guests_path %>
In my guest_controller, the create method called by the form above is :
def create
#guest = Guest.new(guest_params)
#guest.room = Room.find(params[:room_id])
if #guest.save
redirect_to #guest
else
render 'new'
end
end
However, when I create a new guest, it shows that:
ActiveRecord::RecordNotFound in GuestsController#create
Couldn't find Room with 'id'=
I checked that room_id=4 and Room.find(4) return the proper room.
What's wrong?
If you want to select one room from those that exist, use collection_select form helper, here is a relevant snippet from the docs:
f.collection_select(:city_id, City.all, :id, :name)
This outputs a dropdown list that:
fills in city_id parameter in this context
uses City.all for filling in the options in the list (I will be referring to "each" city as city)
uses city.id as data (that gets sent in the form)
shows city.name for each city in the dropdown list (hopefully, human-readable)
Bear in mind though, that in terms of security it's like "look, you can select this, and this and this!", that does not prevent users from selecting an unlisted option: either by modifying form markup by hand or sending handcrafted queries.
So should you ever be limiting access to specific rooms, and list only Room.unlocked (unlocked assumed a scope), make sure the received room_id refers to a room from that scope as well. Most of these problems are dealt with using either validations or careful association management (Room.unlocked.find_by_id(:room_id) that outputs nil if the room is not in that scope).
UPD: as for the latest problem you're having -- your understanding on how the form contents look in params seems to be wrong. It's quite a common misconception actually.
form_for :guest will construct a separate object/hash in params[:guest], with all the form's fields inside it. So it actually is inside params[:guest][:room_id], but no, don't rush with adding the missing part.
You've already built a #guest object from entire params[:guest], so if the room actually exists, it's inside #guest.room already and can be validated inside the model during save. Have a look at Rails validators.
Take a look at the fields_for tag:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
It allows just that, to create a guest while creating a room and associating each other.
In rails is there any simple way to implement select or create from view.
Eg:
Product has_many(or has_one) Tags.
While creating new Product I can select existing tags or create new one.
This can be done by using JavaScript and other ways are there.. But all will take more time and effort.
Please share if you know other simple way...
Edit:
Something like this.
But imagine you have 100 tags or more ! your page will look bad with 100 checkbox or more..., one elegant way to do this is by using a jQuery plugin called jQuery Tokeninput i use it in my project and it's very helpful for what do you want, you can find the plugin Here
This is a screencast on how to use it : Token fields
and this is the revised version : Token Fields (revised)
check also this blog post about the same plugin if you want too How to create a token input field where the user can also add new items
cheer
Yep.
You are after nested forms. Try, https://github.com/ryanb/nested_form
For example,
<% form_for #product do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :tags do |tag| %>
<p>
<%= tag.label :content, "Tag Name" %><br />
<%= tag.check_box :content %>
</p>
<%= tag.link_to_remove "Remove this tag" %>
<% end %>
<%= tag.link_to_add "Add new tag" %>
<p><%= f.submit "Submit" %></p>
<% end %>
Setup the controller and model as given in the documentation and try the above code in the view. This railscast will help you a lot in figuring nested forms http://railscasts.com/episodes/197-nested-model-form-part-2
I have a Client model that can have many Projects.
These are my view files:
edit.html.erb
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f %>
<%= f.submit Create %>
<% end %>
_fields.html.erb
<div>
<%= f.label :name %><br/>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :client_id %><br/>
<%= f.select(:client_id, current_user.client_names) %>
</div>
This is easy and works great.
But is there a way to create a Project from within a Client view as well?
For example on the client edit page it would be nice to have a link New Project for this Client that leads to the above New Project form, but with the respective client preselected in the select box.
Can this be done somehow?
I can't seem to find a way to pass the Client ID to the New Project form.
Thanks for any help!
This is certainly possible, but requires passing the client id to the new_projects_path. This can be done by nesting the routes to projects inside of clients, or just appending the client id. The difference would be URLs that look like the following:
/clients/1/projects/new
- or -
/projects/new?client_id=1
In your projects controller, you should be able to instantiate the #project variable with a preselected client:
#project = Project.new :client_id => params[:client_id]
Yes, you can do this. You're going to use accepts_nested_attributes_for :project in your Client model. Nesting this will allow you to do something like
<%= f.fields_for :project do |p| %>
<fieldset>
<%= p.text_area :content %>
</fieldset>
<% end %>
Check out the railscasts episode #196 on Nested Model Form.
I am creating a very simple book review site and it needs the ability to allow the user to add little comments about a book. Now I have my two tables, one for the book and one for the comments and now need a way to transfer data between the two because i find the way rails handles things quite puzzling.
So my book model contains "has_many :comments"
and me comment model has: "belongs_to :book"
the view i am using to both view and add comments is "/views/book/viewbook.html.erb"
this shows the book and all its details, at the bottom is a section where the user can add their own comments, it looks like this:
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
now i know this cannot work because that above ":title" and ":body" would be in the book model but i need them to be send to the comment model because these are in the comment DB. How do i pass this data to the comment database. I've tried ":comment.title" and other various things but still cannot workout how to pass this data.
Any thoughts would be greatly appreciated.
(I apologize if this question is very stupid or has not been explained to well, my lecturer set this assignment and rails is not a language i have ever used.)
You define what the form is for in the opening form tag:
<% form_for :comment do |form| %>
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
<%= end %>
The idea is that the form is an empty comment object. Controllers communicate between models and views, so your controller should have an action to process the form that knows to save the comment object into the comment model. You'll also want to specify which book the comment is for. There are a lot of ways to handle this (hidden fields, nested RESTful resources, etc.).
Maybe start here: http://guides.rubyonrails.org/action_controller_overview.html
It sounds like you need to use nested object forms. This is a new feature in Rails 2.3:
http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
So i have modified it using your great suggestions and now only one problem has come-up. Whilst i can now successfully store the comments title, body, time and the person who left the comment to the DB i still cant work out how to use store the book.id. Here is the view:
<% form_for :comment, :url => { :action => "addcomment" } do |form| %>
<%= form.hidden_field :user_id, :value => session[:user_id] %>
<%= form.hidden_field :book_id, :value => #book.id %> <!-- WONT WORK -->
<%= form.label "Title" %><%= form.text_field :title %><br />
<%= form.label "Comment" %><%= form.text_area :comment %>
<%= submit_tag "addComment", :class => "submit" %>
<% end %>
Here is my controller that can now successfully store the details, apart from the book.id
def addcomment
#newcomment = Comment.new(params[:comment])
if #newcomment.save
flash[:notice] = "Comment Was Added To The System."
redirect_to :action => "index"
end
end
i though that "#book.id" would work because in that view i am also showing the books details using things like "#book.title" and "#book.authour" and that seems to work, but not for the ID field though.
So can now successfully post comments and store them with the correct details, now im on to displaying a list of comments of that particular book using the "book_id" value. Here is what i thought would work, i also have code like this in the search part of my app so i thought it would well:
def view
#book = Book.find(params[:id])
#reviews = Comment.find_by_book_id(#book.id)
end
With the corresponding view:
<% if #reviews %>
<% for review in #reviews %>
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
<% end %>
Now that should get the comments that have the "book_id" of the book i am viewing and the display each one using the for loop. Doesnt quite work though, it spits out an error message saying the following:
#undefined method `each' for #<Comment:0xb682c2f4>
#Extracted source (around line #27)
And line 27 is
<% for review in #reviews %>
To find the comments for a book it's just:
#book = Book.find(params[:id])
like you've already done, then:
#book.comments
for that books comments. So:
<% for review in #book.comments %
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
You don't have to find the comments in the controller.