Form Design: How to best display/add different statuses? - ruby-on-rails

I am working on a rails form. Essentially, a person can have multiple statuses and switch between the different statuses. In database table, the display will be simple as follows:
status start_date end_date
work 1/1/15 1/10/15
sick 1/11/15 2/15/15
work 2/16/15 3/15/15
sick 1/15/15 1/14/15
I need to prompt user to input these information. I have made a status class which belongs to a person class. So basically, these fields will be a part of nested forms.
My question is: How can I dynamically display these information to make forms elegant and clean to use?
Thanks!

If I understood your domain, your Person has many Status, right?
The simplest way to do it is use the gem cocoon. Your view will look like this:
<%= form_for #person do |person_form| %>
<%= person_form.input :name %>
<%= person_form.fields_for :statuses do |status_form| %>
<%= status_form.field :start_date, :end_date %>
<!-- cocoon's method to dynamically add nested forms -->
<%= link_to_add_association 'add status', person_form, :statuses
<% end %>
<% end %>

Related

How to display only unchecked items in active admin for has many relationship?

I am using Ruby on Rails 5 with active admin as a backend for resources management. I need to show the only unchecked items for a check_boxes field all the time in new and edit action. Instead of running a complex query for the collection I think this is the best way to manage. All of my associated models stuff related to this are working fine.
It should show only 2nd item if it is not checked already.
Right now my code snippet is
f.input :subscribers, :as => :check_boxes, :collection => Subscriber.all.collect {|subscriber| [subscriber.email, subscriber.id]}
Is there any way in active admin to display only unchecked values ?
Have you considered using collection_check_boxes for this case?
It would look something like this:
<%= f.collection_check_boxes(:subscribers_ids, Subscriber.all, :id, :email) do |b| %>
<% if !b.check_box.include?(checked="checked") %>
<%= b.label %>
<%= b.check_box %>
<% end %>
<% end %>
I think that should solve your problem. If you want to learn more about collection_check_boxes

Ruby on Rails: assign relationship on creation

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.

Update two non-associated models with one submit button, using nested form

I have a User controller that possesses the two following records:
user.vehicles from VehiclesController
user.trip_distances from TripDistancesController
In the Vehicles view, I need to display and update both models (Vehicle and TripDistance)
I figured out that I need to use a nested form. However, I don't understand how to implement it since my #trip_distances record needs a loop in order to display all of its content.
First, I tried to put this in the Vehicle model, but since both are NOT linked, I wonder if this is correct:
accepts_nested_attributes_for :trip_distances, allow_destroy: true
Then, in the Vehicle view's _form file, during the edit action, I have:
<%= form_for(#vehicle) do |f| %>
I need to put my nested form inside it. Here's what I got so far:
<% #trip_distances.each do |t| %>
<%= f.fields_for t do |fields| %>
<div class="field">
<%= fields.text_field :id_contract %>
<%= fields.number_field :length %>
</div>
<% end %>
<% end -%>
My trip_distances records are correctly displayed, but the unique Submit button doesn't work anymore. I'd like to update both model while clicking on one button. What am I missing?
I was able to make it work by associating the two models. Then I use the following fields_for tag:
<%= f.fields_for :trip_distances do |builder| %>
My submit button is now working, but the :trip_distances fields are not updated, I don't know why.

Rails - Multiple forms, different Models (Objects), one submit button

I have a view with 3 forms, Schedules, Workouts and Exercises, all behaving like an edit form, each. And one submit(save) button in the all the view.
When I click on the save button. Every data changed on those forms should be updated after click.
What is the best solution for this ? Javascript updating each data separated ? How to do that ? Is there a more Rails way to do this easily ?
My difficulty is how to integrated all those models in one view, while all this is happening in the show(view) from the Student model.
If you're implementing something like a profile / edit page (where you can save all the records at once), the two ways I would look at would either be to save the forms via Ajax, or use a single submit method to handle them
Ajax
The ajax method would be the most conventional:
Every form you submit will go to the form's own update method in the backend
Each form could be handled by a single button, but it's best to split them up
#app/controllers/profile_controller.rb
def edit
#schedules = Schedule.all #-> not sure how many records you're using
#workouts = Workout.all
#exercises = Exercise.all
end
#app/views/profile/edit.html.erb
<%= form_for #schedule do |f| %>
<%= f.text_field :test %>
<% end %>
# -> other forms
<%= button_to "Save", "#", id: "save" %>
#app/assets/javascripts/application.js
$("#save").on("click", function() {
$("form").submit(); // we'll have to define the form to submit
});
Single
If you submit all the forms as one, you'll have to encase them all in a single form, as sending different errors. This could be achieved by using _, and handled in the backend by looping through the different params, saving each one individually.
I'd do this:
#app/controllers/application_controller.rb
def submit
types = %w(schedules exercises workouts)
for type in types do
type.constantize.update_attributes()
end
end
This allows you to create a form with the different data types submitted in the same action:
#app/views/profile/edit.html.erb
<%= form_tag profile_submit_path do %>
<%= fields_for #schedules do |f| %>
<%= f.text_field :title %>
<% end %>
# -> fields_for for the other objects
<% end %>
This will allow you to send the updated objects to your controller, allowing them to submit
If all of your models (Schedules, Workouts and Exercises) are associated, using fields_for should be a good option.
From the above link:
<%= form_for #person do |person_form| %>
First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>
<%= fields_for :permission, #person.permission do |permission_fields| %>
Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<%= f.submit %>
<% end %>
Read the guides.
You could have some simple javascript that iterates over all form tags and submits each of them.
Alternatively, if you are going to use javascript anyways, you could follow an AJAXish auto-save approach upon changing any field.
But I think it might be cleaner if you just had one form for multiple models, using fields_for.

passing dynamic params field

Im working with Rails 3.0.3
I want to create bills in my App. Each Bill has many entries (Material, how much of that and the Price)
The Problem i have, is that i want to write the bill and the entries and then save both at the same time. So when you click on save Bill, the Bill + each Entry should be created (saved in the db).
I can write the bill + each entry (with javascript), but i dont know how i could save both of them. Right now i can only save the bill it selft. Is it possible to pass a dynamic field via params so i can handle that in the bills controller to save? How would you implement this?
What you are looking for is called nested form, you have a main form for your bill and multiple forms that are dynamically generated as children of this general form using fields_for like this:
<% form_for #bill do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :entry do |builder| %>
<%= render "entry", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
Of course you will need some js for the dynamic creation of the different entries, here you have a couple of railscasts that will be helpfull.
Nested model form Part 1
Nested model form Part 2

Resources