How can I add a row into the same array?

172 Views Asked by At

I'm building an app with Nuxt 3 and consuming an external API for reading and storing data; I am handling the forms with the Formkit library. In the newrecipe page I make use of a component called NewIngredientInput which is used to generate a new row for a new ingredient if the user wishes. All the ingredients should be stored in a single array as it is arranged in the database model and in the API, but this is not the case.

The structure of the JSON that it receive should be the following:

{
  "name": "data",
  "ingredients": [
    {
      "amount": "1",
      "unit": "data",
      "name": "data"
    },
    {
      "amount": "2",
      "unit": "data",
      "name": "data"
    },
    // ... other ingredient objects
  ],
  "description": "data",
  "region": "data",
  "keywords": "data",
  "photo": [
    {
      "name": "1.jpg",
      "file": "[object File]"
    }
  ]
}

But what I currently get is:

{
  "name": "data",
  "form_2": {
    "amount": "1",
    "unit": "data",
    "name": "data"
  },
  "description": "data",
  "region": "data",
  "keywords": "data",
  "photo": [
    {
      "name": "1.jpg",
      "file": "[object File]"
    }
  ],
  "form_8": {
    "amount": "2",
    "unit": "data",
    "name": "data"
  }
}

Here is my code for the componente NewIngredientInput.vue:

<template>
  <FormKit type="form" name="ingredients">
    <div class="newIngridient">
      <FormKit
        v-model="ingredient.amount"
        type="number"
        name="amount"
        id="amount"
        label="Cantidad"
        validation="required"
      />
      <FormKit
        v-model="ingredient.unit_measure"
        type="text"
        name="unit"
        :id="`${id}`"
        label="Unidad"
        validation="required"
      />
      <div class="ingredient">
        <FormKit
          v-model="ingredient.name"
          type="text"
          name="name"
          :id="`${id}`"
          label="Ingrediente"
          validation="required"
        />
      </div>
      <button class="remove" @click="removeRow" v-show="showRemoveButton">
        -
      </button>
    </div>
  </FormKit>
</template>

<script>
export default {
  props: {
    showRemoveButton: {
      type: Boolean,
      default: false,
    },

    // Pass the ingredient object as a prop
    ingredientValue: Object,
    ingredient: Object,
    id: Number,
  },
  data() {
    return {
      ingredient: { amount: "", unit_measure: "", name: "" },
    };
  },
  watch: {
    // When the prop value (ingredient object) changes, update the local ingredient data
    ingredientValue: {
      handler(newValue) {
        this.ingredient.amount = newValue.amount;
        this.ingredient.unit_measure = newValue.unit_measure;
        this.ingredient.name = newValue.name;
      },
      deep: true, // Watch for nested property changes
    },
  },
  methods: {
    removeRow() {
      this.$emit("removeRow");
    },
  },
};
</script>

And here is my code for the page newrecipe.vue:

<template>
  <div class="container">
    <UserMenu />
    <SideNavUser />
    <div class="new-recipe-container">
      <div class="title">Crear Receta</div>
      <div class="form">
        <FormKit type="form" @submit="createRecipe" #default="{ value }">
          <FormKit
            type="text"
            name="name"
            id="name"
            label="Título de la Receta"
            validation="required"
          />
          <div
            class="new-ingredient"
            v-for="(row, index) in inputRows"
            :key="index"
          >
            <NewIngredientInput
              v-model="row.amount"
              v-model:unit_measure="row.unit_measure"
              v-model:name="row.name"
              :showRemoveButton="index > 0"
              @removeRow="removeRow(index)"
            />
            <button @click="addRow" class="add">+</button>
          </div>
          <FormKit
            type="textarea"
            rows="10"
            name="description"
            id="description"
            label="Instrucciones"
            placeholder="Escribe oraciones o párrafos."
            validation="required"
          />
          <div class="triple">
            <FormKit
              type="text"
              name="region"
              id="region"
              label="Región"
              validation="required"
            />
            <FormKit
              type="text"
              name="keywords"
              id="keywords"
              label="Palabras Clave"
              placeholder="pollo, ajo, ..."
              help="Ingresa palabras claves separadas por comas."
              validation="required"
            />
            <div class="photo">
              <FormKit
                type="file"
                name="photo"
                id="photo"
                accept=".jpg,.png"
                label="Imagen"
                help="Solo puedes incluir imagenes con extensión .jpg o .png"
                validation="required"
              />
            </div>
          </div>
          <FormKit type="submit" label="Crear" />
          <pre wrap>{{ value }}</pre>
        </FormKit>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      inputRows: [{ amount: "", unit_measure: "", name: "" }],
    };
  },
  methods: {
    addRow() {
      this.inputRows.push({ amount: "", unit_measure: "", name: "" });
    },
    removeRow(index) {
      this.inputRows.splice(index, 1);
    },

    async createRecipe() {
      // Get the form data from the elements
      const ingredients = this.inputRows.map((row) => {
        return {
          amount: row.amount,
          unit: row.unit_measure,
          name: row.name,
        };
      });
      const recipeForm = {
        name: this.name,
        description: this.formValues.description,
        region: this.formValues.region,
        keywords: this.formValues.keywords,
        ingredients: ingredients,
      };

      try {
        const photoForm = new FormData();
        photoForm.append("photo", this.formValues.photo.files[0]);
        const imageResponse = await axios.post(
          "API endpoint for photo",
          photoForm
        );

        const image = imageResponse.data;

        recipeForm.photo = image;

        const recipeResponse = await axios.post(
          "API endpoint for all the recipe data",
          recipeForm
        );

        console.log("Receta creada exitosamens: ", recipeResponse.data);
      } catch (error) {
        console.error("Error creating recipe:", error.message);
      }
    },
  },
};
</script>

At this moment I am not sending anything to my API, only with the "pre" element on the template I can see the structure of the resulting JSON that will be sent to the database. I understand that the connection with the API is not well done.

0

There are 0 best solutions below