How to import a component as prop from another component? - vue.js

It is possible to import a component as prop from another component?
For example:
Q-Dialog
<template>
<q-dialog>
<q-layout>
<q-page-container>
<myCustomComponent />
</q-page-container>
</q-layout>
</q-dialog>
</template>
<script>
//Edited:This works, but I want to register dynamically from props
//import myCustomComponent from "components/MyCustomComponent.vue";
import myCustomComponent from this.myComponent;
export default {
props: ["myComponent"],
components: { myCustomComponent }
}
Another component:
this.$q.dialog({
component: CustomComponent, //dialog
myComponent: 'components/MyCustomComponent.vue'
})
Edited, for better clarify what I am trying to achieve in this case:
My dialog is an abstract component in which an unlimited number of different myCustomComponent can be rendered.
To achieve this, I need that the registration of each component (import) is not done in the q-dialog.
A solution to consider is to register each component in the file from which the q-dialog is loaded for rendering (different from the q-dialog, in my case the another component file) and then pass that path from the imported file to the q-dialog, possibly as props.
Is this possible?
Edited with solution:
Parent component
<script>
import registeredComponent from "components/MyCustomComponent.vue";
export default {
data() {
return {
myComponent: registeredComponent
}
}
methods: {
btnClickShowDialog(){
this.$q.dialog({
component: dialogComponent,
//pass registered component as prop to dialog
myCustomComponent: this.myComponent
})
}
}
</script>
Q-dialog
<template>
<q-dialog>
<q-layout>
<q-page-container>
<component :is="myCustomComponent" />
</q-page-container>
</q-layout>
</q-dialog>
</template>
<script>
export default {
props: ["myCustomComponent"]
}
</script>

In your q-dialog component you can use the component tag to dynamically render a passed in component prop. See this stackblitz.
// q-dialog html
<component :is="myComponent" />
In your parent component you'll want to import the desired component, assign it to a data property and pass it in
// parent component js
import SomeComponent from './SomeComponent.vue'
data () {
return {
passedInComponent: SomeComponent
}
}
// parent component html
<q-dialog :my-component="passedInComponent" />

Related

Vue: Reuse loading spinner template and logic

I've multiple (20+) pages where I need the following code:
<template>
<template v-if="isLoading">
<Spinner full size="medium" />
</template>
<template v-else>
<p>Page data</p>
</template>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
I don't want to rewrite this part everytime I need it so is there a way to create something like a higher order component with access to the computed method and a way to add the hoc to the script tag of the different vue files? Or another way to archive this?
I could recommend extending the spinner component where you want to show the spinner. I've created a boilerplate setup that show a very simple implementation of this approach here.
The main idea is to expose a default slot for you spinner component, and wrap the page component in that slot.
<template>
<div>
<Spinner v-if="isLoading" full size="medium" />
<!-- Slot for component data -->
<slot v-else></slot>
</div>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
Then in any component that you want to show the spinner:
<template>
<spinner>
<!-- Pass here the component content to be shown after the spinner is hidden -->
</spinner>
</template>
<script>
import Spinner from "./spinner";
export default {
name: "Page1",
extends: Spinner,
components: { Spinner }
};
</script>

Why Vue 2 remove every thing when using :is prop override component?

Please see this minimum example, I have a simple component called HelloWorld.vue
HelloWorld.vue
<template>
<div class="foo">bar</div>
</template>
When I using this component like this
App.vue
<template>
<HelloWorld />
</template>
<script>
import HelloWorld from "./HelloWorld.vue";
export default {
components: {
HelloWorld,
},
};
</script>
This rendered HTML looks like this
Rendered HTML
<div class="foo">bar</div>
However, when I add :is prop, rendered HTML changed
App.vue
<template>
<HelloWorld is="h2" />
</template>
<script>
import HelloWorld from "./HelloWorld.vue";
export default {
components: {
HelloWorld,
},
};
</script>
Rendered HTML
<h2></h2>
Why is this happening?
Is it possible to overwrite only the outer HTML tag just like the class and style prop?
is should be used together with the component element:
<component is="h2"></component>
<HelloWorld is="h2" /> efficiently renders h2 instead of HelloWorld.
In order for root element to be configurable, the component should provide this:
<template>
<component :is="tag" class="foo">bar</div>
</template>
<script>
export default {
props: {
tag: {
type: String,
default: 'div'
}
}
}
</script>

How to use custom input component in vuejs?

While using Bootstrap-Vue as UI framework, I am trying to make a custom form component and use it in several parent components. Here is my form component
<template>
<b-form>
<b-input-group>
<b-form-input placeholder="Post Title"></b-form-input>
<wysiwyg-input placeholder="Post Content" />
</b-input-group>
</b-form>
</template>
and the parent component is
<FormFields :title="title" :content="content" />
How can i access the value in parent component from child component.
Note: I am using vue-quill editor as well.
Here is the codesandbox link:
https://codesandbox.io/s/mystifying-benz-w8wgu?file=/src/App.vue
Thanks in advance !
Define a prop on your child component and pass the data to it when you use it in the parent:
// ChildComponent.vue
export default {
props: {
someData: {}
}
}
// ParentComponent.vue
<template>
<child-component :some-data="myData"></child-component>
</template>
<script>
export default {
data() {
return {
myData: {...}
}
}
}
</script>

Dynamic component and conditional rendering in Vue 2

Recently I've played with dynamic components with Vue 2.
Let's say we have two components SignIn and SignOut that must be conditionally rendered.
import SignIn from '~/components/SignIn.vue
import SignUp from '~/components/SignUp.vue
export default {
components: {
SignOut,
SignIn
}
data() {
return {
condition: true
}
}
I usually use the Vue conditional render pattern as follow:
<template>
<SignIn v-if="condition" />
<SignUp v-else />
</template>
But I can achive the same result with dynamic components pattern.
<script>
data() {
return {
condition: true,
component: this.condition ? 'SignIn' : 'SignUp'
}
}
</script>
<template>
<component :is="component" />
</template>
What do you think about it?

Vue: How to supply the layout component with dynamic props depending on router-view component's data

I'd like to have a layout component with a header component and a <router-view>, but the header component should receive different dynamic props depending on the data of the router-view component.
I'm agnostic to the way in which the layout architecture is being achieved: Either with nested routes or using a slot and passing the layout name via the route's meta field.
So, the header component should be passed in a dynamic prop from the view component - let's say Home.vue: If we go to route /home the router-view will be Home.vue and in Home.vue we have a reactive data field (i.e. scrolledDown) and the prop passed to the header component (i.e. theme) should depend on the value of this data field (scrolledDown).
The easiest way I can think of is using local state management (vuex or whatever), so that the view components (i.e. Home.vue) can store the reactive data and the layout component can grab it and pass it as prop to the header component. But maybe there are much better ways to accomplish this?
MainHeader.vue:
<template>
<div :class="`header--${theme}`">...</div>
</template>
<script>
export default {
props: ["theme"]
}
</script>
Layout.vue:
<template>
<div>
<main-header />
<router-view />
</div>
</template>
<script>
import MainHeader from "../MainHeader";
export default {
components: { MainHeader }
}
</script>
or alternatively:
App.vue:
<template>
<component :is="layout">
<router-view />
</component>
</template>
// script with `layout` data prop and layout component imports
Layout.vue:
<template>
<div>
<main-header />
<slot />
</div>
</template>
<script>
import MainHeader from "../MainHeader";
export default {
components: { MainHeader }
}
</script>
Home.vue:
<template>
<div>...</div>
</template>
<script>
export default {
computed: {
scrolledDown() { ... }
}
}
</script>