`In Vue.js, it is not possible to access a data property within a

I encountered an issue while attempting to utilize a property of my data within a computed method in the following manner:

data() {
    return {
      ToDoItems: [
        { id: uniqueId("todo-"), label: "Learn Vue", done: false },
        {
          id: uniqueId("todo-"),
          label: "Create a Vue project with the CLI",
          done: true,
        },
        { id: uniqueId("todo-"), label: "Have fun", done: true },
        { id: uniqueId("todo-"), label: "Create a to-do list", done: false },
      ],
    };
  },
computed: {
listSummary() {
  const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
    .length;
  return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
},
},

However, both the IDE (Visual Studio Code) and the compiler raised an error:

Property 'ToDoItems' does not exist on type 'ComponentPublicInstance<{}, {}, {}, {}, {}, EmitsOptions, {}, {}, false, ComponentOptionsBase<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, {}>>'.

I'm currently following the vue.js tutorial provided by Mozilla (here) but using version 3 of Vue.js.

Could there have been any changes that would make this operation no longer feasible or require a different approach?

Thank you in advance.

Here is the complete code snippet:

<template>
  <div id="app">
    <h1>To-Do List</h1>
    <to-do-form @todo-added="addToDo"></to-do-form>
    <h2 id="list-summary">{{ listSummary }}</h2>
    <ul aria-labelledby="list-summary" class="stack-large">
      <li v-for="item in ToDoItems" :key="item.id">
        <to-do-item :label="item.label" :done="true" :id="item.id"></to-do-item>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import uniqueId from "lodash.uniqueid";
import { defineComponent } from "vue";
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm.vue";

export default defineComponent({
  name: "App",
  components: {
    ToDoItem,
    ToDoForm,
  },
  data() {
    return {
      ToDoItems: [
        { id: uniqueId("todo-"), label: "Learn Vue", done: false },
        {
          id: uniqueId("todo-"),
          label: "Create a Vue project with the CLI",
          done: true,
        },
        { id: uniqueId("todo-"), label: "Have fun", done: true },
        { id: uniqueId("todo-"), label: "Create a to-do list", done: false },
      ],
    };
  },
  methods: {
    addToDo(toDoLabel: string) {
      this.ToDoItems.push({
        id: uniqueId("todo-"),
        label: toDoLabel,
        done: false,
      });
    },
  },
  computed: {
    listSummary() {
      const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
        .length;
      return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
    },
  },
});
</script>

<style>
/* Global styles */
.btn {
  padding: 0.8rem 1rem 0.7rem;
  border: 0.2rem solid #4d4d4d;
  cursor: pointer;
  text-transform: capitalize;
}
.btn__danger {
  color: #fff;
  background-color: #ca3c3c;
  border-color: #bd2130;
}
.btn__filter {
  border-color: lightgrey;
}
.btn__danger:focus {
  outline-color: #c82333;
}
.btn__primary {
  color: #fff;
  background-color: #000;
}
.btn-group {
  display: flex;
  justify-content: space-between;
}
.btn-group > * {
  flex: 1 1 auto;
}
.btn-group > * + * {
  margin-left: 0.8rem;
}
.label-wrapper {
  margin: 0;
  flex: 0 0 100%;
  text-align: center;
}
[class*="__lg"] {
  display: inline-block;
  width: 100%;
  font-size: 1.9rem;
}
[class*="__lg"]:not(:last-child) {
  margin-bottom: 1rem;
}
@media screen and (min-width: 620px) {
  [class*="__lg"] {
    font-size: 2.4rem;
  }
}
.visually-hidden {
  position: absolute;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}
[class*="stack"] > * {
  margin-top: 0;
  margin-bottom: 0;
}
.stack-small > * + * {
  margin-top: 1.25rem;
}
.stack-large > * + * {
  margin-top: 2.5rem;
}
@media screen and (min-width: 550px) {
  .stack-small > * + * {
    margin-top: 1.4rem;
  }
  .stack-large > * + * {
    margin-top: 2.8rem;
  }
}
/* End global styles */
#app {
  background: #fff;
  margin: 2rem 0 4rem 0;
  padding: 1rem;
  padding-top: 0;
  position: relative;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
@media screen and (min-width: 550px) {
  #app {
    padding: 4rem;
  }
}
#app > * {
  max-width: 50rem;
  margin-left: auto;
  margin-right: auto;
}
#app > form {
  max-width: 100%;
}
#app h1 {
  display: block;
  min-width: 100%;
  width: 100%;
  text-align: center;
  margin: 0;
  margin-bottom: 1rem;
}
</style>

Answer №1

Brahim Boussadjra's response is "alright", however, it fails to directly address the issue at hand. user16362509 seems to be on the right track, but falls short of answering the specific question. The key here is to properly annotate the return types of both data properties and computed properties in order for TypeScript to properly understand the code. By providing explicit type annotations, you not only ensure stricter type checking but also enhance code clarity. While this issue may not arise when utilizing Vue3's composition API correctly, it does exist within the options API as observed. It is recommended to add annotations for better understanding and maintenance of the codebase. Interestingly, when specifying the return type of a computed method, Vue exhibits some quirks, yet it is considered good practice to make types explicit.

<script lang="ts">
import uniqueId from "lodash.uniqueid";
import { defineComponent } from "vue";
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm.vue";

export default defineComponent({
  name: "App",
  components: {
    ToDoItem,
    ToDoForm,
  },
  data(): { ToDoItems: Array<{id: *string*, label: string, done: boolean}> } {
    return {
      ToDoItems: [
        { id: uniqueId("todo-"), label: "Learn Vue", done: false },
        {
          id: uniqueId("todo-"),
          label: "Create a Vue project with the CLI",
          done: true,
        },
        { id: uniqueId("todo-"), label: "Have fun", done: true },
        { id: uniqueId("todo-"), label: "Create a to-do list", done: false },
      ],
    };
  },
  methods: {
    addToDo(toDoLabel: string): void {
      this.ToDoItems.push({
        id: uniqueId("todo-"),
        label: toDoLabel,
        done: false,
      });
    },
  },
  computed: {
    listSummary(): string {
      const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
        .length;
      return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
    },
  },
});
</script>

Answer №2

With Vue 3 already in use, consider leveraging the composition API along with script setup for enhanced support of TypeScript.

Check out the live demo

<template>
  <div id="app">
    <h1>To-Do List</h1>
    <form @submit.prevent="addToDo">
      <input type="text" ref="label" />
      <button type="submit">Add</button>
    </form>
    <h2 id="list-summary">{{ listSummary }}</h2>
    <ul aria-labelledby="list-summary" class="stack-large">
      <li v-for="item in ToDoItems" :key="item.id">
        <input type="checkbox" v-model="item.done" />
        {{ item.id }} {{ item.label }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
interface ToDoItem {
  id: string;
  label: string;
  done: boolean;
}
const label = ref(null)
const ToDoItems = ref<ToDoItem[]>([
  { id: 1, label: 'Learn Vue', done: false },
  { id: 2, label: 'Create a Vue project with the CLI', done: true },
  { id: 3, label: 'Have fun', done: true },
  { id: 4, label: 'Create a to-do list', done: false },
]);
const addToDo = () => {
  ToDoItems.value.push({
    id: ToDoItems.value.length + 1,
    label: label.value.value,
    done: false,
  });
  label.value.value = '';
};
const listSummary = computed(() => {
  return `${ToDoItems.value.filter((item) => item.done).length} out of ${ToDoItems.value.length} items completed`;
});
</script>

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

Issue with Flex property not being supported in Safari browser

.rq-search-container { position: relative; width: 100%; background: white; display: flex; flex-direction: row; align-items: flex-start; border-radius: 2px; z-index: 30; -webkit-flex: 1 0 15em; } While this code functi ...

Tips for integrating Material UI with useRef

There seems to be an issue with the code, but I haven't been able to pinpoint exactly what it is. My question is: How do you properly use useRef with Material UI? I am attempting to create a login page. The code was functioning fine with the original ...

"Every time you run mat sort, it works flawlessly once before encountering an

I am having an issue with a dynamic mat table that includes sorting functionality. dataSource: MatTableDataSource<MemberList>; @Output() walkthroughData: EventEmitter<number> = new EventEmitter(); @ViewChild(MatSort, { static: true }) sort ...

Do you have any recommendations for a jQuery plugin that can create a sleek horizontal scrolling image gallery?

Recently, I came across the Smooth div scroll plugin developed by Thomas Kahn, and it fits my requirements perfectly. However, I have encountered a bug that seems to be persisting. The issue arises when both mousewheel scroll and touch scroll are enabled s ...

Understanding the mechanism of callback function in NodeJS within the context of routes and controllers

Trying to grasp the concept of callbacks and puzzled by the recurring issue TypeError: callback is not a function Here's my router setup: // getPriceRouter.js router.post('/getPrice', function(req, res) { priceController.getPrice(req, ...

Having trouble with Vuex in Vue JS when trying to set up a modular store

Currently, I am in the process of building a web application using Vue along with Vuex. Despite being new to Vue, I am attempting to integrate Vuex into my Vue application. However, I am encountering an issue when using modularised Vuex. In my project, th ...

Unpacking Objects in JavaScript and TypeScript: The Power of Destructuring

I have a variable called props. The type includes VariantTheme, VariantSize, VariantGradient, and React.DOMAttributes<HTMLOrSVGElement> Now I need to create another variable, let's name it htmlProps. I want to transfer the values from props to ...

Set the class of the list item to "active" class

My approach to styling involves using a UL and li class to contain form selection options. I plan on hiding the radio button and using its value upon form submission. I have managed to enable checking the radio box when the <li> is clicked, but for s ...

Creating an interactive table with the power of FPDF and PHP

I have developed a unique Invoice System that allows users to customize the number of headings and fields using FPDF in conjunction with PHP. Subsequently, I can input the heading names and field values into HTML input fields. However, I am encountering a ...

c# JavaScriptConverter - understanding the deserialization of custom properties

I'm facing an issue where I have a JSON serialized class that I am trying to deserialize into an object. For example: public class ContentItemViewModel { public string CssClass { get; set; } public MyCustomClass PropertyB { get; set; } } Th ...

Is there a way to utilize Angular in identifying whether a given value corresponds with the value of a particular radio button within my application?

I have an array of lists that I need to display in radio buttons. Like this: https://i.stack.imgur.com/cmYgO.png https://i.stack.imgur.com/Zx4bm.png https://i.stack.imgur.com/jBTe3.png My goal is to have a checkbox that matches the values loaded from a ...

Encountering a 415 Error while making an ajax call using formData

I am attempting to make an ajax call with formData that includes a list of image files and some strings. However, the call is not successful and I am receiving error 415. Here is the code: var file = picChooser.files[0]; var jobExecutionImagesContext = n ...

Ways to retrieve the initial key object received through an AJAX request

I have a form with three dynamic dropdowns. The second dropdown depends on the first one, and the third dropdown depends on the second one. Therefore, selecting an option in the first dropdown will automatically populate the next two dropdowns. Below is t ...

Is there a way to restrict the type of the value returned by URLSearchParams.get() to a specific union type?

When handling a search parameter in the URL, such as ?mode=view, it is important to validate the value of mode to ensure it is either 'edit' or 'view'. To achieve this, a custom type called ModeTuple is created and converted to a union ...

Leverage AngularJS $http.get method to continuously fetch JSON array upon scrolling

Here is a snippet of code that utilizes AngularJS to retrieve a JSON response. I am looking for assistance in implementing a functionality where the page should make additional requests when the user scrolls to the bottom, continuing until the JSON array ...

What happens to the content within a <textarea> element if it is not enclosed between the opening and closing tags?

Here's a puzzling situation - I've entered the word Hello. into a <textarea>, and while I can see it on my screen, it doesn't show up between the opening and closing <textarea> tags. It may seem like a simple issue, but I'v ...

In a Django template, implement a checkbox feature in the list view to select multiple objects. Retrieve all selected checkbox objects for each pagination and display them in

In my HTML template, I have a list view with checkboxes and pagination. My goal is to retrieve all the checked box objects from each page's pagination and send them to the server (specifically the view part of Django). For example, if I check 4 object ...

Unable to connect to React Node Socket.IO application from any source other than localhost using IPv4 and NGROK

I recently followed a tutorial on creating a simple React app/Node with Socket.IO. The tutorial worked perfectly, so I decided to test it with two other devices: One PC on the same wifi network (via IPv4 - 192.168.1.8:3000) A mobile device using Ngrok tun ...

Error message in JavaScript saying "The response string is undefined

I am working on a program built in angularjs. Currently, I receive JSON data from the server when online, but I am now developing an offline mode for the application. Despite trying to tackle the issue, I am unable to identify why I cannot resolve it. In ...

Children cannot access parent prop if the prop is null

My current task involves building component sets for forms generated by a JSON array with objects like the ones outlined below. In order to populate and connect other fields, I must monitor the parent value object. { id: 'title', label: &apo ...