Inject content in component from other component using Vue - javascript

I have your usual admin dashboard (Core-UI) which I'm modifying for my own needs. I have an "aside" component into which I want to load MonitorAside.vue whenever I am on the Monitor page (I am using vue-router)
Here's a small rundown.
src/containers/Full.vue imports all the main components (including aside) and then uses router-view to render the view based on the route.
src/components/Aside.vue is the component in question. It contains fixed content but I want its content to dynamically be changed if another component requires to do so.
src/views/Monitor.vue is the page in question and thus the page which dynamically needs to inject/swap the content of the aside component. Note that this component is not imported in Full.vue but is rendered through the router there.
src/views/asides/MonitorAside.vue is the component I want to be loaded into Aside.vue whenever I am on the Monitor page.
How would I go about doing this?

So whenever vue-router navigates to a different page, you want the content of your Aside component to change? You can watch the $route value and then render the content depending on your route names, something like:
<template>
<div>
<monitor-aside v-if="page === 'monitor'"></monitor-aside>
<div v-else>
<!-- your default aside content -->
</div>
</div>
</template>
<script>
import MonitorAside from '../views/asides/MonitorAside.vue'
export default {
data() {
return {
page: null
}
},
methods: {
setContent(routeName) {
if (routeName === 'Monitor') {
this.page = 'monitor';
} else {
this.page = null;
}
}
},
created() {
this.setContent(this.$route.name);
},
watch: {
'$route'(to, from) {
this.setContent(to.name);
}
}
components: {
MonitorAside
}
}
</script>

Related

Laravel & Inertia.js - reactivity problem (vue3)

could it be that Inertia.js page components are blocking the reactivity of vue?
I have a Page component, in this component is a normal single file component.
I have a function that adds items to the ItemsManager.items object.
When I'm running this function the single component below doesnt adds this items in the v-for.
But when I'm reload the Page Component it works and the previously added items appear.
Here the single file component:
<template>
<div>
<div v-for="item in items" :key="item.$key">
test
</div>
</div>
</template>
<script>
import { ItemsManager } from "./utils.js";
export default {
name: "test-component",
data: () => ({
items: ItemsManager.items
}),
};
</script>
utils.js:
export const ItemsManager = {
items: [],
add(item) {
item.$key = this.items.length;
this.items.unshift(item);
},
};
function that adds the items (in page component):
addItem(title, options) {
ItemsManager.add({
name: title,
options: options
});
},
Thanks in advance!
Since you're using Vue2, you need to know that there are some caveats when adding/deleting things to Objects/Arrays. You don't show any code relevant to your actual way of adding stuff to your object, but I can still recommend that you'd check this page to understand and fix your issue.
https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

Vue - Pass components to router-view on dynamically loaded component

