Environment
"@strapi/strapi": "4.12.0",
"nuxt": "^3.6.5",
Current situation
In my development environment, I have a projects page with a category filter that can show either all projects or a category with the corresponding projects.
I am using useAsyncQuery to fetch the projects and categories from Strapi.
In the filter there is a dropdown with various categories. On click of those categories, the function switchCategory(filter.id) is called. In that function are various states updated using this .
See here the script setup:
<script setup> import { projectsQuery } from "~/queries/projects/archive"; import { filterQuery } from "~/queries/projects/filter"; const { data: filterRes } = await useAsyncQuery(filterQuery) const { data: projectsRes } = await useAsyncQuery(projectsQuery) var isLoading = useState('isLoading', () => false) var filterActive = useState('filterActive', () => false) var filterActiveText = useState('filterActiveText', () => 'Alle projecten') const projectsUrl = useState('projectsUrl', () => '/projecten/') const filters = useState('filters', () => filterRes.value.projectCategories.data) const projects = useState('projects', () => projectsRes.value.projects.data) var filteredProjects = useState('filteredProjects', () => projects) function switchCategory(id) { this.isLoading = true this.filterActive = false; document.getElementById("projects-container").scrollIntoView({ behavior: 'smooth', }); setTimeout(() => { if (id) { this.filters.map((item) => { if (id == item.id) { this.filteredProjects = item.attributes.projects.data; this.filterActiveText = item.attributes.Title; } }); } else { this.filteredProjects = this.projects; this.filterActiveText = 'Alle projecten'; } this.isLoading = false; }, 600) } And in the template element
<div class="flex flex-col gap-y-8 md:gap-y-24 lg:gap-y-40 transition-all duration-600 ease-in-out" :class="{ 'blur-0': !isLoading, 'blur': isLoading, }"> <div class="grid md:grid-cols-2 gap-8 md:gap-16 lg:gap-24 relative" v-for="(project, id) in filteredProjects" :key="id"> <Nuxt-Link class="absolute inset-0 w-full h-full z-10" :to="projectsUrl + project.attributes.Slug"></Nuxt-Link> <div class="flex flex-col items-start justify-between"> <div class="sticky top-40 pb-16 lg:pb-20 prose-xl"> <h3 class="text-xl md:text-4xl lg:text-5xl font-bold">{{ project.attributes.Title }}</h3> <p class="block">{{ project.attributes.Intro }}</p> </div> <Button class="flex mb-4" color="light" :to="projectsUrl + project.attributes.Slug"> Project bekijken </Button> </div> <div class="-order-1 md:order-1 relative"> <div class="aspect-video md:aspect-square lg:aspect-[12/18] relative"> <Media :url="project.attributes.FeaturedImage.data.attributes.url" :extension="project.attributes.FeaturedImage.data.attributes.ext" /> </div> </div> </div> </div> Problem
When I build the project, using nuxt build and then start a node server using node .output/server/index.mjs the projects get loaded in fine. But when I try to filter, trying to update the variables like isLoading and filterActive inside the switchCategory function, it throws me: TypeError: Cannot set properties of undefined (setting 'isLoading').
Fix attempt #1
I thought, maybe when it's built, it doesn't understand the this context (because this is also undefined when I try to console.log it). So I tried to refactor the code like this:
const switchCategory = (id) => { isLoading = true filterActive = false; document.getElementById("projects-container").scrollIntoView({ behavior: 'smooth', }); setTimeout(() => { if (id && filters) { filters && filters.value.map((item) => { if (id == item.id) { filteredProjects = item.attributes.projects.data; filterActiveText = item.attributes.Title; } }); } else { filteredProjects = projects; filterActiveText = 'Alle projecten'; } isLoading = false; }, 600) } I changed the normal function to an arrow function and removed the this.
In the setTimeout I had to change the filters.map to filters.value.map (not entirely sure why it changed format).
Code runs normal now (without errors), but it doesn't change update the DOM of the projects loop in the template.
Fix attempt #2
After searching and debugging for a pretty long time, I found in the Vue docs about defineExpose.
It mentions that script setup is "closed by default". I (desperately) added it to my script setup with all variables:
defineExpose({ filterRes, projectsRes, pageRes, isLoading, filterActive, filterActiveText, projectsUrl, filters, projects, filteredProjects, }) Unfortunately, and as expected, it didn't magically work.
------
Solution?
I am most likely looking at this in the wrong way, but I can't think of any other way on how to fix this.
Am I missing something?