Showing attachment in edit view Rails 6 (active storage) - ruby-on-rails

Situation:
I sucessfully upload a file called :physical_copy in my form partial "_a_partial.html.erb", which is used for creating new objects and editing existing objects at the same time
Code:
<%= f.file_field :physical_copy,
required: true,
class: 'form-control',
value: #tax_relevant_document.physical_copy %>
New & create works perfectly fine.
Problem:
The file is not shown and hence deleted when editing, which is not the planned behavior. I can work around by first downloading and "re-uploading" but this shouldn't be the normal behavior.
Guess:
This "value" thing looks awry to me but removal of the attribute didn't help.
I would like:
edit view to show the existing filename in the field as basically value representation
the form should include the existing attachment in the field

I found a solution and made it conditional, which works fine in my case:
<% if !object.attachment_attribute.attached? %>
(File Field from above)
<% else # (attachment exists) %>
<%=
link_to(
object.attachment_attribute.filename,
rails_blob_path(object.attachment_attribute, disposition: 'attachment')
)
%>
<% end %>
Basically the attachment for download!

Related

Rails only pass params that have changed on edit submit

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

How do I stop a hidden_field from converting my file value into a string?

So I've debugged the value from my parse.com database like this:
<%= debug #activity["image1"] %>
Result in view shows it is a file as I expect:
--- !ruby/object:Parse::File
parse_filename: tfss-1c325650-79d2-4d34-ab3b-c62397c7ac9a-image1.jpg
url: http://files.parsetfss.com/0b53rerr13f-42e5-be7ac9a-image1.jpg
I try to add it as a value of my form like this:
<%= form_for :leads, :url => activities_create_path do |f| %>
<%= f.hidden_field :image, :value => #activity["image1"] %>
<% end %>
In my controller I try to save like this:
def create
#lead = Leads.new(params[:leads])
if #lead.valid?
lead = Parse::Object.new("Leads")
lead["image"] = params[:leads][:image]
lead.save
redirect_to root_url
end
end
The error I'm getting tells me that it can't save because it was expecting a file but got a string.
I know the value is a file but I suspect hidden_fields values end up being strings.
Is there a way to convert a hidden_field value to a string?
If question 1 isn't possible then can I create a file_field and set it's value without manually uploading a image file?
What I actually need to do is take the default image file of an object from my parse.com database and store it exactly as it is (a file) back into another table in parse.com.
The problem I'm having is in my rails form where the file is being converted to a string.
Another option I have is to some how grabbed the same image which is actually being displayed at the top of the page where the form.
This is how I display the image from the file:
<%= image_tag #activity["image1"].url, onerror:"this.style.display='none'" if #activity["image1"].present? %>
Then some how use that image to simulate adding a file to a file_field. However I think that is slightly complicated and unnecessary for what I'm trying to do.
I have a file from one table in parse.com and I read it in rails but now want to take that file and do nothing to it, then store it back into parse.com but in another table. However as I mentioned above hidden_field converts the file to a string but I need it to remain in it's same format.
I hope this all makes sense.
Would appreciate your help.
Thanks for your time.
Http parameters and html fields values will always be like a string.
Try to store the value of 'parse_filename' or 'url' from your Parse::File in the hidden field. In the action method you can read the file using the path submitted.

Create a non-database linked form field in ruby on rails

