Put the loop variable into an attribute of a template - javascript

I've been trying to get this loop to work, but I keep getting the error
card is undefined
How can I put the img property of the card object into the template?
function Board() {
this.cards = [new Card()];
}
function Card() {
this.img = 'img.png';
}
Vue.component('card', {
props: ['card'],
template: '<img v-bind:src="card.img">'
});
let app = new Vue({
el: '#app',
data: {
board: new Board()
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<card v-for="card in board.cards" v-bind:key="card.img">
</div>

I don't do Vue.js, but what jumped out at me was that you've said your card component takes a card property, but you're giving it a key property instead. The other thing that jumped out was trying to get card.img in two places (the template, and the markup).
If you change v-bind:key="card.img" to v-bind:card="card" (or :card="card"), in the markup, it works:
function Board()
{
this.cards = [new Card()];
}
function Card()
{
this.img = 'img.png';
}
Vue.component('card',
{
props:['card'],
template: '<img v-bind:src="card.img">'
});
let app = new Vue(
{
el:'#app',
data:
{
board: new Board()
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<card v-for="card in board.cards" v-bind:card="card">
</div>
Or if you wanted key as well as card so Vue.js puts the cards in order by card.img, you'd have both:
function Board()
{
this.cards = [new Card()];
}
function Card()
{
this.img = 'img.png';
}
Vue.component('card',
{
props:['card'],
template: '<img v-bind:src="card.img">'
});
let app = new Vue(
{
el:'#app',
data:
{
board: new Board()
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<card v-for="card in board.cards" v-bind:key="card.img" v-bind:card="card">
</div>

Related

How to clone vue instance to another dom node

How can i clone a vue application to another dom node?
I have made a simple vue app to work out my problem, i tried cloning using js clone function and re-initializing the application, it will mount but it wont workout the events. Here's my code:
const initMyApp = (el) => {
return new Vue({
el,
data: {
message: 'HEY'
},
methods: {
sayHello: function () {
this.message = 'HELLO!!!' + new Date().getMilliseconds()
}
},
mounted: function () {
console.log('mounted')
}
})
}
initMyApp('#app')
function moveApp() {
var appNode = document.getElementById("app")
var cloned = appNode.cloneNode(true);
document.getElementById("appContainer").innerHTML = ''
document.getElementById("appContainer").appendChild(cloned);
initMyApp('#appContainer')
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<h2>Main</h2>
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
<button onclick="moveApp()">DO</button>
<div>
<h2>Container</h2>
<div id="appContainer">
</div>
</div>
Any suggestion is really appreciated
If you want to mount your Vue app on different DOM elements, then you should be using mount() instead of the el:
const initMyApp = (el) => {
return new Vue({
data: {
message: 'HEY'
},
methods: {
sayHello: function() {
this.message = 'HELLO!!!' + new Date().getMilliseconds()
}
},
mounted: function() {
console.log('mounted')
}
})
}
function moveAppFirst() {
const vue1 = initMyApp()
vue1.$mount('#app')
}
function moveAppSecond() {
const vue2 = initMyApp()
vue2.$mount('#appContainer')
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<button onclick="moveAppFirst()">DO FIRST</button>
<div id="app">
<h2>Main</h2>
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
<button onclick="moveAppSecond()">DO SECOND</button>
<div>
<h2>Container</h2>
<div id="appContainer">
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
</div>
More information on mount(): https://v2.vuejs.org/v2/api/#vm-mount

Vuejs: data list not accessible from template in another template

So I have this set-up, which crashes on v-for construct of table-component. It shows an error: "Property or method "tablesList" is not defined on the instance but referenced during render". If I omit v-for table-component renders. If I access this data from "container" component all is fine. So the problem is in accessing data from child template in parent template.
What am I doing wrong?
let container = Vue.component("container", {
props: ["item"],
template: `<div class="container">
<table-component v-for="item in tablesList"></table-component>
</div>`
});
let table = Vue.component("table-component", {
props: ["item"],
template: `<div class="table">
this is a table
</div>`
});
let app = new Vue({
el: "#app",
data() {
return {
containersList: [],
tablesList: [{item:'item'}]
};
},
methods: {
anyMethod() {
}
}
});
</script>
You are using tablesList in container component But you defined it in app.
You need to add tablesList in container like below,
let container = Vue.component("container", {
props: ["item"],
data: () => {
return {
tablesList: [{item:'item'}]
}
},
template: `<div class="container">
<table-component v-for="item in tablesList"></table-component>
</div>`
});
NOTE: Use v-bind:key when use v-for.
You need to define tablesList in props => https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props

Vue.js does not update HTML

I have the following .html file in my local Vue.js project (it's a simplified version):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<main>
<div id="myElement">
<div v-for="(properties, name) in list" v-bind:key="name">{{ name }}</div>
</div>
</main>
<script type="module">
import SomeClass from "./SomeClass.js";
const myClass = new SomeClass();
let app = new Vue({
el: "#myElement",
data: {
list: myClass.object
},
});
</script>
</body>
</html>
Inside the <script type="module"> tag I import SomeClass which includes a property called object:
export default class SomeClass {
constructor() {
this.object = {
name1: { ... },
name2: { ... },
...
};
}
}
The issue here is that Vue.js reacts to changes in object (if I check it using watch) but it does not update my HTML:
<div v-for"..." v-bind:key="...">...</div>.
What can I do to make Vue.js update my HTML when new properties added to object / old properties updated?
Vue has a method to add properties to items in the data attribute (so they keep reactivity). (More on this: https://v2.vuejs.org/v2/api/#Vue-set)
The snippet below may help you to see the method in action:
class SomeClass {
constructor() {
this.object = {
name1: {
name: 'name3'
},
name2: {
name: 'name4'
}
}
}
}
const myClass = new SomeClass()
new Vue({
el: "#app",
computed: {
// transforming array for reactive display
transformedObj() {
const ret = []
this.keyVal().forEach(e => {
e.forEach(el => {
ret.push(el)
})
})
return ret
}
},
data: {
object: myClass.object
},
methods: {
// transforming object for better display
keyVal() {
return Object.keys(this.object).map(e => {
return Object.entries(this.object[e]).map(([k, v]) => {
return `${k}: ${v}`
})
})
}
},
mounted() {
// add new property (reactive)
Vue.set(this.object.name1, 'newProperty', '2')
// or add new property (reactive)
this.$set(this.object.name2, 'localNewProperty', 45)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="(item, key) in transformedObj">{{item}}</li>
</ul>
Object: {{transformedObj}}
</div>

How to use slots in the HTML with Single File Components

I want to use slots in Vue to create a dynamic modal component.
I already tried a lot of tutorials of Vue / slots, but none of them is exactly what i'm looking for.
This is a piece of my modal.vue:
<template>
...
<slot name="modal-body"></slot>
...
</template>
<script>
</script>
<style>
</style>
This is my javascript compiled file:
import Vue from 'vue';
import modal from './modal.vue';
new Vue({
el: '#modal',
render: r => r(modal)
});
This is piece of my HTML file:
...
<div id="modal">
<template v-slot="modal-body">
<input type="text" id="dynamic-input">
</template>
</div>
...
I was expecting that all elements present inside #modal (#dynamic-input in this case), were inserted into the slot named modal-body, inside my Vue element.
Is it possible to do it? Am i missing something?
Check what version of Vue you are using. The named slot syntax changed in 2.6.0. Consider the differences below. One uses render functions and the other template Strings.
Vue#2.6.10
// Example using template String
const modalTemplateString = {
template: "<div><div>above</div><slot name=\"modal-body\"></slot><div>below</div></div>"
};
const appTemplateString = new Vue({
el: "#appTemplateString",
components: {
modal: modalTemplateString
},
template: "<div><modal><template v-slot:modal-body><div>foobar</div></template></modal></div>"
});
// Example using render function
const modalRenderFunc = {
render(h) {
return h("div", [
h("div", "above"),
h("div", this.$slots["modal-body"]),
h("div", "below")
]);
}
}
const appRenderFunc = new Vue({
el: "#appRenderFunc",
components: {
modal: modalRenderFunc
},
render(h) {
return h("div", [
h("modal", [
h("div", {
slot: "modal-body"
}, "foobar")
])
]);
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<h2>Template String</h2>
<div id="appTemplateString"></div>
<hr/>
<h2>Render Function</h2>
<div id="appRenderFunc"></div>
Vue#2.5.22
// Example using template String
const modalTemplateString = {
template: "<div><div>above</div><slot name=\"modal-body\"></slot><div>below</div></div>"
};
const appTemplateString = new Vue({
el: "#appTemplateString",
components: {
modal: modalTemplateString
},
template: "<div><modal><template slot=\"modal-body\"><div>foobar</div></template></modal></div>"
});
// Example using render function
const modalRenderFunc = {
render(h) {
return h("div", [
h("div", "above"),
h("div", this.$slots["modal-body"]),
h("div", "below")
]);
}
}
const appRenderFunc = new Vue({
el: "#appRenderFunc",
components: {
modal: modalRenderFunc
},
render(h) {
return h("div", [
h("modal", [
h("div", {
slot: "modal-body"
}, "foobar")
])
]);
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.22/dist/vue.js"></script>
<h2>Template String</h2>
<div id="appTemplateString"></div>
<hr/>
<h2>Render Function</h2>
<div id="appRenderFunc"></div>

Vue: Access component object properties

I'm trying to use a statement within a element:
v-if="currentstep < maxStep"
The maxStep should be obtained from the number of components listed on my defauld export
export default {
name: 'step',
data () {
return {
maxStep: 8,
currentstep: 0
}
},
components: {
ConfigPublicador,
ConfigServico,
ModeloReceita,
Integracoes,
ConfigTema,
ConfigApp,
ConfigExtras,
Assets,
Revisao
}
}
Something like
maxStep = components.length
Any Ideias?
Thanks
This is definitely a code smell. But, you can get that value via Object.keys(this.$options.components).length.
Here's an example:
const Foo = {
template: '<div></div>',
}
new Vue({
el: '#app',
components: { Foo },
data() {
return {
count: 0,
}
},
created() {
this.count = Object.keys(this.$options.components).length;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<div id="app">
<div v-if="count > 0">
There is at least one component
</div>
</div>

Categories

Resources