I have a Issues Controller and a nested Comments Controller. My Comments form is in my Issues Show View.
I'm trying to add a checkbox when commenting that would toggle the :closed => boolean attribute on my Issue.
<%= form_for #issue.comments.build, url: script_issue_comments_path(#script, #issue) do |f| %>
<!-- Form , etc-->
<%= form_for [#script, #issue] do |f| %>
<%= f.check_box :closed %> Mark as Closed
<% end %>
<% end %>
That obviously didn't work. How can achieve this ?
Basically i will put the checkbox right next to the submit button of the Comment.. So if selected, the Issue attribute :closed will be set to True
The inner form you have there isn't a nested form (in rails terms), and as you've found won't behave in the way you're wanting because it's not really tied to the submission of the outer form.
One approach you could do, which would keep the comments controller from having responsibility of editing issues at the same time (which would be messy in my opinion), would be to have something like a comment_closes_issue attribute on the comment. Then, when you save the comment, if comment_closes_issue is true, then update the parent issue... It's still blurring the lines between the two models a little, but if they're closely tied, then perhaps that's ok in your application.
Edit - here's an example of how to handle that, assuming a Comment belongs to an Issue:
class Comment < ActiveRecord::Base
belongs_to :issue
after_save :close_parent_issue, if: :comment_closes_issue
private
def close_parent_issue
issue.update_attributes closed: true
end
end
Then in your view, you can just have <%= f.checkbox :comment_closes_issue %>...
Related
I have a edit form that prepopulates with the current values. Its a custom edit screen (not the default one that rails uses) and what Im using it for is for users to submit changes that will get voted on and might eventually get applied to the record. However, in the time it takes to be voted on something else might have changed and I dont want to overwrite the changes if they didnt submit a change.
EDIT: Changing to my more specific case so hopefully answers will work for it...
I have the following tables: Recipes, RecipeIngredients, RecipeSteps, RecipeChanges. On the show view of my recipes it displays all the ingredients/steps and there is a tab that then changes just the ingredients/steps to forms as to allow the user to submit changes. I dont want these changes applied though. Instead Im trying to create a voting system where people can vote on them. So what I have decided on is to convert the parameters from the form into a json string and save it in the RecipeChanges table under a single column (instead of using two table for ingredient changes and step changes). Heres the code for the form (html removed to make it easier to see the rails stuff):
<%= form_for #recipe, url: recipe_recipe_changes_path(#recipe), html: {method: "post"}, remote: true do |f| %>
<%= f.fields_for :recipe_ingredients, f.object.recipe_ingredients.order(:order) do |ff| %>
<%= ff.hidden_field :order, class: "position" %>
<%= ff.text_field :ingredient, placeholder: "Add Ingredient (e.g. 3 cups cooked rice)" %>
<label><%= ff.check_box :_destroy %>Remove</label>
<% end %>
<%= f.fields_for :recipe_steps do |ff| %>
<%= ff.hidden_field :order, class: "position"%>
<%= ff.text_area :step %>
<label><%= ff.check_box :_destroy %>Remove</label>
<% end %>
<%= submit_tag "Submit", class: "button" %>
<% end %>
So this sends a recipe object to my RecipeChange controller and there I handle the params to save them as the json string like so:
def create
#change = RecipeChange.new
#change.recipe_id = params[:recipe_id]
#change.new_recipe = recipe_change_params.to_json
#if #change.save
#add alert for successfully adding
#else
# add code for error handling
#end
end
This works like I want except for it saves all the ingredients/steps and I would like to only save what they have changed. I had two thoughts on how to do this but not sure how to accomplish it.
Check if the fields have changed when they click the submit button and only send the ones that have been edited (not sure if possible)
In the controller grab the original recipe (I have the id so that would be easy) and loop through the ingredients/steps and compare them and remove any that are identical....this is the method I think would be better but not sure how to loop through the hashes to accomplish this
Have a look at ActiveModel::Dirty. http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed
You can do something like:
changes = bag.changed_attributes and get a hash of that attributes that changed, and then save those with bag.update_attributes(changes), for example.
This is a bit old now but I've come across the same or similar scenario and wanted to share for others.
In my case I populate some nested form fields based on an existing object in my #new action. However, in my #create action I did not want to save these nested form params unless they were actually modified compared to the original existing object.
In this case, ActiveModel::Dirty would always be true as it would compare [nil, "value"].
I first tried to modify the params in my #create action and compare them to the original existing object similar to this discussion but this got messy and felt wrong.
I ended up saving all records then doing a cleanup with an instance method in my model that I call after save in my controller's #create action. Still feels a bit dirty but it's working.
Example:
# controllers/changes_controller.rb
# ChangeController#create
def create
# ... shortened for example ...
if #instance.save
#instance.remove_clean_changes
format.html
end
end
# models/change.rb
# Change#remove_clean_changes
# Loop over all original objects and compare the necessary attributes
# to the changes. If they match, they are clean and should be deleted.
def remove_clean_changes
original_objects = self.original_objects
changes = self.changes
original_objects.each do |original_object|
changes.each do |change|
change.destroy if (change.attribute_one == original_object.attribute_one &&
change.original_object_id == original_object.id)
end
end
end
This question is regarding Rails 4/postgresql and the app is hosted on Heroku.
I am making a Quiz-functionality on a website and I am wondering on how to implement the forms (using Formtastic) best to make this is easy as possible. I have three models:
Quiz (has_many :quiz_questions), e.g. "Test to see how awesome you are"
QuizQuestion(belongs_to :quiz, has_many :quiz_options). e.g. "1. Which is your favorite color")
QuizOption (belongs_to :quiz_question). e.g. "Blue"
I have set up the forms like this:
<%= semantic_form_for([:admin, #quiz], :url => admin_quiz_path(#quiz.id)) do |f| %>
<%= render 'form' , :f => f %>
<% end %>
where the form looks like this:
<%= f.inputs %>
<h3>Quiz questions</h3>
<%= f.semantic_fields_for :quiz_questions do |qq_f| %>
<%= qq_f.inputs %>
<h4>Quiz options</h4>
<%= qq_f.semantic_fields_for :quiz_options do |qqo_f| %>
<%= qqo_f.inputs %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit %>
or go <%= link_to 'back', admin_quizzes_path %>
<% end %>
It seems, however, not to be working the way I want. I expect to be able to see the fields of QuizQuestion and QuizOptions in this form (there are objects for those) but I don't.
More importantly is that I would like to be able to create a New QuizQuestion and subsequently QuizOption in this form. It doesn't necessarily have to be jQuery/ajax or anything but I would like to do it all from this form.
Basically, I would like my workflow to be like:
Create a Quiz and add values to it. Click Create.
Add QuizQuestion number one and add the values to it (like "name label"). Click Create.
Add QuizOption related to QuizQuestion number one, and its "name label". Click create.
Repeat for QuizQuestion/QuizOption until the Quiz is done.
How can I do this?
For your workflow you might have to add accept_nested_attributes_for for the nested resources, this way when creating an object object you can actually create nested children (as long as they fulfill all the validations). This way:
# A quiz :has_many :quiz_questions
#quiz = Quiz.create(...)
with a declaration like:
has_many :quiz_questions
accepts_nested_attributes_for :quiz_questions
in your Quiz model you'll actually be able to create QuizQuestion from the quiz model like:
# using the previously quiz model
quiz.quiz_questions.create(...)
Doing the same for the deeply nested associations will do have the same effect.
Perhaps the reason why you don't see any field on the form is because there is not nested object created. Let me explain. When you create a new Quiz object, in your quizs_controller (or whatever the inflection for quiz is...) you need a:
def new
quiz = Quiz.new()
end
and
def create
Quiz.new(quiz_params)
end
private
def quiz_params
# whitelisted parameters sent along with the form
params.require(:quiz).permit(...)
end
if you actually want to be able to see the fields in the form you'll have to use the build method and actually populate that new object with respective the nested resources.
Note that for this to work with the form you will have to whitelist in the quizzes_controller the right attributes. You can debug the params you receive once you send the new quiz formulary and check that everything is right.
TIP! if you don't want to worry about the JS when adding nested resources dynamically, I recommend you using the cocoon gem
I've got a form_for form, and within it, I'm using a form helper check box. The model has a field that's a boolean value, but I'd like to be able to display it on my form in terms of its converse since I think that's easier for users to understand.
Is there any way for me to have a Rails form helper act as if the model's boolean field is flipped?
Example:
<% form_for :user do |form| %>
<%= form.check_box :privacy_disabled %>
<% end %>
In this example, the User model has a boolean field privacy_disabled. I would like to display this in the form as check here to enable privacy.
The checkbox helper function has the ability to set its checked and unchecked values, but inverting these does not seem to properly populate the checkbox with the pre-saved value.
This is how I ended up solving this issue, I was actually close with my first attempt:
<%= form.check_box :privacy_disabled,
{:checked => !#user.privacy_disabled},
0,
1 %>
So the values returned by the checkbox are flipped, and whether it starts out checked is manually flipped as well.
Not the most elegant solution, but you could do something like the following:
Add these methods to your user model:
def privacy_enabled=(val)
self.privacy_disabled = val == "0"
end
def privacy_enabled
!privacy_disabled
end
Change the form to:
<%= form.check_box :privacy_enabled %>
Again, not elegant, but it works.
I have created a form that needs to show data from 2 tables (parent and child). When the form is submitted only the child fields are updated (the parent fields are meant to be display only). While the parent model fields are displayed these need to be protected from updates (preferably via the formbuilder, rather than via css).
FWIW this is a pretty common master/detail use case. However I have not been able to find any examples of this - most of the examples I've seen seem to be trivial/single model display/update where all displayed fields are updateable).
Any ideas/samples/suggestion/tutorials/examples of real world, multi model Rails forms would be helpful.
TIA
Just out of interest, why bother going through the motions of creating a multi-model form when you only want to update the child record? My advice would be keep your form simple, I.e. make it a child form and just display the data from the parent record. If needs be, you could even style that display to look like part of the form, although I think that may throw the user off.
If you really need to do what you are doing, I would still use CSS to disable/readonly the input fields and in your controller update action, only pass the attributes you want to update into the update_attributes method call.
Finally, maybe look into the attr_protected method to prevent the fields you may want protecting from accidental mass-assignment.
I agree with tsdbrown, I don't think a complex form is required. If you'd like to learn more about complex forms or you really have your heart set on using a complex form I'd recommend watching the Railscasts episodes (73 - 75).
As tsdbrown said before, you are adding a complexity layer to your forms that's not need. If all you want is to update a detail model, while showing some info of it's parent, you could just do so with something like:
Order number: <%= #line_item.order.number %>
Order date: <%= #line_item.order.order_date %>
<% form_for #line_item do |f| %>
<%= f.label :quantity %>
<%= f.text_edit :quantity %>
<% end %>
When you'd like to edit both, then you can research on the field_for and nested forms methods (the Railscasts suggestion mentioned before it's great).
Thx for the responses which helped resolve my problem/question. Just want to close this out in case it helps others in the future.
Turns out I had been getting an error trying to reference my parent data element (patients.lname) as it was being passed in an array of results, rather than as a single result. In my view controller I had:
#visit = Visit.all(:select => "visits.id, visits.visittype, visits.visitdate, events.patient_id, patients.lname",
:conditions => ["visits.id = ?",params[:id] ],
:joins => "INNER JOIN events on events.id = visits.event_id INNER JOIN patients on patients.id = events.patient_id" )
In my view I had (this was giving me an invalid reference as I was doing a find all above):
<h1>Editing visit for patient :
<%= #visit.lname %> # should be <%= #visit[0].lname %>
</h1>
Below is the improved (and simpler) version where I find the specific record I need (basically replacing find all with find first):
#visit = Visit.find(:first, :select => "visits.id, visits.visittype, visits.visitdate, events.patient_id, patients.lname",
:conditions => ["visits.id = ?",params[:id] ],
:joins => "INNER JOIN events on events.id = visits.event_id INNER JOIN patients on patients.id = events.patient_id" )
And in the view:
<% form_for(#visit, :builder => LabelFormBuilder) do |f| %>
<%= f.error_messages %>
Name: <%= #visit.lname %>
<%= f.text_field :visittype %>
<%= f.date_select :visitdate %>
<p>
<%= f.submit 'Update' %>
</p>
<% end %>
Sometimes it's hard to see the wood for the trees! Hope this helps someone else.
i have a model named 'chapter' (whose only attributes are 'name' and 'course__id') which belongs to "course" (and a course has_many chapters). on the course 'Show' view, I list all chapters for that course. Easy.
I want to add a form at the end of the list so that a user can easily create a new chapter.
so in my controller, I've added this:
#newchapter=#course.chapters.build
and the form on the view looks like this:
<% form_for([#course,#newchapter]) do |c| -%>
<%= c.label :name, "New Chapter" %>: <%= c.text_field :name %>
<%= c.submit 'Create' %>
<% end %>
(for the sake of clarity: it is outside of the #course.chapters.each block)
Now, the problem is that #course.chapters.size is the actual number of chapters + the empty one i created in the controller.
Is there a way to loop through all #course.chapters except the last (empty) one? or is there a better practice (i.e. not create #newchapter or not like this)?
thanks,
Pierre
You don't want to use #course.chapters.build here because this does add an empty chapter to the course. Instead you'll want to use Chapter.new and set the :course option like this.
#newchapter = Chapter.new(:course => #course)
It may not even be necessary to specify :course here depending on how you are using #newchapter.