WoJ
WoJ

Reputation: 30054

How to return a reactive variable from a setup() function?

I am building a small notes app and learning Vue3 + Typescript on the way. The code below allows to dynamically create and display an Array of notes (I hope I trimmed it down correctly, a general description is below the code):

<template>
  <q-layout>
    <div
        v-for="note in allNotes"
        class="note q-ma-sm q-gutter-sm"
        :id="note.id"
    >
      <q-editor
          v-model="note.text"
      >
      </q-editor>
    </div>

    <q-page-sticky position="bottom-right" :offset="[18, 18]">
      <q-btn fab icon="add" color="accent" @click="addNewNote"/>
    </q-page-sticky>
  </q-layout>
</template>

<script lang="ts">
import {defineComponent, ref} from 'vue'

export default defineComponent({
  name: 'App',
  components: {},
  setup() {

    // a single note
    interface Note {
      id: number
      creationDate: string
      text: string
      tags: string[]
      deleted: boolean
      isFocused: boolean
    }

    // all the notes in the app
    let allNotes = ref<Note[]>([])

    function addNewNote() {
      const now = new Date()
      allNotes.value.push({
        creationDate: now.toISOString(),
        deleted: false,
        id: now.getTime(),
        tags: [],
        text: "",
        isFocused: false
      })
    }

    function displayedNotes() {
      return allNotes
    }

    return {
      allNotes,
      addNewNote,
    }
  }
});
</script>

<style lang="scss">
</style>

The idea is that allNotes holds the notes, which are displayed in a v-for loop.

I wanted to make a small modification, in anticipation that the notes to be displayed will be filtered, to this I created a method displayedNotes that is supposed to simply return allNotes (later there will be some filtering happening there)

<template>
  <q-layout>
    <div
        v-for="note in displayedNotes"
        class="note q-ma-sm q-gutter-sm"
        :id="note.id"
    >
      <q-editor
          v-model="note.text"
      >
      </q-editor>
    </div>

    <q-page-sticky position="bottom-right" :offset="[18, 18]">
      <q-btn fab icon="add" color="accent" @click="addNewNote"/>
    </q-page-sticky>
  </q-layout>
</template>

<script lang="ts">
import {defineComponent, ref} from 'vue'

export default defineComponent({
  name: 'App',
  components: {},
  setup() {

    // a single note
    interface Note {
      id: number
      creationDate: string
      text: string
      tags: string[]
      deleted: boolean
      isFocused: boolean
    }

    // all the notes in the app
    let allNotes = ref<Note[]>([])

    function addNewNote() {
      const now = new Date()
      allNotes.value.push({
        creationDate: now.toISOString(),
        deleted: false,
        id: now.getTime(),
        tags: [],
        text: "",
        isFocused: false
      })
    }

    function displayedNotes() {
      return allNotes
    }

    return {
      allNotes,
      displayedNotes,
      addNewNote,
    }
  }
});
</script>

<style lang="scss">
</style>

The differences are

The end result is that nothing is displayed, displayedNotes is created but is empty when allNotes grow.

How should I return displayedNotes so that it is reactive?

Upvotes: 0

Views: 392

Answers (1)

Mirko t.
Mirko t.

Reputation: 491

You should create a computed property like this:

const filteredNotes = computed(() => displayedNotes())

In that case displayedNotes() might be renamed later on, and in that function you can add your filtering logic. If your allNotes then changes, that function within computed will be called again and will return the new filteredNotes

What you want to do now is only add filteredNotes to your return object, and use it in your v-for loop instead of allNotes

Upvotes: 1

Related Questions