How to transfer a read-only property from an array to a writable property?

I am fairly new to using Vue and completely unfamiliar with TypeScript. I am trying to pass an array of TaskSolution objects (TaskSolution being a custom type) as a property to a component, then delete some TaskSolution objects from the array and have the interface updated accordingly.

Below is the code where I attempted to achieve this:

//TaskSolution.ts
interface TaskSolution {
    task : string,
    solution: string,
};

export type { TaskSolution };

Another file:

//ProvideArrayTest.vue
<script lang="ts">
import { defineComponent } from 'vue';
import UseArrayTest from "./UseArrayTest.vue";
import type { TaskSolution } from "./../types/TaskSolution.ts";

export default defineComponent({
  components : { UseArrayTest }
});
</script>

<template>
  <h1>ProvideArrayTest</h1>
  <UseArrayTest :sentences="[ { task: 'not smart', solution: 'stupid'  }, { task: 'not good', solution: 'bad' }]" />
</template>

<style scoped>

</style>

Another file:

//UseArrayTest.vue
<script lang="ts">

import { ref, defineComponent } from 'vue';
import type { PropType } from "vue";
import type { TaskSolution } from "./../types/TaskSolution.ts";

export default defineComponent({
  // type inference enabled
  props: {
    sentences : {
        required: true,
        type: Array as PropType<TaskSolution[]>
    },
  },
  setup(props) {
    const sentences2 = ref<TaskSolution[]>(props.sentences);
    return { sentences2 };
  },

  methods : {
     deleteSentence(i: number, sentence : TaskSolution) {
        console.log("Delete is executed!");
        if(i==1){
            this.sentences = this.sentences.filter( (item: TaskSolution) => item !== sentence ); 
        }
        else if(i==2){
            this.sentences2 = this.sentences2.filter( (item: TaskSolution) => item !== sentence );
        }
     }
  }
})

</script>

<template>

<h1>sentences array</h1>
  
  <ul>
<li v-for="sentence in sentences">
  Task: {{ sentence.task }} Solution: {{ sentence.solution }} <button @click="deleteSentence(1,sentence)">Delete</button>
</li>
  </ul>
  
<h1>sentences2 array</h1>
 <ul>
<li v-for="sentence in sentences2">
  Task: {{ sentence.task }} Solution: {{ sentence.solution }} <button @click="deleteSentence(2,sentence)">Delete</button>
</li>
  </ul>

</template>

<style scoped>

</style>

After running npm run dev, the task and solution are displayed correctly for sentences but I am unable to delete anything. Sentences2, however, works flawlessly.

Running npm run build results in the following errors:

src/components/UseArrayTest.vue:24:18 - error TS2540: Cannot assign to 'sentences' because it is a read-only property.

24             this.sentences = this.sentences.filter( (item: TaskSolution) => item !== sentence ); //Can't assign to read-only property  

Is there a way to directly modify the sentences property and update the interface without having to initially copy sentences into sentences2?

Answer №1

When working with TypeScript, type checking can reveal the true issue in your code. It's important to avoid mutating props, as two-way binding should be achieved using a prop+event combination. The parent component should hold the state instead of just passing a static object through the prop:

...
data() {
  return { sentences: [...] }
}
...
<UseArrayTest :sentences="sentences" 

If you're simply reassigning a value, consider using the modelValue prop (instead of sentences) and the update:modelValue event:

 deleteSentence(sentence: TaskSolution) {
   this.$emit('update:modelValue', this.sentences.filter(...))
 }

This approach simplifies the code to:

<UseArrayTest :modelValue="sentences" @update:modelValue="sentences = $event" />

You can also utilize the v-model syntax sugar for even cleaner code:

<UseArrayTest v-model="sentences" />

Instead of completely reassigning the array or object on each modification, consider using custom events. For example, the deleteSentence method could accept an index to delete an element:

 deleteSentence(index: number) {
   this.$emit('deleteSentence', index)
 }

In this scenario, the parent component then mutates the state:

<UseArrayTest :sentences="sentences" @deleteSentence="sentences.splice(i, 1)" />

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for effectively utilizing vue-draggable (or Sortable JS) within a v-data-table using the powerful combination of Vuetify 2 and Vue JS 2

I'm currently experimenting with integrating vue-draggable into Vuetify's v-data-table based on the instructions provided in this article : https://medium.com/vuetify/drag-n-drop-in-vuetify-part-ii-2b07b4b27684 The article mentions : "The main o ...

Encountering issues with property not being found when using connect() with React, Redux, and TypeScript

