Is it possible to use Vue Router to create internal navigation for components added via a for loop?
┌────────────────────────────────────────────────────────────────┐
│ Application │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Router │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ View │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────┐ │ │ │
│ │ │ │ v-for component in components │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ │
│ │ │ │ │ Component │ │ Component │ │ │ │ │
│ │ │ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │ │ │
│ │ │ │ │ │ Router │ │ │ │ Router │ │ │ │ │ │
│ │ │ │ │ │ ┌──────────┐ │ │ │ │ ┌──────────┐ │ │ │ │ │ │
│ │ │ │ │ │ │ View │ │ │ │ │ │ View │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ └──────────┘ │ │ │ │ └──────────┘ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ └──────────────┘ │ │ └──────────────┘ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └──────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
I could use an internal state variable and a bunch of v-if statements to show the desired view but it would be nice to be able to define internal routes for the components to control the view state with vue-router.
I've tried adding a new router with createRouter()
but I can't figure out how I could use
the router for the component (like createApp().use(mainRouter);
)
Application view from main Router
<template>
<section class="home">
<car-panel v-for="item in itemList"
:id="item.id"
/>
</section>
</template>
<script>
import { ref, onMounted } from 'vue';
import Panel from '@/components/Panel.vue';
export default {
components: { Panel },
setup() {
const itemList = ref([]);
onMounted(() => {
// insert a random number of items above 1
for (var i = 0; i < Math.floor(Math.random() * 5) + 1; i++) {
itemList.value.push({
id: i.toString()
});
}
});
return {
itemList,
};
},
}
</script>
Panel
<template>
<article class="panel">
<panel-nav></panel-nav>
<router-view></router-view>
</article>
</template>
<script>
import { createRouter, createWebHashHistory } from 'vue-router';
import PanelNav from '@/components/PanelNav.vue';
export default {
name: 'Panel',
components: {
PanelNav,
},
props: {
id: { type: String },
},
setup() {
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/content', name: 'content', component: ContentView },
{ path: '/', redirect: 'content' },
],
});
return {};
},
}
</script>
EDIT 1
this hacky nonsense comes very close:
- added
:name="id"
to the<router-view>
- used
addRoute
with components object with the key as theid
<template>
<article class="panel">
<panel-nav></panel-nav>
<router-view :name="id"></router-view>
</article>
</template>
<script>
import { toRefs } from 'vue';
import { useRouter } from 'vue-router';
import PanelNav from '@/components/PanelNav.vue';
import ContentView from '@/views/ContentView.vue';
export default {
name: 'Panel',
components: {
PanelNav,
},
props: {
id: { type: String },
},
setup(props) {
const { id } = toRefs(props);
const router = useRouter();
let comps = {};
comps[id.value] = ContentView
// add a route to the /home route
router.addRoute('home', {
path: `${id.value}/content`,
components: comps,
});
return {};
},
}
</script>
This allows me to go to /home/0/content and this will add the ContentView
component to the Panel. However, as has been suggested by @jeremy castelli, I can only do this for a single Panel
at a time. So if I go to /home/0/content
, Panel 0 will show content. If I go to /home/1/content
, Panel 0 will stop showing content and Panel 1 will show it instead - not quite what I'm after
EDIT 2:
Ultimately I've given up on this idea for this project and going back to a reactive state
string and a series of on-view components with v-if="state === 'this-state'"
to manage them. If I find a better way, I may go back and refactor
EDIT 3 (final)
It's been recommended to put each sub component into an <iframe>
so they each have an individual URL.
This would require the panels to be a separate app but that's fundamentally what this is doing