Recently, I ran into a problem while programming my last project. I have a Recipe object that has_many Ingredient objects, and it also has an attached image.
To get started with accepting nested parameters, I highly recommend checking out this article that does a great job at summing up Ruby's 'accepts_nested_attributes_for' macro to create multiple associated objects upon creation of your primary object.
I had set my app up following that guide, and it was working perfectly. My recipe was getting created, along with all the associated ingredient objects. However, that was when I was sending the data via JSON.
JSON vs FormData
JSON data does a great job at handling nested objects. The complexity came when I brought in passing an image file to Ruby from JavaScript. JSON does not allow you to send over full files. I had to use JavaScript's 'FormData' to send over the image file, but when I was passing in my other data to 'FormData' like I had with my JSON data, Ruby param's would show '[object Object]' as my params for 'ingredients_attributes'. This was an issue, and I had a hard time locating a working solution online. I found a solution that is not very complicated, so I am writing this article to share that here!
JavaScript code to properly append nested data to FormData()
I'm going to pair some of this down for simplicity's sake. Let's say we have a form that has the state of recipeName that points to a string and state of ingredients that points to an array of objects with the keys quantity, unit, and name.
Inside our handle submit, we will begin by instantiating a recipe object.
const recipe = {
name: recipeName,
ingredients_attributes: ingredients
}
We will then instantiate a the FormData variable and append the recipe's name like so:
const formData = FormData()
formData.append("name", recipe.name)
The above code is simple enough. I will now demonstrate how to append our ingredients attributes so that we are able to send as many ingredients objects as we need. We will be doing this using a forEach with index. Like I mentioned above, it is not a very complicated solution, but I had a hard time locating a solution online so I hope this can help someone else in their coding journey.
recipe.ingredients_attributes.forEach((ingredient, index) => {
formData.append(`ingredients_attributes[${index}][quantity]`, ingredient.quantity;
formData.append(`ingredients_attributes[${index}][unit]`, ingredient.unit;
formData.append(`ingredients_attributes[${index}][name]`, ingredient.name;
So as you can see, when using form data we need to append each individual key value pair for Ruby to access the information properly. We can utilize the index in JavaScript's forEach to allow us to send as many nested objects as we'd like, iterating over the different key value pairs that we are expecting in our object. I will also add that in this specific form, quantity and unit were optional values for the user, so they always instantiated as an empty string to prevent any errors on our backend.
I hope that this article helps you get over any bumps in your challenge. Happy coding!
Top comments (0)