How to keep the reactivity of data when passing it into a function?

Suppose my component looks like this:

<script setup lang="ts">
interface FileMetadata {
  owner: string,
  location: string,
  size: number,
  // And around 50 mores...
}

interface File {
  fileName: string,
  metadata: FileMetadata
}

const file = defineModel<File>({ required: true });

const buildControls = (metadata: FileMetadata) => {
  return [
    {
      label: 'Owner',
      model: metadata.owner,  // Correct so?
      type: 'text'
    },
    {
      label: 'Location',
      model: metadata.location,  // Correct so?
      type: 'text'
    },
    {
      label: 'Size',
      model: metadata.size,  // Correct so?
      type: 'number'
    },
    // And around 50 mores...
  ];
};

const controls = buildControls(file.value.metadata);  // Value is already unpack here
</script>

<template>
  <div v-for=(item, idx) in controls>
    <v-text-field v-model="item[idx].model" :label="item[idx].label" :type="item[idx].type" />
  </div>
</template>

This component takes a File object via its v-model, renders the view to allow users to edit its metadata. Whenever the model changes (another file was passed), the values of the text fields should be changed accordingly. And whenever users change values in the text fields, its corresponding values in the model must be changed as well. So, it’s a two-way binding.

The view is rendered fine, but I cannot change values of the textboxes (it always changes back to its initial values when it loses its focus). I suppose because when the buildControls() function is called, the file is already unpacked, which makes it lose the reactivity. But if I do not unpack it (by accessing its .value), I cannot access its metadata attribute.

If I make the controls reactive by doing

const controls = reactive(buildControls(file.value.metadata));

then I can change the values in text fields, but the file model is not updated accordingly.

In my idea, the value of the model key should be a Ref object. But somehow I cannot achieve it.

A possible solution is to store the key name of the FileMetadata only instead of the object in the model key. Then, it can be access later in the template as file.metadata[item.model]. However, I do not like this approach. Why can’t I just simply store an object instead?

What did I do wrong here? What is the correct way to keep the reactivity of the data?

13

controls is not reactive, this needs to be changed:

const controls = ref();
...
controls.value = buildControls(...);

Or:

const controls = reactive(buildControls(...));

But the problem is that controls is intended for two-way synchronization with file, its structure makes it hard to implement. This is a general problem with storing mixed mutable and static data, there are not enough reasons to keep it together.

If the component is supposed to emit file changes to a parent based on the changes that v-model=”item[idx].model”` there will be problems with tracking specific changes.

There should be a list of metadata keys that both static and mutable data could use. The suggested way is to keep static field data as a constant, so the types could derive from it for extra type safety (ValidateKeys can be implemented like shown here):

const fileMetaFields = {
  owner: {
    label: 'Owner',
    type: 'text'
  },
  ...
}

type FileMetaKeys = keyof typeof fileMetaFields;

type FileMeta = ValidateKeys<FileMetaKeys, {
  owner: string,
  ...
}

Additional utility types could be written to automatically map type: 'text' to string type.

The component may not need own state if it’s supposed to update parent’s model. v-model in v-text-field limits the ways in which the model could be updated, so it needs to be desugared:

  function updateModelField({ key, value }) {
    emit('update:modelValue', {
      ...file.value,
      metadata: {
        ...file.value.metadata
        [key]: value
      },
    });
  }

  ...

  <div v-for=(field, key) in fileMetaFields>
    <v-text-field
      :label="field.label"
      :modelValue="file.value.metadata[key]" />
      @update:modelValue="updateModelField({ key, value: $event })"
      ... 

This indicates the problem with file. In order to prevent model prop from being mutated in a child and do this in a parent like Vue two-way binding requires, the whole object needs to be cloned to change one field. This is inefficient and can be avoided by providing a child with only data it needs (metadata instead of file) and updating file in a parent with custom event rather than v-model.

So it would be in the child:

  const props = defineProps<{ data: FileMeta }>();

  function updateModelField({ key, value }) {
    emit('update:field', { key, value });
  }

In a parent:

<FileMetaForm
  :data="file.metadata"
  @update:field="file.metadata[$event.key] = $event.value"
>

2

If you want keep reactivity of data, you can use computed,modify your code as follows:

<script setup lang="ts">
interface FileMetadata {
  owner: string,
  location: string,
  size: number,
  // And around 50 mores...
}

interface File {
  fileName: string,
  metadata: FileMetadata
}

const file = defineModel<File>({ required: true });

const buildControls = (metadata: FileMetadata) => {
  return [
    {
      label: 'Owner',
      model: metadata,  // Correct so?
      type: 'text',
      field: "owner"
    },
    {
      label: 'Location',
      model: metadata,  // Correct so?
      type: 'text',
      field: "location"
    },
    {
      label: 'Size',
      model: metadata,  // Correct so?
      type: 'number',
      field: "size"
    },
    // And around 50 mores...
  ];
};

const controls = buildControls(file.value.metadata);  // Value is already unpack here
</script>

<template>
  <div v-for="item in controls">
    <v-text-field v-model="item.model[item.field]" :label="item.label" :type="item.type" />
  </div>
</template>

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật