Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

eggplantSword's avatar

Get computed value from another vue file

I'm trying to create a file that has a computed method that would return a list of items, I originally used a js file that had a static version of my list but now I need to show the items depending on permissions.

This is the original file

const DISTRIBUTION_PAGES = [
    {
        label: "Países",
        component: 'Distribution/Countries',
        href: "/distribution/countries",
    },
    {
        label: "Provincias",
        component: 'Distribution/Provinces',
        href: "/distribution/provinces",
    },
    {
        label: "Cantones",
        component: 'Distribution/Cantons',
        href: "/distribution/cantons",
    },
    {
        label: "Distritos",
        component: 'Distribution/Districts',
        href: "/distribution/districts",
    },
];

export default DISTRIBUTION_PAGES;

I'm new to vue 3, this is what I have to test which is basically nothing. I did see that when accessing methods you have to use a ref but in this case it's not a real component with a template so I'm not sure where to put the ref exactly or if it even matters.

<script setup>
 import {computed} from "vue";

 const DIST_PAGES = computed(() => {
     return 'here'
 })
</script>

I would like to do something like this in the component where I need the list

import Pages from '@/...'

//use like

{{pages.DIST_PAGES}} or {{pages}}

How can I make this work?

0 likes
8 replies
MohamedTammam's avatar

You can create a composable

Change your original file to be

import { computed } from "vue";

export function usePages() {
	const DISTRIBUTION_PAGES = [
		// ...
	]

	const DIST_PAGES = computed(() => {
		// Do your logic and return the array
		return DISTRIBUTION_PAGES;
	});

	return { DIST_PAGES}
}

In your VueJS component

<script setup>
import { usePages } from 'file/path';

const { DIST_PAGES } = usePages();


return { DIST_PAGES  };
</script>
eggplantSword's avatar

@MohamedTammam How can I use DIST_PAGES in the script?

ex

import {useDistributionPages} from "@/Pages/Distribution/dist-tabs";
const {DISTRIBUTION_PAGES} = useDistributionPages();

const menuItems = [
    {
        label: 'Distribución',
        items: DISTRIBUTION_PAGES
    }
]

This doesn't work and throws an error

Uncaught (in promise) TypeError: Converting circular structure to JSON --> starting at object with constructor 'ReactiveEffect' | property 'deps' -> object with constructor 'Array' | index 0 -> object with constructor 'Object' | property 'Set(7)' -> object with constructor 'Array' --- index 0 closes the circle at JSON.stringify

If I try to use it like this, it doesn't break but it also doesn't work right

 {
        label: 'Distribución',
        items: useDistributionPages()
    },

This is the output of the menu item

{ "label": "Distribución", "items": { "DISTRIBUTION_PAGES": [ { "label": "Distritos", "component": "Distribution/Districts", "href": "/distribution/districts", "perm": "district" } ] } }

I need it like this, without DISTRIBUTION_PAGES and as an array

{ "label": "Distribución", "items": [ { "label": "Distritos", "component": "Distribution/Districts", "href": "/distribution/districts", "perm": "district" } ]  }
eggplantSword's avatar

@MohamedTammam I'm not converting it myself as far as I can tell

import {useDistributionPages} from "@/Pages/Distribution/dist-tabs";
const {DISTRIBUTION_PAGES} = useDistributionPages();

const menuItems = [
    {
        label: 'Distribución',
        items: useDistributionPages()
    }
]

 <my-menu :show="showMenu" @close="showMenu = false;" :items="menuItems"></my-menu>

Menu component

<script setup>
import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogPanel,
} from '@headlessui/vue'

import {Link, usePage} from '@inertiajs/inertia-vue3';

const props = defineProps({
    items: {
        type: [Array, Object],
        default: []
    },
});

const emit = defineEmits(['close'])

const match_url = (menu_item) => {
    return usePage().component.value.startsWith(menu_item.component)
}
</script>