I need to render a different layout for the same route for a specific URI with different components depending on the user being on mobile or in desktop.
I would like to avoid having route path checks in the PageCommon(layout component) to keep it clean.
The app has a main component taking care of the layout, it has different router-views where we load the different components for each page URI. This would be a normal route for that.
{
path: '',
component: PageCommon,
children: [
{
path: '',
name: 'Home',
components: {
default: Home,
header: Header,
'main-menu': MainMenu,
'page-content': PageContent,
footer: Footer,
'content-footer': ContentFooter
}
},
I can't change the route components property once the component is loaded so I tried to make a wrapper and pass the components dynamically.
{
path: 'my-view',
name: 'My_View',
component: () => import('#/components/MyView/ViewWrapper')
},
In /components/MyView/ViewWrapper'
<page-common v-if="isMobile">
<my-mobile-view is="default"></my-mobile-view>
<main-menu is="main-menu"></main-menu>
</page-common>
<page-common v-else>
<my-desktop-view is="default"></my-desktop-view>
<header is="header"></header>
<main-menu is="main-menu"></main-menu>
<footer is="footer"></footer>
</page-common>
</template>
I would expect that the components passed inside page-common block would be substituted on the appropriate , but is not how it works, and Vue just loads page-common component with empty router-views.
Is there any approach for this?
Note that I already tried using :is property for loading different components, but the problem then is on how to tell the parent to use this or that component for this page. This is the code for that:
<template>
<component :is="myView"></component>
</template>
<script>
import DesktopView from "#/components/MyView/DesktopView";
import MobileView from "#/components/MyView/MobileView";
export default {
name: 'MyView',
components: {
DesktopView,
MobileView,
},
data(){
return {
myView: null,
isMobile: this.detectMobile()
}
},
methods : {
getViewComponent() {
return this.isMobile ? 'mobile-view' : 'desktop-view';
}
},
created() {
this.myView = this.getViewComponent();
}
}
</script>
I could use this approach for each of the PageCommon router views, creating a component for each that does the above, but it looks like a very bad solution.
A computed method is all you need.
You should have this top level Logic in App.vue and the <router-view> should be placed in both DesktopView and MobileView.
// App.vue
<template>
<component :is="myView"></component>
</template>
<script>
import DesktopView from "#/components/MyView/DesktopView";
import MobileView from "#/components/MyView/MobileView";
export default {
name: 'MyView',
components: {
DesktopView,
MobileView,
},
computed: {
myView() {
return this.detectMobile() ? 'mobile-view' : 'desktop-view';
}
}
}
</script>
You may also want to consider code splitting by setting up Dynamic Components for those layouts since Mobile will load Desktop View because it is compiled into final build, register them globally as dynamic imports instead if importing them in MyView and then delete components also after doing the following instead, this way only the one that is needed will be downloaded saving mobile users their bandwidth:
// main.js
import LoadingDesktopComponent from '#/components/LoadingDesktopComponent '
Vue.componenet('desktop-view', () => ({
component: import('#/components/MyView/DesktopView'),
loading: LoadingDesktopComponent // Displayed while loading the Desktop View
})
// LoadingDesktopComponent .vue
<template>
<div>
Optimizing for Desktop, Please wait.
</div>
</template>
<script>
export default {
name: 'loading-component'
}
</script>
Routing logic will only be processed when <router-view> is available,this means you can delay the presentation of Vue Router, for example you can have :is show a splash screen like a loading screen on any URI before displaying a component in :is that contains <router-view>, only than at that point will the URI be processed to display the relevant content.

How to dynamically update a var from a parallel component in vue.js

I have a 'views' page that imports two components, one of which is a NavBar that will display a loading animation until the other component is fully loaded in.
The way I'm trying to accomplish this, is I am trying to define a 'loading' var in the view, pass that var into the NavBar AND releases components. IF I change the loading to false from the releases component that should propagate over to the NavBar (to stop the loading animation).
views/Release.vue
<template>
<div>
<NavBar v-bind:loading="this.loading"></NavBar>
<div id="vue-main">
<h1><b>Releases</b></h1>
<Releases v-bind:loading="this.loading"></Releases>
<Footer></Footer>
</div>
</div>
</template>
<script>
import Releases from "../components/Releases.vue";
import NavBar from "./components/NavBar.vue";
export default {
name: "releases",
data () {
return {
loading: 'loading'
}
},
components: {
NavBar,
Releases,
}
};
</script>
components/NavBar.vue
<template>
<div>
<div id="nav">
<a href='/link1'>Link 1</a>
<a href='/link2'>Link 2</a>
<a href='/link3'>Link 3</a>
<pulse-loader :loading="this.loading"></pulse-loader>
</div>
</div>
</template>
<script>
import PulseLoader from 'vue-spinner/src/PulseLoader.vue';
export default {
name: 'NavBar',
props: ['loading'],
components: {
PulseLoader
},
};
</script>
I have left out Releases.vue from this post for brevity, but no matter where I set
this.loading=false
It does not seem to propagate over to NavBar component.
What am I doing wrong here? Not sure If I need to use $emit for something like this?
No, you should NOT modify the prop loading from Releases.vue.
In Releases.vue when data loaded, call $emit:
this.loadReleases()
.then(() => {
// Your logic.
this.$emit('loaded', true);
});
In the view Release.vue
<template>
<div>
<NavBar :loading="loading"></NavBar>
<div id="vue-main">
<h1><b>Releases</b></h1>
<Releases #loaded="updateLoading"></Releases>
<Footer></Footer>
</div>
</div>
</template>
<script>
import Releases from "../components/Releases.vue";
import NavBar from "./components/NavBar.vue";
export default {
name: "releases",
data () {
return {
loading: true,
}
},
components: {
NavBar,
Releases,
},
methods: {
updateLoading(val) {
this.loading = !val; // loading = false;
},
},
};
</script>
Please, use : instead of v-bind, # instead of v-on for making the code clear. And it's no need to use this on the template.
Do not use this in your templates.
<NavBar v-bind:loading="loading"></NavBar>
<div id="vue-main">
<h1><b>Releases</b></h1>
<Releases v-bind:loading="loading"></Releases>
<Footer></Footer>
<pulse-loader :loading="loading"></pulse-loader>
In fact, eveything in the template refers to this component, and you can't refer to anything else directly from the template.
$emit is for sending data up to the parent, and the main use case for that is to tell the parent to update a property that then flows back down to the component. Your use case is updating children, and using v-bind is appropriate, as the NavBar owns the data.

Nuxt Layout update data on every route

I am new to Nuxt/Vue and am getting confused about how data works between different pages. I want to display a light or dark logo depending on if I am on the index page or not. When navigating between pages the data doesn't update the route name so the logo doesnt change: Layout page below.
<template>
<main>
<img v-if="page != 'index'" src="~/assets/img/logo-white.svg">
<img v-else src="~/assets/img/logo-dark.svg">
<nuxt />
</main>
</template>
<script>
export default {
data () {
return {
page: this.$route.name
}
}
}
</script>
Any help would be great.
Thanks,
Jamie
In a nuxt layout, every page is rendered in place of the <nuxt /> tag.
You are setting the reactive property page in the layout which is mounted only once in the beginning, and all other pages are rendered inside it later. So when a page change occurs the layout doesn't re-mount and the value of page remains the same.
You can add a watcher on the route like this:
<script>
export default {
data () {
return {
page: this.$route.name
}
}
watch: {
'$route': function (value) {
this.page = value.name
}
}
}
</script>
So, now everytime the route changes, page will updated with the new name of the route.
Use a computed property instead of data, $route is reactive and will trigger a computed update.
computed: {
page() {
return this.$route.name
}
}
You could also just access $route in your template.
<img v-if="$route.name != 'index'" src="~/assets/img/logo-white.svg">

Change variable in component using vue.js

I have navbar blade, component with text and another components with page.
It works like I have component with text in navbar, and another component after navbar. That's three another components. How to change text from for example index.vue in text.vue?
That's what I have:
Text.vue:
<template>
<p class="title">{{msg}}</p>
</template>
<script>
export default {
props: [
'msg',
],
data() {
return {
}
},
mounted() {
},
methods: {
}
}
</script>
Component in navbar.blade.php:
<navbar-title></navbar-title>
And I try to change it in index.vue, that should work when we are on this page:
data() {
return {
msg: 'text',
}
But it doesn't work. How to do it correctly?
EDIT:
Vue.component('title', require('./components/Title.vue'));
To pass the message variable from your index.vue through navbar.vue to title.vue each needs to pass the property to the child and each child must pass the property on again all throughout the tree.
Something like this should work for your case: <title :msg="msg"></title>

Categories