I have created the basic Vue component which is as follow.
<template>
<div class="example">
/* here render the component or HTML passed by pluing */
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'Example',
});
</script>
Now I need to create a plugin that will render any HTML or Component passed to the Example.I know the basic syntax of plugins and how to use them in the Vue but don't know how to do this task since I am a newbie to Vue.
export default {
install(vue, opts){
/* how to take the component or HTML and pass to `Example` Component */
}
}
I need to implement these in vue2.
You can use a slot to render an arbitrary HTML inside any of your components.
Your Example Component:
<template>
<div class="example">
<slot />
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'Example',
});
</script>
Then use it like this:
<template>
<example>
<div>Render me inside the slot</div>
</example>
</template>
And it will result in the following rendered HTML:
<div class="example">
<div>Render me inside the slot</div>
</div>
Then in you plugin you can use your Example component and pass any html you want to it
Update: this how your plugin might look like. Just create a global component inside of it
import Vue from 'vue'
const ExamplePlugin = {
install(Vue){
Vue.component('Example', {
name: 'Example',
template: '<div class="example"><slot /></div>'
})
}
}
Then you can Vue.use(ExamplePlugin) and Example component will be available globally in your application
Related
Problem: I am trying to programmatically register a component to be used in a slot in my Vue/Nuxt site. The component name is included in the data of the parent index.vue file, in this instance the component is named Projects. I am including it in a v-for template as the various objects in the 'modules' data array are iterated over. I had assumed this would be possible/easy from the dynamic component documentation and example however I have not managed to get it working in my case.
What I expect to happen: I expected the component to be registered and 'slotted' into the Module component correctly.
What actually happens: While I can see on the rendered view that the component is 'there', it is not in the correct place (i.e. slotted into the Module component). I also get a vue/no-unused-components error saying: The "Projects" component has been registered but not used.
I have read the documentation about component registration in modular systems but these seem to be for more complex cases than what I am trying to achieve. Any advice would be really helpful as I am totally stuck!
index.vue
<template>
<div>
<template v-for="module in modules">
<Module
:title="module.title"
:to="module.link"
/>
<component v-bind:is="module.slot" />
</Module>
</template>
</div>
</template>
<script>
import Module from '~/components/module/Module.vue'
import Projects from '~/components/module/slots/Projects.vue'
export default {
components: {
Module,
Projects
},
data () {
return {
modules: [
{
title: 'Work',
slot: 'Projects'
},
{
...
}
]
}
}
}
</script>
Edit: As a side note, I get the same error when registering the component with import like so:
components: {
Module,
'Projects': () => import('#/components/module/slots/Projects')
}
Module.vue
<template>
<div>
<h2>
{{ title }}
</h2>
<slot />
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
Projects.vue
<template>
<div>
<h3>Projects</h3>
</div>
</template>
<script>
export default {
name: 'Projects'
}
</script>
You use the self closure tag in your Module component.
This prevents your Projects component to be rendered within the slot.
Just replace:
<Module
:title="module.title"
:to="module.link"
/>
with:
<Module
:title="module.title"
:to="module.link"
>
and it should work.
I have a Vue component that imports and registers other components locally. I want to loop through components object and render them dynamically. I'm trying to achieve this like the following (inside .vue file):
<template>
<div v-for="component in components" class="component">
<component v-bind:is="component"></component>
</div>
</template>
<script>
import CarouselDefault from './carousel/CarouselDefault'
import AlertError from './alerts/AlertError'
import AlertInfo from './alerts/AlertInfo'
import AlertSuccess from './alerts/AlertSuccess'
import AlertWarning from './alerts/AlertWarning'
export default {
name: 'App',
components: {
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
}
}
</script>
and I get this error message:
Property or method "components" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
The components property aren't avalibale in template builder of vue, you have to defien a computed or a property of data .
example:
computed:{
components(){
return [
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
];
}
}
or
data(){
return {
components:[
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
]
}
}
I am currently using Algolia instantsearch for Vue, which is working fine. However, I am using multiple indexes, so I would like to be able to set a custom Title to each of my indexes.
This is my files:
Home.vue:
<ais-index :appId="angoliaAppId" :apiKey="angoliaAppKey" indexName="contacts" :query="query">
<search-results></search-results>
</ais-index>
<ais-index :appId="angoliaAppId" :apiKey="angoliaAppKey" indexName="users" :query="query">
<search-results></search-results>
</ais-index>
import SearchResults from "../search/Results";
export default {
data() {
return {
query: '',
angoliaAppId: process.env.MIX_ALGOLIA_APP_ID,
angoliaAppKey: process.env.MIX_ALGOLIA_SECRET,
};
},
components: {
SearchResults
}
};
And my component file, looks like this:
../search/Results:
<template>
<ais-results class="ais-Hits__root" v-if="searchStore.query.length > 0" :data="category">
<template slot-scope="{ result }">
<div>
<ais-highlight :result="result" attribute-name="name"></ais-highlight>
</div>
</template>
</ais-results>
</template>
import SearchResults from "../search/Results";
import {
Component
} from "vue-instantsearch";
export default {
mixins: [Component]
};
Now, as said, I would very much like to be able to set a custom title name to each of my <search-results> components, like this:
<search-results :title="My Contacts"></search-results>
Which would then for example add this in the component file:
<h1>{{ title }}</h1>
Seems like you'll have to use props
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
I am trying to insert a custom JavaScript (a sketcher) into a Vue component but no luck so far. When I try inserting javascript directly into a template like this:
<template>
<div id="sketcher">
var sketcher = new ChemDoodle.SketcherCanvas('sketcher', 400, 300);
</div>
</template>
I get an error: "Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as , as they will not be parsed."
My another attempt is to create a component in a different .js file and import it into a Vue component.
sketcher.js:
export default ({
mounted(){
return new ChemDoodle.SketcherCanvas('sketcher', 400, 300)
}
})
Home.vue
<template>
<div id="sketcher">
<app-sketcher></app-sketcher>
</div>
</template>
<script>
import sketcher from './sketcher';
export default {
components: {
"app-sketcher": sketcher
}
}
</script>
but I get this error: "Failed to mount component: template or render function not defined". I would highly appreciate any suggestions on how to fix this or to propose other options of embedding javascript into a Vue component.
Your first error message is self-explanatory: you cannot put a <script> tag in the <template> of a single-file Vue component.
In your second example, you aren't defining a template for the app-sketcher component when you register it in your Home.vue file. Your sketcher.js file exports just the definition for a mounted hook and nothing else.
You can make a Sketcher.vue file:
<template>
<div id="sketcher"></div>
</template>
<script>
export default {
mounted() {
return new ChemDoodle.SketcherCanvas('sketcher', 400, 300)
}
}
</script>
And then use import it and use it in your Home.vue file like so:
<template>
<app-sketcher/>
</template>
<script>
import AppSketcher from './Sketcher';
export default {
components: { AppSketcher }
}
</script>
Using Vue.js,
How to create componentA that gets componentB as a prop, and print it inside of it?
example:
index.vue
<template>
<div>
<componentA :componentPlaceHolder="componentB"></componentA>
</div>
</template>
<script>
import componentA from './compnentA.vue';
import componentB from './componentB.vue'
export default {
name: 'index',
components: {componentA,componentB }
}
</script>
componentA.vue
<template>
<div>
{{componentPlaceHolder}}
</div>
</template>
<script>
export default {
name: 'componentA',
props: {
'componentPlaceHolder': {}
}
}
</script>
There are some issues to your implementation:
You have gotten the scope wrong: componentPlaceHolder lives in the parent scope, not in that of component A. Read: Compilation Scope.
Use :is (i.e. v-bind: is) for dynamic component binding. The data bound should reference the key of the component.
Since you are nested additional components in another component in the same context, that means you have to interweave the content. This is done by using slots, declared in <component-a>.
Avoid using case-sensitive DOM elements, use kebab case instead, i.e. <component-a> instead of <componentA>, since HTML elements are case-insensitive (<componentA> and <componenta> will be treated the same).
Here is the updated code:
<template>
<div>
<component-a>
<customComponent :is="componentPlaceHolder"></customComponent>
</component-a>
</div>
</template>
<script>
import componentA from './componentA.vue';
import componentB from './componentB.vue'
export default {
name: 'index',
components: {
'component-a': componentA,
'component-b': componentB
},
data: {
componentPlaceHolder: 'component-b'
}
}
</script>
And then in your componentA.vue:
<template>
<div>
<!-- Slot will interweave whatever that is found in <componentA> -->
<slot></slot>
</div>
</template>
<script>
export default {
name: 'componentA'
}
</script>
Proof-of-concept example
If in doubt, here is a live proof-of-concept example:
var componentA = {
template: '#component-a'
};
var componentB = {
template: '#component-b'
};
new Vue({
el: '#app',
components: {
'component-a': componentA,
'component-b': componentB
},
data: {
componentPlaceHolder: 'component-b'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<component-a>
<!-- DOM elements in here will be interweaved into <slot> -->
<customComponent :is="componentPlaceHolder"></customComponent>
</component-a>
</div>
<template id="component-a">
<div>
<p>I am component A</p>
<slot></slot>
</div>
</template>
<template id="component-b">
<p>I am component B</p>
</template>
Footnote:
The VueJS readme is exceptionally composed, and I suggest here are some things that you can read up on that is very relevant to your use case:
Compilation Scope
Dynamic Components
Content Distribution with Slots