Mixing forms in Rails - ruby-on-rails

In my app users can register information about a tournament. If the user is a paying customer, she can fill in additional info, otherwise the fields for the additional info are unavailable (visible but disabled).
I laid this out as two objects: Tournament and TournamentExtras where the former has_one :tournament_extras and the latter belongs_to :tournament. Tournament also accepts_nested_attributes_for :tournament_extras.
I would like this to show up as a single form. The fields in the two objects are related, meaning you can add a start date (to Tournament) but only paying customers can add an end date (to TournamentExtras) but since the two fields are logically realted, they should show up after eachother in the form.
How do I do this?
I tried opening the form_for and fields_for loops withing eachother (before adding any fields) hoping that I could add any field whereever I like. That didn't work; apparently you can't reference the form object within the fields loop(?).

form_form and fields_for should work with each other.
Somewhat of a skeleton for this is:
form_for(#tournament) do |f|
f.fields_for(#tournament.tournament_extra) do |g|
end
end

Related

Rails dynamic & modifiable form schemas

I have a problem with forms. I'm developing an application that allows applicants to submit applications, and I want to make sure that users (people who receive those applications) could add/delete questions in their applications. How would I implement that?
My thought is to have a Application model and a ApplicationQuestion model that belongs_to Application. But I'm pretty lost beyond that.
UPDATE:
Another issue is confusing me. In my systems I could get Admins to add/delete ApplicationQuestions dynamically, and I have no problem implementing that. However, I want the Users to see the current set of ApplicationQuestions and answer them without being able to add/delete/edit questions. I'm now using a proxy model ApplicationQuestionAnswer which belongs_to Application and ApplciationQuestion. However I'm still having trouble with the views.
I'm actually more concerned about the views. How should I render the
forms to form-fillers?
Start with an Application form containing a stable amount of Questions (refer to Rails nested forms to implement it). The basic implementation could look like that:
<%= form_for #application do |f| %>
<%= f.fields_for :application_questions do |aq_form| %>
<%= aq_form.text_field :question %>
<%= aq_form.text_field :question %>
Dynamic adding and removing questions could be achieved by adding (and removing) HTML blocks produced by Rails helpers shown above:
<%= aq_form.text_field :question %>
You could dig into it's implementation by yourself (and write some javascript to orchestrate HTML blocks addition/removement) or use some gem with that functionality. One of those popular solutions is cocoon. It provides you with pretty links (link_to_add_association and link_to_remove_association), which being clicked perform the desired dynamic actions. You may be intrested in trying a cocoon demo application to get an idea of how to embed it into your project.
Yes you can keep the models as you are thinking. There will be a User model which would have two roles basically. One would be the teacher (just taking an example) who asks the questions and one would be student who will answer those questions. Now the teacher would be able to create application having different questions so two more models Application and ApplicationQuestion. The relations can be:
Application belongs_to User
ApplicationQuestion belongs to Application
Now the student will answer the questions which can be stored in answer table which would be another model ApplicationQuestionAnswer which would also belong to User and ApplicationQuestion. Now the questions can be of different type (numeric, string, date etc) so take care of that, they all can be stored in a single column by handling it at the code end and for this also have a question_type attribute in the ApplicationQuestion model.
Now the teacher can edit the application and delete the questions if they want or add new. Be sure to add dependent destroy so the answer would also be deleted when the question is deleted. Also suppose if the question can have choices then be sure to add one more model ApplicationQuestionChoice which would belong to ApplicationQuestion and its id will be saved as the answer of the student.
Update:
The ApplicationQuestionAnswer model will save each question's answer instead what you can do is that have two models. One would be ApplicationAnswer and another ApplicationQuestionAnswer. And the associations would be:
ApplicationAnswer belongs_to User
ApplicationAnswer belongs_to Application
ApplicationQuestionAnswer belongs_to ApplicationAnswer
Now in the view the form would be for creating/updating ApplicationAnswer and having nested forms for its associated ApplicationQuestionAnswer which you will display to the students. And this will save all the answers.
Also the same nested forms can be used with the teacher to add or remove questions. You can also use any gem for nested form too:
cocoon
nested_form
Hope this helps.

How do I create a fields_for a potentially unlimited number of children objects?

I'm making an application that involves booking appointments for users. I have a User model and an AvailableDate model. The user has_many availble_dates and the AvailableDate belongs_to user.
I want to present a form for the user so that they can mark a couple of dates in a calendar and each of the dates they mark will become an AvailableDate object tied to that user.
At the moment my solution is to do all the work that a form_for helper would normally do manually. This involves a lot of javascript and is generally just getting far too messy.
I can't figure out how I should make a form_for tag work when I need to create potentially infinitely many dates. In theory a user could keep marking off dates in the future as available. If I knew how many dates I needed to create for a user, I could do user.available_dates.build, N times. But this doesn't work here.
Can anyone help? It like this problem should be pretty common. Am I designing my application wrong?
One technique is to render the fields for your association once, outside the form.
When the user performs whatever interaction that should create a new set of inputs you use javascript to clone the initial set of fields and insert them into the form. The one thing you need to do is change the name of these inputs so that they are unique. Usually people use the current time in milliseconds for this unique identifier.
Been there & have found several resources to help: Tutorial & Cocoon
The bottom line is you need to ensure child_index is unique for each field. The tutorial I use has child_index: Time.now.to_i to create a truly unique id, consequently allowing you to add as many fields as you want
The best way to do this:
Render fields_for as a partial (passing your form builder object)
When you want to add new field, create ajax_field action
Make ajax_field view have its own form_builder
Both your original & ajax_field forms will call the partial
On front-end, you can use JS to GET new form action & append field to page
I can give you code if you want

How to edit accepts_nested_attributes_for?

I have a Company model that has_many Address(es) and many Phone(s).
Address(es) belong_to Company as do Phone(s).
My problem is that I don't understand how to edit a Company's particular Adress and Phone.
In my edit action, I call up the specific record and assign it to an instance variable (i.e. #address = some scoped searched for the specific address I want), and then in my fields_for I references this child's attributes, ex:
<%= f.fields_for :addresses, #address do |address| %>
A) I'm not sure if this is the way to do it. The documentation on how to access a parent's specific child for editing is sparse.
B) While this works fine if the update succeeds, when it fails and I render :edit the view presents additional fields with the parent's current child (the one I specified in my edit action + another child -- seemingly the next record in line).
So basically, my form is extended with two children when the render :edit is called. Weird.
What's the deal with this? How do nested attributes work? Is there a better way to manage forms with multiple associated models?
Thanks.
It sounds like you are using the fields_for helper outside of its purpose here. From what you've described, you want to edit an address outside of its parents relationship. If that's the case you'll edit that address in its own form (and likely its own controller) using the form_for helper.
The fields_for would be used if you wanted to do any CRUD operations in the same form as the parent thereby leveraging the accepts_nested_attributes_for functionality.
Defiantly watch the screen casts that dmarucco posted to get a good and general understanding of nested forms, then check out the problem I had before here with nested forms (I too had a many to many relationship):
Rails 3.1+ Nested Forms Issue: Can't mass-assign protected attributes
I posted my solution at the bottom of that page.
I also used nested_form and simple_form gems for formatting my nested forms, so you may want to look into that
Hope this sets you in the right path!
These screencasts could you help much more than thousands words.
Railscast - Lesson 1
Railscast - Lesson 2

How can I display data from a related model in a Rails form

I have two models
class Car
has_many :engines
end
class Engine
belongs_to :car
end
In the car form I have a select field where the user can select the engine type. The list might be "1.4L; 1.6L; 2.0L..."
Lets say I want to display additional information from the Engine model when the user selects a engine. This should be displayed on the Car form. This might be e.g. BHP, max revs, ...etc
How do I set something like this up. I guess there are two aspects:
How to display data from the engine
model on the car form without using
a field (this data is not editable).
How to update this data dynamically
when the user selects an option in
the select field.
Can anyone point me towards a starting point for this. I'm a bit lost where to begin.
Many thanks!
If you're working with collection_select in your form, you are setting two arguments, like :id and :name in your collection_select call. :id is the method called to determine the value for the option tag and :name is the method used to display the option tag's content.
Solution: Create a method (e.g. :name_for_select) in your engine model which returns a string with more information about your engine and call collection_select with :id, :name_for_select instead.
This is called a nested form, and if you google that you will find a lot of hints and tips. E.g. check out this and this tutorial.
You should also consider using a form builder like formtastic or simple_form: they have a lot of helpers to make life easier for you.

One controller with multiple models? Am I doing this correctly?

My web app, up until this point, has been fairly straight forward. I have Users, Contacts, Appointments and a few other things to manage. All of these are easy - it's just one model per section so I just did a scaffold for each, then modified the scaffolded code to fit my need. Pretty easy...
Unfortunately I am having a problem on this next section because I want the 'Financials' section of my app to be more in depth than the other sections which I simply scaffolded. For example, when the user clicks the 'Contacts' link on the navigation bar, it just shows a list of contacts, pretty straight forward and is in line with the scaffold. However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.
So the financials tab will basically work with data from two models: transactions and bank_accounts. I think I should make the models (transactions & bank_accounts) and then make a controller called Financials, then I can query the models from the Financials controller and display the pages in app/views/financials/
Am I correct in this app layout? I have never worked with more than the basics of scaffolding so I want to ensure I get this right!
Thank you!
If you are comfortable with scaffolding then i would suggest that you generate a scaffold for both
transactions: script/generate scaffold transaction financial_id:integer ...
bank_accounts: script/generate scaffold bank_account financial_id:integer ...
and financials script/generate scaffold financials ...
In your transactions model, add this:
class Transaction < ActiveRecord::Base
belongs_to :financial
end
In your bank_account model, add this:
class Bank_account < ActiveRecord::Base
belongs_to :financial
end
In your financial model, add this:
class Financial < ActiveRecord::Base
has_many :transactions
has_many :bank_accounts
end
Now from your financials controller you can use something like this:
def index
#financial = Financial.find(params[:id])
#This fetches all bank_accounts related to financial
#bank_accounts = #financial.bank_accounts
#This fetches all transactions related to financial
#transactions = #financial.transactions
end
In your views you can view all the bank accounts belonging to a particular financial by just doing this:
<% #bank_accounts.each do |bank_account| -%>
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
In your views you can view all the transactions belonging to a particular financial by adding something similar:
<% #transactions.each do |transaction| -%>
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
Remember, while creating a new transaction/bankaccount use the id belonging to the particular financial. Hope this helps. Cheers! :)
It sounds to me as if you want two views:
However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.the transactions on the right.
Keep in mind that one screen doesn't always mean one view in MVC. Conceptually, a view simply renders model data so the user can do something meaningful with it through controller actions. Rails muddies this up a bit by having you create a file in a views/ directory for each screen, so it's easy to think that one screen means one view. However, you can also call layouts within views in Rails, and these layouts are themselves views.
Putting transactions and accounts in separate views means each would have a controller to handle the user's interaction with each segment of data. The account controller, for example, would handle the event where the user selects an account to view additional information about an account. This would result in fetching transactions to show the user. The user could then interact with a transaction in an attempt to reconcile, void, or contest it. This would invoke logic in the transaction controller.
While this does a good job of removing the coupling from your controllers, this may seem like it couples the two views. However, the coupling is one-way: the account list includes logic specific to the transaction view (i.e. updating it when you select an account). The transaction view remains reusable in other areas of the application. For the most part, the account listing remains reusable as well, especially if you isolate it into a separate layout. The only difference you'd need for this specific screen is the handling of when a user clicks on an account.
If you merge everything into a financials view, it's still possible to keep things loosely-coupled, but it becomes more difficult to do so. The additional layer takes extra effort and may subconsciously guide you to merge everything up together.
Of course, either way works, and you could easily argue that the final design is an exercise that is best left to the developer's preference. I simply offer my interpretation of how one would most-accurately stick to the patters Rails attempts to guide developers to using.
I don't have a huge amount of experience with Ruby on Rails, or MVC work in general, but I think you're going about it the right way. You generally want a model object (and by extension, database table) for data-oriented resources... That is, your models should correspond to your "nouns". Your controller's actions are your "verbs", and to me it makes perfect sense to categorize them how you like.
In this case, I would say that putting a bunch of "verbs" under financials, which interact with two model object types as needed, makes sense IF AND ONLY IF it makes sense to you for you to organize it like this. (Bear in mind that you can also evaluate how well organized and intuitive the corresponding URLs feel to you, though I'm sure most MVC purists would add that you should never just rely on examining the URLs!)
So in summary, I think you're doing everything right here, as long as it makes sense to you. Yes, you'll have to write your views and controller actions yourself, though you can start using ruby script/generate controller financials action1 action2 [...]; that at least gives you skeletons of the view files and empty controller actions.

Resources