javascriptvue.jsvuejs3

Vue 3.1.12 - v-model doesn't work with dynamic generated select


I have an indefinite number of selects. For each select, I need to track the change in their values. So I used v-model:

<!-- Options -->
<div class="size-box" v-for="(values, optionName) in product.values" :key="optionName">
                    <h4>{{ optionName }}</h4>
                    <div class="d-flex align-items-center">
                      <div class="size-select language">
                        <select v-model="selectedValues[optionName]">
                          <option v-for="value in values" :key="value.id" :value="value.id">
                            {{ value.name }}
                          </option>
                        </select>
                      </div>
                    </div>
                  </div>

<!-- Add to Cart -->
    <div class="shop-details-top-cart-box-btn">
      <button
        class="btn--primary style2"
        type="button"
        @click="addToCart"
      >
        Add to Cart
      </button>
    </div>

The photo shows the structure of the received data.

Here is my script section:

<script>
import { getProduct } from "@/services/productService";

export default {
  name: "Show",
  data() {
    return {
      product: null,
      currentSku: null,
      selectedValues: {},
      isProductReady: false,
    };
  },
  async created() {
    await this.fetchProduct();
  },
  methods: {
    async fetchProduct() {
      try {
        const id = this.$route.params.id;
        this.product = await getProduct(id);
        console.log(this.product);

        if (this.product && this.product.values) {
          Object.keys(this.product.values).forEach((optionName) => {
            const firstValue = this.product.values[optionName][0];
            if (firstValue) {
              this.selectedValues[optionName] = firstValue.id;
            }
          });

          this.updateSku();
          console.log(this.currentSku);
        }

        this.$nextTick(() => {
          this.isProductReady = true;
          this.triggerLocalChange();
        });
      } catch (error) {
        console.error("Failed to load product:", error);
      }
    },
    updateSku() {
      if (!this.product || !this.product.skus) return;

      const matchingSku = this.product.skus.find((sku) => {
        return sku.values.every(
          (value) => this.selectedValues[value.option_name] === value.value_id
        );
      });

      this.currentSku = matchingSku || null;
    },
    addToCart() {
      if (this.currentSku) {
        console.log(
          "Added to cart",
          this.selectedValues,
        );
      } else {
        console.log("Please select valid options first.");
      }
    },
    triggerLocalChange() {
      if (this.isProductReady) {
        $(document).trigger("change");
      }
    },
  },
  mounted() {
    this.$nextTick(() => {
      if (!this.isProductReady) {
        console.log("DOM updated but product data is not fully loaded yet.");
      }
    });
  },
};
</script>

Response from the backend:

{
"name": "Test Product",
"manufacturer": "Nike", 
  "values": {
    "Color": {
      "0": {
        "id": 1,
        "name": "Green"
      },
      "2": {
        "id": 8,
        "name": "White"
      },
      "4": {
        "id": 10,
        "name": "Brown"
      }
    },
    "Clothes Size": [
      {
        "id": 15,
        "name": "M"
      },
      {
        "id": 16,
        "name": "L"
      }
    ]
  }
}

I tried to do the following: when changing the value in at least one select, cause the currentSku value to change. However, I noticed that the values ​​of selectedValues ​​do not change as they should. Please help me understand the problem, I'm writing on Vue for the first time. Thank you all


Solution

  • selectedValues is an array, it's possible to modify non-index properties, but they are not reactive.

    It should be:

    selectedValues: {}