<template>
    <TransitionRoot appear show as="template">
        <Dialog @close="emit('close')" class="relative z-50">
            <TransitionChild
                as="template"
                enter="duration-300 ease-out"
                enter-from="opacity-0"
                enter-to="opacity-100"
                leave="duration-200 ease-in"
                leave-from="opacity-100"
                leave-to="opacity-0">
                <div class="fixed inset-0 bg-gray-900 bg-opacity-30"/>
            </TransitionChild>

            <div class="fixed inset-y-0 left-0 flex items-center justify-center">
                <TransitionChild
                    as="template"
                    enter="transition ease-in-out duration-200 transform"
                    enter-from="-translate-x-full"
                    enter-to="translate-x-0"
                    leave="transition ease-in-out duration-200 transform"
                    leave-from="translate-x-0"
                    leave-to="-translate-x-full">

                    <DialogPanel
                        class="w-72 h-full max-w-sm shadow-lg bg-gray-0 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-100 scrollbar-track-gray-00">
                        <div class="flex flex-col divide-y divide-gray-50">
                            <div v-for="item in items" :key="item.href">
                                {{item}}
                                <div v-if="item.items">
                                    <p class="px-4 py-2 inline-block text-sm font-semibold text-gray-400 uppercase bg-gray-00 w-full">
                                        {{ item.label }}
                                    </p>

                                    <div class="flex flex-col w-full divide-y divide-gray-100">
                                        <Link :href="menu_item.href" v-for="menu_item in item.items"
                                              :key="menu_item.href"
                                              class="p-4 flex items-center group w-full"
                                              :class="[match_url(menu_item) ? 'bg-teal-600' : 'hover:bg-white']">
                                            <p class="inline-block text-base font-semibold cursor-pointer"
                                               :class="[match_url(menu_item) ? 'text-teal-50' : 'text-teal group-hover:text-teal-600']">
                                                {{ menu_item.label }} - {{menu_item}}
                                            </p>
                                        </Link>
                                    </div>
                                </div>
                                <div v-else>
                                    <Link :href="item.href" class="p-4 flex items-center group w-full"
                                          :class="[match_url(item) ? 'bg-teal-600' : 'hover:bg-white']">
                                        <p class="inline-block text-base font-semibold cursor-pointer"
                                           :class="[match_url(item) ? 'text-teal-50' : 'text-teal group-hover:text-teal-600']">
                                            {{ item.label }}
                                        </p>
                                    </Link>
                                </div>
                            </div>
                        </div>
                    </DialogPanel>
                </TransitionChild>
            </div>
        </Dialog>
    </TransitionRoot>
</template>

MohamedTammam's avatar

@msslgomez Can you please show the code for useDistributionPages

And I believe this should be

const menuItems = [
        label: 'Distribución',
        items: DISTRIBUTION_PAGES
]

 <my-menu :show="showMenu" @close="showMenu = false;" :items="menuItems.items"></my-menu>
eggplantSword's avatar

@MohamedTammam

import {computed} from "vue";
import {usePage} from "@inertiajs/inertia-vue3";

export function useDistributionPages() {
    const PAGES = [
        {
            label: "Países",
            component: 'Distribution/Countries',
            href: "/distribution/countries",
            perm: "country"
        },
        {
            label: "Provincias",
            component: 'Distribution/Provinces',
            href: "/distribution/provinces",
            perm: "province"
        },
        {
            label: "Cantones",
            component: 'Distribution/Cantons',
            href: "/distribution/cantons",
            perm: "canton"
        },
        {
            label: "Distritos",
            component: 'Distribution/Districts',
            href: "/distribution/districts",
            perm: "district"
        },
    ]

    const DISTRIBUTION_PAGES = computed(() => {
        return PAGES.filter(dp => {
            return usePage().props.value.auth.read_perms[dp.perm]
        })
    });

    return {DISTRIBUTION_PAGES}
}
eggplantSword's avatar

@MohamedTammam it can't be that way the whole object is needed as it's not the only one, if you check the code for my menu I run through the item and then loop thought the items if they exist v-if="item.items" v-for="menu_item in item.items". This menu works with menu items that have items and also if they don't, it would change the way the menu items are displayed and would be incorrect.

Please or to participate in this conversation.