Making a Dynamically Controlled Form: The Backend
Tags: ruby, rails
Published: 2020-09-12 23:04:48

In my last post, I detailed the steps to create a dynamically controlled form component in a React app to create a recipe app. However, that is only half of what we need to save that information to the server. In this post, I’ll go over how to build out a controller in Ruby on Rails that accepts information from a dynamically controlled form. 


## Creating a recipe
In the controller, write a create method. This create method will be used to create a new recipe. 


```

   def create
       new_recipe_params = {
           title: params[:title],
           summary: params[:summary],
           user_id: params[:user_id]

       }
       recipe = Recipe.create(new_recipe_params)
       create_ingredients(params[:ingredients], recipe.id)
       create_steps(params[:steps], recipe.id)
       render json: { recipe: RecipeSerializer.new(recipe)}
   end
```

The parameters of a new recipe are as follows: 

- Title 
- Summary 
- User_id 
- Ingredients 
- Steps

As you can see,the method takes in the title, summary, and user_id data and uses helper methods for the ingredients and steps. Also note that there is a private method that handles the form permissions: 

```

private

   def recipe_params
       params.require(:recipe).permit(:title, :summary, :user_id, :steps=> [], :ingredients=>[])
   end

```

The ingredients are stored in an array from the form on the front end. In order to add them to the recipe, the create_ingredient method goes through that array and creates an ingredient for each element using the name and amount information. Then, the shovel operator adds the new ingredient to to ingredients attribute on recipe. 

```
def create_ingredients(array, id)
       recipe = Recipe.find(id)
       array.each do |ingredient|
           new_ingredient = Ingredient.create({recipe_id: id, name: ingredient[:name], amount: ingredient[:amount]})
           recipe.ingredients << new_ingredient
       end
   end
```



The create_steps method uses similar logic. 

```
  def create_steps(array, id)
       recipe = Recipe.find(id)
       array.each do |step|
           new_step = Step.create({recipe_id: id, step_summary: step[:step_summary]})
           recipe.steps << new_step
       end
   end
```
## Showing a recipe
A show method allows us to view the newly created recipe. 

```
 def show 
        @recipe = Recipe.find(params[:id])
        if @recipe
            render json: {recipe: @recipe, ingredients: @recipe.ingredients, steps:@recipe.steps }
        else 
            render json: { error: "That recipe does not exist...yet"}, status: :not_acceptable
        end
    end
```
This works for creating and showing a recipe, but what about editing a recipe? 

## Editing a recipe
The edit route finds the recipe.  

```
   def edit
       @recipe = Recipe.find(params[:id])
       render json: @recipe
   end
```

Like the create method, the update method also needs helper methods for the ingredients and steps attributes. 

```
   def update
        @recipe = Recipe.find(params[:id])

        edit_ingredients(params[:ingredients], @recipe.id)
        edit_steps(params[:steps], @recipe.id)

        @recipe.update(recipe_params)

        render json: @recipe
    end
```

The edit_ingredients method is similar to the create method. First, find the ingredient and then apply the updates to the ingredients that were updated. Again, the updated ingredients are shoveled into recipe.  

```
   def edit_ingredients(array, id)
        recipe = Recipe.find(id)
        if recipe.ingredients
            array.each do |ingredient|
                if ingredient[:id]
                    updated_ingredient = Ingredient.update({recipe_id: id, name: ingredient[:name], amount: ingredient[:amount]}) 
                else 
                   updated_ingredient = Ingredient.create({recipe_id: id, name: ingredient[:name], amount: ingredient[:amount]})
                    recipe.ingredients << updated_ingredient
                end 
            end 
        end
    end
```

Again, the edit_steps method follows the same pattern as the edit_ingredients method.

```
   def edit_steps(array, id)
        recipe = Recipe.find(id)
        if recipe.steps
            array.each do |step|
                if step[:id]
                    updated_step = Step.update({recipe_id: id, step_summary: step[:step_summary]}) 
                else 
                    updated_step = Step.create({recipe_id: id, step_summary: step[:step_summary]})
                    recipe.steps << updated_step
                end 
            end 
        end
    end

```
## Delete a recipe 
In contrast, the destroy method is relatively simple. 

```
   def destroy 
        @recipe = Recipe.find(params[:id])
        if @recipe 
            @recipe.destroy 
            render json: {message: "Recipe successfully deleted"}
        else 
            render json: {message: "Failed to destroy recipe"}
        end
    end
```

# Conclusion 

In order to add information from a dynamically controlled form, use helper methods in your controller to take in information. The edit method will also use helper methods for dynamic information. After implementing the controller logic, a new recipe will be saved to the backend.