const CustomComponent = {
props: ['index'],
template: `<span>I am a custom component: {{ index }}</span>`
};
const UserInputResult = {
components: {
CustomComponent
},
props: ['templateString'],
template: `<section v-html="templateString"></section>`
}
const app = new Vue({
el: '#app',
data(){
return {
userInput: 'user input example [:component-1]'
}
},
components: {
UserInputResult
},
methods: {
generateTemplate(){
let raw = this.userInput;
if (!!raw && raw.match(/\[\:component\-\d+\]/g)) {
let components = [...raw.match(/\[\:component\-\d+\]/g)];
components.forEach(component => {
raw = raw.replace(component, `<custom-component :index="${component.match(/\d+/)[0]}"></custom-component>`);
});
}
return raw;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<textarea v-model="userInput"></textarea>
<user-input-result :template-string="generateTemplate()">
</div>
I want to render a custom component which has a dynamical template base on user input.
when user input a specific string ([:component-1]), it will be render as a component (CustomComponent)
how to achieve this?
Thanks a lot for anyone help!
You should look into v-slot
https://v2.vuejs.org/v2/guide/components-slots.html
Example:
Parent:
<child-component v-html="myTemplate">
<span>From parent</span>
</child-component>
Child:
<div>
<v-slot></v-slot> //Will output "<span>From parent</span>"
</div>
**Added more explaination
You can then condition check and update myTemplate to your desired template. "<span>From parent</span>" is just there for explanation on how slot works.
updated by the questioner
const CustomComponent = {
props: ['index'],
template: `<span>I am a custom component: {{ index }}</span>`
};
const UserInputResult = {
template: `<section><slot></slot></section>`
}
const app = new Vue({
el: '#app',
data(){
return {
userInput: 'user input example [:component-1]'
}
},
components: {
UserInputResult,
CustomComponent
},
methods: {
generateTemplate(){
let raw = this.userInput;
if (!!raw && raw.match(/\[\:component\-\d+\]/g)) {
let components = [...raw.match(/\[\:component\-\d+\]/g)];
components.forEach(component => {
raw = raw.replace(component, `<custom-component :index="${component.match(/\d+/)[0]}"></custom-component>`);
});
}
return raw;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<textarea v-model="userInput"></textarea>
<user-input-result>
{{ generateTemplate() }}
</user-input-result>
</div>
I figured it out by using Vue.complie
according to dynamically-fetch-and-compile-a-template-with-nuxt
const UserInputResult = {
props: ['templateString'],
render(h){
return h({
components: {
CustomComponent
},
template: `<section>${this.templateString}</section>`
});
}
}
Related
I made two components and tried to show these in vue3 app.
my code in html
<div id="app">
<image_preview>
URL: [[image]]
</image_preview>
<file_uploader>
Counter:[[counter]]
</file_uploader>
</div>
in javascript
const ImagePreview = {
data(){
return {
image:"test.png"
}
},
mounted() {
},
delimiters: ['[[', ']]']
}
const Counter = {
data() {
return{counter: 0}
},
mounted() {
setInterval(() => {
this.counter++
}, 1000)
},
delimiters: ['[[', ']]']
}
Vue.createApp({
components:{
"image_preview":ImagePreview,
"file_uploader":Counter
}
}).mount('#app')
However nothing appears in html
Where am I wrong?
please re-read vue's documentation on components you know if you're going to need a template to render components and I bet you haven't read vue's documentation on components
follow my example and it takes care of your problem:
// </script><script type="module">
import { createApp, ref, onMounted } from 'https://unpkg.com/vue#3/dist/vue.esm-browser.js'
const ImagePreview = {
template: '#image_preview',
setup() {
return {
image: 'test.png'
}
}
}
const Counter = {
template: '#file_uploader',
setup() {
const counter = ref(0)
onMounted(() => setInterval(() => counter.value++, 1_000))
return { counter }
}
}
const app = createApp({
components:{
"image_preview": ImagePreview,
"file_uploader": Counter
}
})
.mount('#app')
<div id="app">
<image_preview>
URL: [[image]]
</image_preview>
<file_uploader>
Counter:[[counter]]
</file_uploader>
</div>
<template id="image_preview">
URL: {{ image }}
</template>
<template id="file_uploader">
Counter: {{ counter }}
</template>
require('./bootstrap');
window.Vue = require('vue');
Vue.component('exampleComponent1', require('./components/exampleComponent1.vue'));
Vue.component('exampleComponent2', require('./components/exampleComponent2.vue'));
const app = new Vue({
el: '#app'
});
from the above code, I want to pass data from exampleComponent1 to exampleComponent2 when some event has occurred in exampleComponent1.
What is the optimal solution for this ??
The key here is to set their parent component as the one receiving from the first (using emit) and sending to the second (using props):
const component1 = Vue.component('component1', {
template: '#component1',
data() { return { name: '' } },
methods: {
updateName() { this.$emit("namechanged", this.name); }
}
});
const component2 = Vue.component('component2', {
template: '#component2',
props: ['name'],
});
new Vue({
el: "#app",
components: { component1, component2 },
data() { return { name: '' } },
methods: {
updateName(newName) { this.name = newName; }
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div><component1 #namechanged="updateName"/></div>
<div><component2 :name="name"/></div>
</div>
<template id="component1"><input v-model="name" #input="updateName"/></template>
<template id="component2"><p>Input From component 1: {{name}}</p></template>
You can use a Event Bus for this.
// in some global file
const EventBus = new Vue();
// creating or emitting event
EventBus.$emit('someEvent', 'some-data')
// listen to the event
EventBus.$on('someEvent', function(data) {
console.log(data) // 'some-data
})
I've just started learning Vue. Now I'm trying to build my first component, but the template didn't show up, and the console didn't give me any error.
Here's some of my code:
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
and here's a Jsbin with my full code
Your Vue instance needs a mounting id specified in the el property:
var vm = new Vue({
el: '#app', // Specifying a DOM id "app"
data() {
// ...
}
})
And the app template needs to be wrapped with that same id:
<div id="app">
<button-counter></button-counter>
</div>
Demo:
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
var vm = new Vue({
el: '#app',
data() {
return {
name:'yazid',
age:'20',
date:'press to known the date',
skills:['HTML','CSS','JS'],
ele:'<h1> elements from vue js</h1>',
completed_languages:[{
lang:'html',
percent:'90%'
},{
lang:'CSS',
percent:'70%'
},{
lang:'JS',
percent:'70%'
}]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button-counter></button-counter>
</div>
In the following example, I would like a component that doubles a number. This number is passed not as a property, but as content. How is it possible to get the content value in Vue?
var twice = {
template: '<div>{{ value }}</div>',
computed: {
value() {
return parseInt(this.$slot) * 2;
}
}
};
new Vue({
el: '#app',
components: {
twice: twice
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="app">
<twice>21</twice>
</div>
You could access the default slot text by this.$slots.default[0].text :
var twice = {
template: `<div>
number : <slot></slot><br>
<div>double : {{ value }}</div>
</div>`,
computed: {
value() {
return parseInt(this.$slots.default[0].text) * 2;
}
},
mounted() {
console.log(this.$slots.default[0].text)
}
};
new Vue({
el: '#app',
components: {
twice: twice
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<twice>21</twice>
</div>
I am playing with an example given from vue-router documentation
HTML:
<div id="app">
<h3>{{title}}</h3>
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>
JavaScript:
const Foo = { template: '<div>foo</div>', data: {title: "Foo" }
const Bar = { template: '<div>bar</div>', data: {title: "Bar" }
Is it possible to access selected component data outside anchor and update {{title}} header?
Full example is here
I've two possibilities. First one is to edit your parent data directly with this.$parent.title.
And the second way is to trigger an event this.$parent.$emit('changeTitle', 'foo');.
I think the last one is better, because the control of your state is always in your parent component.
const Home = {
template: '<div>Home</div>',
data: {
title: "Home"
},
mounted() {
this.$parent.title = 'home'; // directly
}
}
const Foo = {
template: '<div>Foo</div>',
data: {
title: "Foo"
},
mounted() {
this.$parent.$emit('changeTitle', 'foo'); // emit an event
}
}
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
component: Home
},
{
path: '/foo',
component: Foo
}
]
})
new Vue({
router,
el: '#app',
data: {
title: 'Initial'
},
destroy() {
this.$off('changeTitle');
},
created() {
const _self = this;
this.$on('changeTitle', function(newTitle) {
_self.title = newTitle;
});
}
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h3>{{title}}</h3>
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>