Is there a way, similar to KnockoutJS to quick and easy render content from a template by Id?
<script type="text/html" id="template-example"><span>Hello world!</span></script>
<div data-bind="template: '#template-example'"></div>
I've read the documentation but did not find anything, and also tried to create a component called quick-template where the template attribute used a vale from props, but I did not render anything. Probably because the template is bound before props are populated
Version 2.6.12
When it comes to Vue, the only reputable source is the documentation.
In theory:
The template is a core part of each vue component. In fact, a vue component won't render without one (or without a render function - which I consider just another way of providing a template, since that's what it returns).
It can can be provided as:
template: rawHTML (HTML string)
template: '#some-id' (where #some-id points to a <template>, <script type="text/x-template"> or plain DOM element).
<template>content</template> - in SFCs
render function (returns an HTML string). Overrides any other form of providing a template.
el: '#some-id', the template consists of the .innerHTML of the DOM element with that id 1, 2.
I won't cover render functions, they're not relevant for your case. Vue parses both HTML and JSX. Vue loader accepts pre-processors, which makes it compatible with PUG, using a plugin.
In practice:
Register the templates as components, with the desired template attribute, alongside anything else they might need (props, data, computed, methods, components, directives, filters, emits, watch, expose, compilerOptions, inheritAttrs, mixins, extends and/or lifecycle hooks).
Use them in your app.
Demo 3:
Vue.component('some-list', {
template: '#some-list',
props: ['powers']
})
Vue.component('some-item', {
template: '#some-item',
props: ['value']
})
Vue.component('svg-example', {
template: '#svg-example'
})
new Vue({
el: '#app',
data: () => ({
myComponents: ['some-list', 'svg-example'],
powers: 8
}),
methods: {
getPowers(comp) {
return (comp === 'some-list') && Number(this.powers)
}
}
})
#app {
display: flex;
justify-content: space-evenly;
}
#app input {
height: min-content;
}
#app div > div {
padding: 3px 7px;
}
<script src="https://unpkg.com/vue#2.6.14/dist/vue.min.js"></script>
<div id="app">
<span v-text="powers"></span>
<input type="range" v-model="powers" min="2" max="12">
<component v-for="component in myComponents"
:powers="getPowers(component)"
:key="component"
:is="component" />
</div>
<template id="some-list">
<div>
<some-item v-for="(item, key) in powers" :key="key" :value="item" />
</div>
</template>
<template id="some-item">
<div v-text="Math.pow(2, value)" />
</template>
<template id="svg-example">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 300 200"
width="300">
<image href="https://picsum.photos/300/200" />
</svg>
</template>
Same example 4, 5, registering the components on the app, not globally on Vue:
new Vue({
el: '#app',
components: {
SomeList: {
template: '#some-list',
props: ['powers'],
components: {
SomeItem: {
template: '<div v-text="Math.pow(2, value)" />',
props: ['value']
}
}
},
SvgExample: {
template: '#svg-example'
}
},
data: () => ({
powers: 8
})
})
#app {
display: flex;
justify-content: space-evenly;
}
#app input {
height: min-content;
}
#app div > div {
padding: 3px 7px;
}
<script src="https://unpkg.com/vue#2.6.14/dist/vue.min.js"></script>
<div id="app">
<span>{{powers}}</span>
<input type="range" v-model="powers" min="2" max="12">
<some-list :powers="Number(powers)"></some-list>
<svg-example></svg-example>
</div>
<script type="text/x-template" id="some-list">
<div>
<some-item v-for="(item, key) in powers" :key="key" :value="item" />
</div>
</script>
<!-- innerHTML of this hidden div is used as template -->
<div id="svg-example" style="display: none">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 300 200"
width="300">
<image href="https://picsum.photos/id/4/300/200" />
</svg>
</div>
Notes:
1 - Only works inside new Vue({}).
2 - The placeholder (initial DOM element) will be replaced with the result of Vue's mounting process, so any event registered on the element prior to mounting will be lost.
3 - The template can be anywhere in DOM, not necessarily inside the Vue app.
4 - Less flexible (SomeItem component needs to be declared inside SomeList component, if declared in App, <SomeList /> can't use it - not sure why, I'd have expected it to be available to child components).
5 - I've tweaked a few things with alternate syntaxes between the two examples, so you could compare them
You could register a custom component and use the content of the script tag as template.
Vue.component('my-component', '#template-example');
Then somewhere in your Vue app:
<div id="app">
<my-component></my-component>
</div>
Related
Good day everyone! I'm currently learning vue and I'm following a youtube tutorial on building an image carousel from scratch. Here's the code for the Image Carousel parent component:
<template>
<div class = "slides">
<div class = "slides-inner">
<div v-for= "slide in slides">
<Slide v-bind:slide= "slide"></Slide>
</div>
</div>
</div>
<script>
import Slide from './Slide';
export default{
data(){
return{
slides:[
{ src:'src/assets/slide1.jpg' },
{ src:'src/assets/slide2.jpg' },
{ src:'src/assets/slide3.jpg' },
{ src:'src/assets/slide4.jpg' },
{ src:'src/assets/slide5.jpg' }
]
}
},
components: {
Slide
}
}
</script>
<style scoped>
.slides {
align-items: center;
background-color: #666;
color: #999;
display: flex;
font-size: 1.5rem;
justify-content: center;
min-height: 10rem;
}
</style>
and here's the code for the individual image slides:
<template>
<div class="slide">
{{slide.src}}
</div>
</template>
<script>
export default {
data(){
return{}
},
props: {
slide: ['slide']
}
}
</script>
<style scoped>
</style>
The v-bind on the v-for loop on the parent Image Carousel component is supposed to bind the slide.src property to the current slide being looped so that it will display the image in the browser but what I'm getting is a blank browser screen and an error that says the right value of the operator at v-bind is not an object although the tutorial I'm following works exactly as it should with this same code so I'm wondering what I'm doing wrong.
In your slide component, it should be
export default {
data(){
return{}
},
props: {
slide: Object
}
}
You can check valid props type in Vue document
Please also see the Vue Style Guide as prop definitions should be as detailed as possible. An Essential Requirement by the creators of Vue. See Vue Style Guide.
I am working on a simple Vue app, using vue-cli and webpack for that purpose.
So basicly i have 2 components, a parent and a child component ~
like this:
<template>
<div class="triPeaks__wrapper">
<div class="triPeaks">
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
</div>
<div class="triPeaks__line">
<tower-line :towerLine="towerLineCards" />
</div>
<tri-pack />
</div>
</template>
the towerLineCards is the important thing there, it is a prop that is passed to the tower-line component, it is basicly a array with 10 elements, it is a array with 10 numbers that are shuffled, so it can be something like that:
[1,5,2,6,8,9,16,25,40,32]
this array is create via beforeMount on the lifecycle.
On the child component:
<template>
<div class="towerLine-wrapper">
<div class="towerLine">
<img v-for="index in 10" :key="index" class="towerLine__image" :src="getImage(index)" alt="">
</div>
</div>
</template>
<script>
export default {
props: {
towerLine: {
type: Array,
required: true
}
},
method: {
getImage (index) {
return '#/assets/images/cards/1.png'
}
}
}
</script>
<style lang="scss">
.towerLine {
display: flex;
position: relative;
top: -90px;
left: -40px;
&__image {
width: 80px;
height: 100px;
&:not(:first-child) {
margin-left: 3px;
}
}
}
</style>
the issue is with the :src image that i am returning via the getImage(), this way it is not working. If i change to just src it works just fine, i did this way just to test, because the number in the path should be dynamic when i got this to work.
What is wrong with this approach? any help?
Thanks
Firstly, you should use a computed property instead of a method for getImage().
And to solve the other problem, you could add require(YOUR_IMAGE_PATH) when you call your specific image or put it inside /static/your_image.png instead of #/assets/images/cards/1.png.
Here is my fiddle : DEMO
new Vue({
el: '#app',
data: {
modules: ['abc', 'def']
}
})
How can I hide/show with v-if based on the values found in an array ( modules[] ) ?
Any help would be appreciated.
Thank you.
You have at least a couple of options to manage this, which I'll describe here. The first is a simple check inside of v-if to see if the item is contained within modules. For example:
<div class="abc box" v-if="modules.indexOf('abc') >= 0">abc</div>
<div class="def box" v-if="modules.indexOf('def') >= 0">def</div>
...
This is a little bit crude and not great from a maintenance or performance perspective. Another option would be to use the modules array as the source for your collection of divs, using v-for. For example:
<div class="box" v-for="module in modules" :class="module">{{ module }}</div>
There's a few things to note here:
v-for looks directly at the modules array to determine the number of divs to render.
The value in the array is used both as the text for the div ({{ module }}) and as one of the classes (:class="module").
The class box is always applied, using the standard class attribute. The combination of this and the :class binding end up with e.g. abc box as the class list.
Here's a working example demonstrating the v-for approach.
new Vue({
el: '#app',
data: {
modules: ['abc', 'def']
}
});
.box {
height: 75px;
width: 75px;
background: #444;
padding: 10px;
margin: 5px;
color: #fff;
}
<script src="https://unpkg.com/vue#2.5.6/dist/vue.js"></script>
<div id="app">
<div class="box" v-for="module in modules" :class="module">{{ module }}</div>
</div>
this is my code:
<input type="checkbox" id="checklist-1-checkbox-1" v-on:click="addGreenClass(this)" name="checkbox1-1">
vue.js
new Vue({
el: '#admin-dashboard',
methods: {
addGreenClass(element) {
console.log(element);
},
}
});
In my console, I get the object referring to the whole document rather than referring to the input tag that the checkbox is sourcing the event from.
I'm sure I have the code wrong, but I can't seem to find my answer in similar questions that are asking for the code in pure Javascript in. Could someone help me here? Thank you.
Passing this from the context of an inline statement in the Vue instance's template is going to pass a reference to the Window object.
If you want to access an element from a method in your Vue instance, you can use a ref. No need to pass anything.
Here's the documentation on the ref attribute.
Here's an example:
new Vue({
el: '#app',
methods: {
addGreenClass() {
console.log(this.$refs.input)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input ref="input" #click="addGreenClass">
</div>
If you're trying to add a class to the element, you don't need to reference the element at all in your addGreenClass method. Simply bind an array of class names (say inputClasses) to your input using :class="inputClasses", and then push the name of the class you want to the array in your addGreenClass method. Vue will update the DOM automatically:
new Vue({
el: '#app',
data() {
return {
inputClasses: ['large'],
}
},
methods: {
addGreenClass() {
this.inputClasses.push('green');
}
}
})
.large {
height: 40px;
}
.green {
border: 3px solid green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input :class="inputClasses" #click="addGreenClass">
</div>
Since you are using checkboxes, I'm stealing a little code from #thanksd and showing how it might be done without even using a method. This highlights Vue's data driven approach.
new Vue({
el: '#app',
data() {
return {
classes: [],
}
}
})
.large {
height: 40px;
}
.green {
border: 3px solid green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<div :class="classes">Styled DIV</div>
<label>
<input type="checkbox" value="green" v-model="classes">
Green
</label>
<label>
<input type="checkbox" value="large" v-model="classes">
Large
</label>
</div>
I just made my first project with VueJS and Vue-loader.
So I made my first component to show a simple message, it works fine when I make one message, but I get an error when I make multiple messages:
(Emitted value instead of an instance of Error)
Error compiling template:
<message>This is a small message!</message>
<message>Another one</message>
- Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
This is my code. I'm very new to this and I can't figure out what's wrong.
App.vue
<template>
<message>This is a small message!</message>
<message>Another one</message>
</template>
<script>
import Message from './Components/Message.vue';
export default {
name: 'app',
components: {
Message,
},
data () {
return {
}
}
}
</script>
Message.Vue
<template>
<div class="box">
<p>
<slot></slot>
</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
.box { background-color: #e3e3e3; padding: 10px; border: 1px solid #c5c5c5; margin-bottom: 1em;}
</style>
I hope somebody can help!
The error is pretty self-explanatory. You should have only one root element in each component. So just pack everything in a div.
<template>
<div>
<message>This is a small message!</message>
<message>Another one</message>
</div>
</template>