VueJs render template issue - vue.js

I've an issue to fill data from model into code when I'v more then one template with separate data. What I need is first template renders as many times as many objects in firstFormDetails array and same the second one. There example of my code below:
<div id="app">
<first v-for="item in secondFormDetails" track-by="id"></first>
<second v-for="item in firstFormDetails" track-by="id"></second>
</div>
<template id="template-first">
<div> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div> {{ item.docNumber }}</div>
</template>
And VueJs module as follows:
Vue.component('first', {
template: '#template-first',
data: function() {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
Vue.component('second', {
template: '#template-second',
data: function() {
return {
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
new Vue({
el: '#app'
});

#vbranden is correct, move the v-for into the component
<template id="template-first">
<div v-for="item in firstFormDetails"> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div v-for="item in secondFormDetails"> {{ item.docNumber }}</div>
</template>

You must define what components you use. Let try to use this:
var first = Vue.component('first', {
template: '#template-first',
data: function() {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
var second = Vue.component('second', {
template: '#template-second',
data: function() {
return {
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
new Vue({
el: '#app',
components: {
'first': first,
'second': second
}
});

in addition on #johnnynotsolucky 's answer, you will need a wrapper element out of v-for, since only allow only one element inside it.
<template id="template-first">
<div class="form-details">
<div v-for="item in firstFormDetails"> {{ item.docNumber }}</div>
</div>
</template>

var first = Vue.component('first', {
template: '#template-first',
props: ['item']
});
var second = Vue.component('second', {
template: '#template-second',
props: ['item']
});
new Vue({
el: '#app',
components: {
'first': first,
'second': second
},
data: function () {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
],
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
<div id="app">
<first v-for="item in secondFormDetails" :item="item"></first>
<second v-for="item in firstFormDetails" :item="item"></second>
</div>
<template id="template-first">
<div> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div> {{ item.docNumber }}</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>

When Vue app is loaded, it looks for component field, if component field is not defined then not component is loaded, if component field is defined, Vue looks for the definition of the component and parse it for syntax checking, once the syntax is correct the component is binded. This happens recursively for nested components.
Registering a component is mandatory

Related

vue reactivity in list with slot scoped component

Good afternoon!
I have a loop and inside each element I display a separate component that takes props and gives them through the slot. I can't figure out why when new elements are added the update hooks happen on the old ones?
Example:
have: 1 2 3
add: 4
log: created 4, updated 3, updated 2, updated 1
Why is this happening?
Vue.component('slot-component', {
inheritAttrs: false,
created() {
console.log('created', this._uid);
},
updated() {
console.log('updated', this._uid);
},
render(h) {
return this.$scopedSlots.default(this.$attrs);
}
})
new Vue({
el: '#app',
data() {
return {
list: []
}
},
methods: {
add() {
this.list.push({
title: 'some'
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js"></script>
<div id="app">
<div class="list">
<div
v-for="(item, n) in list"
:key="n"
class="list-item"
>
<slot-component
:title="item.title"
v-slot="props"
>
{{ props.title }}
</slot-component>
</div>
<button #click="add">add</button>
</div>
</div>
Update
Thanks to Raphael Rollet for clarifying that all array elements are updated when the array is changed globally. But then it is not entirely clear why this behavior behaves differently with a component that uses props
Pass and use props — not call updated
Vue.component('slot-component', {
props: ['title'],
created() {
console.log('created', this._uid);
},
updated() {
console.log('updated', this._uid);
},
template: `<div>{{ title }}</div>`
})
new Vue({
el: '#app',
data() {
return {
list: []
}
},
methods: {
add() {
this.list.push({ title: 'some' });
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js"></script>
<div id="app">
<div class="list">
<slot-component
v-for="(item, n) in list"
:key="n"
:title="item.title"
class="list-item"
>
</slot-component>
<button #click="add">add</button>
</div>
</div>
Pass and use props with slot — call updated
Vue.component('slot-component', {
props: ['title'],
created() {
console.log('created', this._uid);
},
updated() {
console.log('updated', this._uid);
},
template: `<div><slot>{{ title }}</slot></div>`
})
new Vue({
el: '#app',
data() {
return {
list: []
}
},
methods: {
add() {
this.list.push({ title: 'some' });
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js"></script>
<div id="app">
<div class="list">
<slot-component
v-for="(item, n) in list"
:key="n"
:title="item.title"
class="list-item"
>
{{ item.title }}
</slot-component>
<button #click="add">add</button>
</div>
</div>
Use $attrs — call updated
Vue.component('slot-component', {
created() {
console.log('created', this._uid);
},
updated() {
console.log('updated', this._uid);
},
template: `<div>{{ $attrs.title }}</div>`
})
new Vue({
el: '#app',
data() {
return {
list: []
}
},
methods: {
add() {
this.list.push({ title: 'some' });
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js"></script>
<div id="app">
<div class="list">
<slot-component
v-for="(item, n) in list"
:key="n"
:title="item.title"
class="list-item"
>
</slot-component>
<button #click="add">add</button>
</div>
</div>
Use slot — call updated
Vue.component('slot-component', {
created() {
console.log('created', this._uid);
},
updated() {
console.log('updated', this._uid);
},
template: `<div><slot></slot></div>`
})
new Vue({
el: '#app',
data() {
return {
list: []
}
},
methods: {
add() {
this.list.push({ title: 'some' });
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js"></script>
<div id="app">
<div class="list">
<slot-component
v-for="(item, n) in list"
:key="n"
:title="item.title"
class="list-item"
>
<template #default></template>
</slot-component>
<button #click="add">add</button>
</div>
</div>
The behaviour who seems "weird" is all time because vue binding.
You'r array is update so all property inside is update, someone can have a nice explanation on that because it's too deep.
But the solution is not use a update function on your child (if you don't want update something on every child), but set a watcher on what you want and do something in that case.
Like if you update a title on a child you do that:
watch: {
title(value) {
console.log(value)
}
}
Update:
For the two last example in your update section, like i explain for me it's expected, it's seems weird yes, but it's binding with vue.
About the first, you use a props so you pass a string in your component and nothing else. He have no reason to update.
For the second you do the same thing but with a slot, the only explanation for me it's, when you initialise a slot (even empty) the slot access to the parent data, and because binding is update.
I found this:
https://vuejs.org/guide/components/slots.html#render-scope
Slot content has access to the data scope of the parent component,
because it is defined in the parent.
Be honest it's assumption, if anyone have a other explanation i want to know it !

How can I select element Id with a function in Vue.js?

I want to use the Id of the element in which I use the function as a parameter. Example:
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction(id)"
>
methods: {
aFunction(id){
alert(id);
}
}
But it doesn't work. How can I do it?
In another way, you can make your id attribute bind with the data property, like this :
<template>
<div class="col-2 column-center" v-bind:id="id" #mouseover="aFunction(id)">
test
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
id: 'myId',
};
},
methods: {
aFunction(id) {
alert(id);
},
},
};
</script>
You can pass the event to the method and then get the id from event.target.id.
https://jsfiddle.net/tr0f2jpw/
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
aFunction(event){
alert(event.target.id);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction($event)"
>
some stuff
</div>
</div>

VueJS process component or HTML using a function

I understand how this works from the VueJS Documentation:
<div id="components-demo">
<button-counter></button-counter>
</div>
And also this:
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
</div>
Is there any way to write the equivalent from a function and have Vue pick it up? For example as this:
<div id="app">
{{ writeComponent('component-name', etc) }}
or
{{ writeElement('loadElement') }} <!-- which then writes the component above -->
</div>
The reason for this is that in this context quite a few components might need to be written and writing it out in HTML would be cumbersome.
You could use the render function like below:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
},
render(h) {
let bcs = this.buttonCounters.map(bc => h('button-counter'));
let loadLink = h('a', {on: {"click": ($event) => { this.loadElement(this.request, this.etc)}}}, ["Load"]);
return h('div', {attrs: {"id": "app"}}, [loadLink, " ", ...bcs])
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>
Though the same can be achieved via regular template and v-for:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
<button-counter v-for="(bc, index) in buttonCounters" :key="index"></button-counter>
</div>

Vue.js reactivity inner mechanisms

Given the following Vue code, how does it know to render the v-for again when selected is changed?
It makes complete sense to me when todos is changed.
So, Vue notices that there's a method isSelected involved and then uses "reflection" to watch the selected value, since it's an instance value?
Is that what's happening under the hood?
<div id="app">
<ol>
<li v-for="todo in todos" :class="{ 'selected': isSelected(todo.text) }">
{{ todo.text }}
</li>
</ol>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
todos: [
{ text: 'foo' },
{ text: 'bar' },
{ text: 'quz' }
],
'selected': 'bar'
},
methods: {
isSelected: function(text) {
return text != this.selected;
}
}
})
app.todos.push({ text: 'test' });
app.todos[0].text = 'change';
app.selected = 'foo';
</script>

Reusable nested VueJS components

Is it possible to declare a component inside another component in Vue.JS?
this is what i'm trying to do:
<!-- this is declared inside some-component.vue file -->
<script>
export default {
components:{
'cmptest' : {
template:'#cmptest',
props:['mprop']
}
},
data : () => ({
val:'world'
})
};
</script>
<template>
<div>
<template id="cmptest">
{{mprop}}
</template>
<cmptest mprop="hello"></cmptest>
<cmptest :mprop="val"></cmptest>
</div>
</template>
I'd like to avoid globally registering the child component if possible (with Vue.component(...))
In other words, I'd like to specify child's <template> inside the parent component file (without doing a huge line template:'entire-html-of-child-component-here')
Sure.
Like this:
https://jsfiddle.net/wostex/63t082p2/7/
<div id="app">
<app-child myprop="You"></app-child>
<app-child myprop="Me"></app-child>
<app-child myprop="World"></app-child>
</div>
<script type="text/x-template" id="app-child2">
<span style="color: red">{{ text }}</span>
</script>
<script type="text/x-template" id="app-child">
<div>{{ childData }} {{ myprop }} <app-child2 text="Again"></app-child2></div>
</script>
new Vue({
el: '#app',
components: {
'app-child': {
template: '#app-child',
props: ['myprop'],
data: function() {
return {
childData: 'Hello'
}
},
components: {
'app-child2': {
template: '#app-child2',
props: ['text']
}
}
}
}
});