0
votes

I have layout like this:

.------------------------+----------------.
|  Current Tab Title     |      Controls  |
+------------------------+----------------+
|  Tab1 Tab2 [Tab3]                       |
+-----------------------------------------+
|  Main Content                           |
|                                         |
'-----------------------------------------'

The "Controls" field contents should logically belong to the current tab. But I cannot put it in the same Vue component because it should be rendered in the different place of application template. Also, tab title should depend on the current tab chosen.

I made Tab1 etc a <router-link> components. For Current Tab Title I abused the $route.name field. I can use named views to display Controls.

But I cannot understand how to have some controls in Controls block which would depend on and influence the state in current tab.

For example, for Tab1 we want to display a checkbox which will influence some behavior of the tab. So its state should be stored in Tab1 component, not in Tab1Controls component. But how could I connect them, i.e. pass current value of the checkbox from Tab1 instance to Tab1Controls instance, and pass an event back from Tab1Controls instance to Tab1 instance?

This could obviously be solved with Vuex but I would like to avoid it.

General recommendation is to move state up the hierarchy, but I don't think it is possible with router. And anyway, it will make things a mess, because checkbox data should belong to either Tab1 or Tab1Controls, and has nothing to do with other tabs.

1
You're talking about application level state management which is exactly what Vuex is for. Why not use it? The alternative is an event-bus but that's really just a hackPhil
Why isnt it possible with router? <router-view @emittedEvent="handle" /> is validEstradiaz
@Estradiaz because <router-view> is a wrapper that loads components within. It does not bind any event listeners to those componentsPhil
@Phil i thought passing data.attrs would do this? github.com/vuejs/vue-router/blob/…Estradiaz
@Estradiaz in that case, you might be right. I feel like I tried it ages ago and it didn't work but perhaps now it doesPhil

1 Answers

0
votes

Here is an approach I came to. It is far from ideal but at least it works and allows me to keep state where it should belong.

First, I created a BaseTemplate component which contains most of the markup of the app. I.e. the App component holds just a bare minimum, and most of the body (including immutable parts) is moved to BaseTemplate:

// BaseTemplate.vue
<template>
<div>
  <header>
    <slot name="title">{{ title }}</slot>
    <slot name="controls"/>
  </header>
  <nav>
    <router-link ...>
    <router-link ...>
  </nav>
  <main>
    <slot/>
  </main>
</div>
</template>

<script>
export default {
  props: ['title'],
};
</script>
// Tab1.vue
<template>
  <BaseTemplate title="First Tab">
    <template #controls>
      ...
    </template>

    main content goes here...
  </BaseTemplate>
</template>
...

This way, all props related to Tab1 belong to Tab1 component, and at the same time common markup is moved to the separate component.

Now to the downsides. Unlike the "normal" setup where common controls/navigations are outside the route-view, in this setup they are in fact different component instances for each tab. So they blink when switching. Not sure if it can be mitigated somehow.