I am attempting to do some calculations on a simple ruby screen.
Lets say for fun, i want to create a form that lets the user convert meters to feet.
Of course I don't want to store the values in a database. Nor would I want to create a table just for this.
So my question.. How do I create a single text field tied to a controller.
With a button.
Here's a really simple example of making a form adding two numbers:
rails new math_app -O
cd math_app
rails generate controller Add form result
The last line generates a controller with 2 actions -
"form"- to show the form
"result" - to show the results
In another command prompt window, open the 'math_app' directory and start the server:
rails server
You can open a browser to 'localhost:3000/add/form' and 'localhost:3000/add/result' to see the default pages Rails generated for you. As you edit things below you can revisit these pages (and don't even have to restart the server) to see what they produce.
Edit 'app\views\add\form.html.erb' to create the form we want to show.
<%= form_tag add_result_path do %>
<%= number_field_tag :first %>
+
<%= number_field_tag :second %>
<%= submit_tag "add" %>
<% end %>
Edit 'config/routes.rb' to make the 'result' action take POST requests from the form above.
Change -
get "add/result"
to-
post "add/result"
Edit 'app\controllers\add_controller.rb' result method to retrieve the two numbers from the form data and add them together.
def result
#first = params[:first].to_i
#second = params[:second].to_i
#result = #first + #second
end
Edit 'app\views\add\result.html.erb' to show the result.
<%= #first %> + <%= #second %> = <%= #result %>
<br/>
<%= link_to 'back', add_form_path %>
Done!
check out form_tag http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag
Ruby on rails uses MVC ideology a lot; however you aren't required to save everything to a database form_tag allows you to create an HTML form without tying it to a model.

Problem with file uploads in a nested form using Rails3 with Mongoid and Carrierwave

Im having a problem transferring an SQLlite Rails 3 app over to a Mongoid Rails 3 app. In the SQLlite version, I am easily able to include an image upload form (using Paperclip) from one model ('image') within a nested form from another model ('product'). Here's my 'new' product form:
<%= form_for #product, :html => {:multipart => true} do |f| %>
<% f.fields_for :images do |image_form| %>
<%= f.label :productphoto %>
<%= f.file_field :productphoto %><br />
<% end %>
<% end %>
And here's the 'show' view:
<% #product.images.each do |image| %>
<%= image_tag image.productphoto.url(:gallerythumb) %><br />
<% end %>
When I try to use the same product views in my Mongoid Rails 3 app (using Carrierwave), I get the following error:
TypeError in Stores#show:
can't convert nil into String
<%= image_tag product.image.url(:gallerythumb) %>
Im pretty sure my models in the Mongoid version are correct because if I add a string (like 'name') to my 'image' model and nest that in the 'Product' form, it works. Also, Im able to upload an image into a non-nested model form.
Any help would be greatly appreciated!
I just had a similar problem myself. The problem is not the image upload I think, but the problem is that Rails doesn't recognize :images as being an Array. If you look into the Rails source of the fields_for helper you see that it checks for a method "_attributes=". If that's not there the form will be posted as normal fields and not as an array (params will be "images" instead of "images[0]")
You have to add the following line to your model:
accepts_nested_attributes_for :images
It is carrierwave or mongoid bug
https://github.com/jnicklas/carrierwave/issues#issue/81
This is most likely the issue that Lewy linked to -- that problem is specific to arrangements where your Carrierwave uploader is mounted on a child document in an embedded association and you are saving the parent, and though you don't explicitly show if this is how your data is modeled, I suspect that's the case since you noted that it works with a non-nested form (presumably saving the child document then, not the parent).
If you dig around in the discussions linked from that issue, you'll find some proposed workarounds. Here's what I ended up with to get Carrierwave working in this situation for me:
https://gist.github.com/759788
Full credit is to due to zerobearing2 whose gist I forked, I just made minor changes to get it working in Rails 3.0.3 and commented on my gist with summary info on the relevant discussions.

How to show the content of the form just entered in the ":confirm =>" option on Ruby on Rails

I am trying to have a way of confirming the information entered before actually saving it to the DB
Considered making an individual confirmation page as discussed here
Ruby on Rails: Confirmation Page for ActiveRecord Object Creation
However my form includes an attached file using paperclip which makes it more of a hassle to implement.
I thought of just having a :confirm => pop up that would show the information that the user
had just entered.
The problem is how to show the information that the user had just entered, for example
<% form_for #entry, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.label :file %><br />
<%= f.file_field :file %>
<%= f.submit 'Create', :confirm => "????? " %>
<% end %>
Given that your loading attachments it may not be a bad idea to render a staging view including information derived from the attachment allowing the user to confirm. As in display the file if it's an image, or the first paragraph of text if it's a text file, etc.
It's going to take more work than the just adding a confirm pop up, but I feel the enhanced user experience is worth the extra effort.
I'm not familiar with the way that paperclip works. So you're mostly on your own for the intimate details.
You will probably have to create a record before the staging view can be rendered with the sample of the uploaded file. To accomplish that I'd set up an "active" column on the model in question, that defaults to false.
Usage would look something like this:
User complets new form.
Attachment is updated and records are saved, with the active field set to false.
Redirected to confirmation page that is essentially the show page with a confirm link/button and a cancel link/button
a. When the confirm link/button is clicked it sends a request to the controller triggering the update action on this record setting active to true.
b. When the cancel link/button is clicked it sends a request to the controller trigering the destroy action on this record.
All that's left is to set up a recurring task to remove objects that are inactive and were crated long enough ago that it's safe to assume the user has just ended the browser session.
The confirm option for the Rails submit method can only take a text value.
If you want to dynamically generate the confirm text, one way you could do it is to write your own HTML submit tag, and write custom javascript to analyse the fields you want to use in your generated text.
Or you could use the Rails submit method, but use something like JQuery to add an event handler to it.
I'd just throw my js into an onclick handler. That's all Rails does.
<%= f.submit 'Create', :onclick => "confirm('Name is ' + $F('entry_name'));" %>
(note, didn't test that, but it looks close. confirm is a core js function, not part of any lib)

Resources