Vue 3 <setup script> - вычисляемые свойства не являются реактивными?

Я только начал использовать составной API и <script setup> блок и атрибут. Пока мне это нравится (из vue 2), но у меня была эта странная проблема, с которой я не могу справиться.

Я извлекаю объект JSON из своей базы данных, который содержит атрибуты (пары ключ: значение). Поскольку ключ является динамическим (думаю, { color:"blue", size:"medium" }), мне нужно создать новый объект и добавить каждый атрибут в свой собственный объект, указав, какой из них является ключом, а какой — значением. Итак, { color:"blue"} становится: { key: "color", value: "blue }, что позволяет мне привязать его к входным данным в моем <template>. Когда страница загружается, она преобразует атрибуты в новый объект и отображает их во входных данных.

Если пользователь хочет добавить атрибут, он может нажать кнопку, чтобы создать новую «строку» для атрибутов. Проблема в том, что когда они нажимают кнопку «плюс», объект атрибутов обновляется, но не отражается на форме (не реактивный). Я честно не уверен, почему. Вот мой <script>:

<script setup>

import { computed, reactive } from "vue";
import { getProductByID } from "@/composables/products";

const props = defineProps({
  productid: String,
});

const { product, error, loadProduct} = getProductByID(props.productid);
loadProduct();

const attributes = reactive({
    english: computed(() => {
      if (product.value.attributes && Object.keys(product.value.attributes).length > 0) {
      
        let newValues = [];

        for (const [key, value] of Object.entries(product.value.attributes)) {
          let s = {};
          s["key"] = key;
          s["value"] = value;
          newValues.push(s);
        }
        return newValues;
      }
      return {};
    })
  });

ПРИМЕЧАНИЕ: товар, возвращенный из сборного комплекта, упакован в ref{}:

import axios from "axios";
import { ref } from "vue";

const product = ref({});
const error = ref(null);
...
return {product, error, loadProduct}

Атрибуты привязаны к входу правильно:

<div id = "attributes">
  <!-- English Attributes -->
  <div>
    <div>
      <label>Attributes (property : value) </label>
      <button @click.prevent = "addAttributeInput">
        <span class = "icon">
          <i class = "fas fa-plus"></i>
        </span>
      </button>
    </div> 
    <div v-for = "(attribute, index) in attributes.english" :key = "index">
      <div>
        <input v-model = "attributes.english[index]['key']" placeholder = "attribute" />
      </div>
      <div class = "control">
        <span>:</span>
      </div>
      <div>
        <input v-model = "attributes.english[index]['value']" placeholder = "value" />
      </div>
      <button @click.prevent = "removeAttributeInput(key)">
        <span class = "icon">
          <i class = "fas fa-minus"></i>
        </span>
      </button>
    </div>
  </div>
</div>

Если пользователь хочет добавить (или удалить) атрибут, есть кнопка, которая добавляет пустой объект к объекту attributes.english:

function addAttributeInput() {
  attributes.english.unshift({"key":"", "value":""});
}

function removeAttributeInput(index) {
  attributes.english.splice(index, 1);
}

Когда я нажимаю эти кнопки, он изменяет объект attribute.english (я вижу его в инструментах vue в браузере), но не обновляет форму реактивно. Обертывание объекта attributes с помощью reactive() не делает его реактивным?

🤔 А знаете ли вы, что...
JavaScript является одним из трех основных языков веб-разработки, вместе с HTML и CSS.


282
1

Ответ:

Решено

Проблема с вашим кодом заключается в том, что вы не можете изменять реквизиты computed() с помощью v-model (если он не доступен для записи). Поэтому это не может работать.

Конечно, есть несколько вариантов решения вашей проблемы. Я сделал Stackblitz, чтобы показать, как это сделать с использованием watch(), который устанавливает значение ref().

Это зависит от варианта использования, но может быть лучше использовать записываемый вычисляемый.