I'm struggling to correctly define the access of properties and methods on distinct components using the connect() functionality. Here's what I currently have in my redux-folder: /redux/app/hooks.ts import { TypedUseSelectorHook, useDispatch, us ...

Setting a default value for a data type within Typescript

My goal is to set default values for all properties in my custom type if they are not defined. This is what I have done: // custom type with optional properties type MyType = { // an example property: str?: string } // with numerous properties, assign ...

Adding an item to a sleek carousel

Adding items to a Slick carousel in a vue.js demo is proving to be a bit tricky. When I try to use the refresh() function after adding an item, it doesn't seem to work as expected even though the item is successfully added with vue.js. //Attempting to ...

Retrieve the input type corresponding to the name and return it as a string using string template literals

type ExtractKeyType<T extends string, K extends number> = `${T}.${K}`; type PathImpl<T, Key extends keyof T> = Key extends string ? T[Key] extends readonly unknown[] ? ExtractKeyType<Key, 0 | 1> : T[Key] extends Record<str ...

Encountered a syntax error while running the Vue2 serve command: Unexpected token

I recently developed a test app using vue-diagram-editor from https://github.com/max-kut/vue-diagram-editor. Everything was working fine with the test app, so I decided to install it into my main Vue app that was running smoothly before. $ npm install --s ...

How can methods from another class be accessed in a TypeScript constructor?

I need to access a method from UserModel within the constructor of my UserLogic class. How can I achieve this? import { UserModel, ItUser } from '../../models/user.model'; export class UserLogic { public user: ItUser; constructor() { ...

Creating a variable as a list of string arrays in TypeScript

Currently working with Angular 2.0, I am trying to declare a variable in a Typescript file that is a list of string arrays. I attempted using yAxis_val: list, but it is not functioning correctly. If anyone knows the proper way to achieve this, please prov ...

Tips for integrating a logo into router view animations

How can I incorporate a logo into the white fade-in screen that appears when navigating between subpages using <router-view> within App.vue? Here is a simplified version of my App.vue code: <template> <div id="app"> <div ...

how to switch page direction seamlessly in vue without triggering a page refresh

I'm looking to update the page direction in Pinia whenever a value changes, and while my code is functioning correctly, it reloads the page which I want to avoid. Take a look at my App.vue <script setup> import { useaCountdownStore } from " ...

Ways to delete a class in typescript

There's a menu on my website with li tags containing an a element for navigation. Everything is working fine, but I'm facing an issue where I need to remove all elements with the class seleccionado and only add it to the clicked li. I tried using ...

Comparison between instanceof and constructor.name

Background information: Currently, our application retrieves images from a GET API Accept: 'application/octet-stream', responseType: 'blob' and we utilize the following method to display the image on the user interface. let imageUrl ...

onmouseleave event stops triggering after blur event

I am facing an issue with a mouseleave event. Initially, when the page loads, the mouseleave event functions correctly. However, after clicking on the searchBar (click event), and then clicking outside of it (blur event), the mouseleave functionality stops ...

Building an AngularJS Service with TypeScript that is Non-Singleton: A Step-by-Step Guide

I need help converting an AngularJS Typescript Service into a Non-Singleton. Can anyone provide guidance on how to achieve this? (Note: This is not the same as other questions that focus on achieving this in JS) I have included some simple pseudo code be ...

Tips for creating an efficient folder structure for your Angular 6 project that can easily scale

Exploring the ins and outs of Angular 6 and its fundamental components has left me with a bit of confusion regarding the ideal folder structure to employ. I've delved into various tutorials from sources like Traversy Media, Udemy, and beyond, each adv ...

Exploring the functionality of a Vue component designed solely through a template

I currently have a basic Vue application set up: <!DOCTYPE html> <html> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'& ...

Putting a variable in a statement in Angular and TypeScript is a common task that can be accomplished

This snippet is written in Angular and involves using a variable to modify a conditional statement without having to repeatedly update every variable type. The code snippet below showcases an example in an HTML input tag. import { FormBuilder, FormGroup, V ...

How to conceal certain columns in Angular Material when in mobile view

I am currently utilizing an Angular Material table: <div class="table-container"> <table mat-table [dataSource]="dataSource" class="child"> <mat-divider></mat-divider> <ng-container matColumnDef="title" ...

The <router-view /> component in Vue is not displaying any content

After deploying this app on my server using sub directories like MyServer.com/vue/, everything seems to be running smoothly. There are no errors in the console. In my App.vue file, I only have <router-view /> displayed. Locally, the application work ...

How can I add a parameter to a JSON URL in Angular?

I'm looking to enhance my URL by adding a new parameter, but I'm unsure of the steps needed. ts.file route(name:string) { this.router.navigate(['/homepage', (name)]); console.log('name); } service private url1 = './assets/ ...