passing an input value directly into a function within a loop - vuejs2

So basically I have this;
<ul class="queryView">
<li
v-for="(object, index) in Objects"
>
{{ object.key }}
<input
type="text"
#input="saveValue(value)"
>
</li>
</ul>
...
saveValue(value){
... do somthing with value
},
Since it's in a loop, v-model does not work as they will affect each looped element. i.e If in one input field I put in a word all of the fields will display the same word.
thus I need to get the input value directly into the function.

v-model does work if properly used.
See this JS Fiddle:
https://jsfiddle.net/eywraw8t/167740/
But if you want to use a function to handle the values, it is also fine, but much more verbose.
See this JS Fiddle:
https://jsfiddle.net/eywraw8t/167731/

See the docs: https://v2.vuejs.org/v2/api/#v-on
[…] the method receives the native event as the only argument. If using inline statement, the statement has access to the special $event property: v-on:click="handle('ok', $event)".
The "input" event has a target property, which in your case is your <input> element, on which you can read its current value.
new Vue({
el: '#app',
methods: {
saveValue(event) {
const target = event.target;
const value = target.value;
console.log(value);
},
otherMethod(text1, event) {
console.log(text1);
console.log(event.target.value);
},
},
});
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
<ul>
<li v-for="i in 3">
{{i}}<input #input="saveValue" />
</li>
</ul>
<p>
With inline statement:
<input #input="otherMethod('other', $event)" />
</p>
</div>

Related

All dynamically generated components are changing to the same value in VUEJS

we are building a chat application in Vuejs, now every chat message is component in our application, now whenever we are changing the value of one chat message, the value of all chat messages changes
What is happening
source code
App Component
const App = new Vue({
el: '#myApp',
data: {
children: [
MyCmp
],
m1: '',
m2: '',
m3: 'Hello world',
m4: 'How are you'
},
methods: {
sendMessage (event) {
if(event.key == "Enter") {
this.m2= this.m3;
this.children.push(MyCmp);
}
},
}
});
component code
let MyCmp = {
props: ['myMessage'],
template: `
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p>{{ myMessage }}</p>
</div>
</li>
`
};
** view where components are generating **
<ol class="chat">
<template v-for="(child, index) in children">
<component :is="child" :key="child.name" v-bind="{myMessage: m3}"></component>
</template>
</ol>
Even though you are creating new components by pushing them into the children array, they are still getting bound to the same data via the line v-bind="{myMessage: m3}". Whenever you change m3, it will be passed down to all the child components and they will all render the same message.
This is an odd way of creating custom components since you could easily do so using the templating syntax or render function provided by Vue.
Solution 1 - Recommended
Change your approach - push message strings instead of card component definitions into children and use MyCmp inside v-for to render the message cards.
So the new template could be refactored as :-
<ol class="chat">
<template v-for="(message, index) in children">
<my-cmp :key="index" :my-message="message"></my-cmp>
</template>
</ol>
And inside App component, you can replace this.children.push(MyCmp) with this.children.push(messageVariable); where messageVariable contains the message that you receive from the input box.
Why is the recommended? This is a standard approach where component lists are rendered based on an array of data. It will be easier to scale and maintain.
Solution 2
You can use the v-once directive to bind the message one-time as static text. The binding won't be updated even if m3 changes on the parent.
Then MyCmp template will become :-
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p v-once>{{ myMessage }}</p>
</div>
</li>
You bind myMessage of all your components instances with one variable m3. So, when m3 is changed myMessage in all instances changes respectively. Use another variable (e.g. msg) for rendering the message and then use myMessage property only for the initialisation of msg, like this:
let MyCmp = {
props: ['myMessage'],
data: function () {
return {
msg: this.myMessage
}
},
template: `
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p>{{ msg }}</p>
</div>
</li>
`
};

Don't see div with v-if, regardless of the value in the data

I have problem with displaying component with v-if.
In one component i have <div v-if="seen">...</div>.
In another component I have <button v-on:click="seen = !seen">...</button>.
In "var vue = nev Vue({...})" file, in data: I have seen: true and this is not working.
I found "solution" which works: example
and I tried this "function version" of data in my code, but it doesn't work too :/
Here is my code:
Main File
var vue = new Vue({
el: '#app',
components: {
Navigation,
Home,
Footer,
Login
},
data: function () {
return {
seen: true
}
},
template: "<div><navigation></navigation><login></login><home></home><Footer></Footer></div>"
})
template that I can't see
<div v-if="seen" id="loginbox">
<form>
<input type="text" placeholder="login" class="input is-rounded"/>
<input type="password" placeholder="password" class="input is-rounded"/>
</form>
</div>
button template
<div class="navbar-menu">
<div class="navbar-start"></div>
<div class="navbar-end">
<p class="nav-item">
<a class="nav-link" v-on:click="seen = !seen">Login</a>
</p>
<p class="nav-item">
<a class="nav-link">Register</a>
</p>
</div>
</div>
I expect that when I click on button, "loginbox" template will be shown.
EDIT:
I did it in half way. I used props (used export default...) in in template that I cannot seen. It not work properly, becouse now I can change value of "seen" only with button which is in this template. I'd like change value of it by button which is in another template.
You should somehow share data between components.
You can do it many ways, but for this case i suggest to use event handling https://v2.vuejs.org/v2/guide/events.html#ad
Edited your sandbox with example - https://codesandbox.io/s/mzq0r2w88j

x-template has trouble displaying value on the v-for

I had this issue while trying to render html into a vue component.
I am trying to insert component html through x-template. The issue is when I was trying to display the value {{i.value}} like this it was throwing error on console.
<script type="text/x-template" id="first-template">
<div>
<ul>
<li v-for="i in dataCollection">{{ i.id }}</li>
</ul>
</div>
</script>
Vue.component('menu', {
template: '#first-template',
data() {
return {
dataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}],
}
}
});
The error on console was:
But when I was giving value as attribute like:
<script type="text/x-template" id="first-template">
<div>
<ul>
<li v-for="i in dataCollection" :id="i.id"></li>
</ul>
</div>
</script>
it works perfect.
Anyone know any fix ?
You should not put script/x-template tages inside of the element that you mount to the main instance to. Vue 2.0 will read all of its content and try to use it as a template for the main instance, and Vue's virtualDOM treats script/x-template's like normal DOM, which screws everthing up,
Simply moving the template out of the main element solved the problem.
Source
This is a suggestion, not a answer.
As #DmitriyPanov mentioned, you'd better bind unique key when using v-for.
Another issue is you'd better to use non built-in/resevered html elements.
so change component id from menu to v-menu or else you like.
Then simulate similar codes below which are working fine.
I doubt the error is caused by some elements of dataCollection doesn't have key=id (probably you didn't post out all elements). You can try {{ 'id' in i ? i.id : 'None' }}.
Vue.component('v-menu', { //
template: '#first-template',
data() {
return {
newDataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}, {'xx':0}],
dataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}]
}
}
});
new Vue({
el: '#app',
data() {
return {testProperty: {
'test': '1'
}}
},
methods:{
test: function() {
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<v-menu></v-menu>
</div>
<script type="text/x-template" id="first-template">
<div>
<div style="float:left;margin-right:100px;">
<p>Old:</p>
<ul>
<li v-for="(i, index) in dataCollection" :key="index">{{ i.id }}</li>
</ul>
</div>
<div>
<p>Adjusted:</p>
<ul>
<li v-for="(i, index) in newDataCollection" :key="index">{{ 'id' in i ? i.id : 'None' }}</li>
</ul>
</div>
</div>
</script>
I think the problem here lies in the placement of the X-Template code (I had the same issue). According to the documentation:
Your x-template needs to be defined outside the DOM element to which Vue is attached.
If you are using some kind of CMS, you might end up doing just that.
What helped me in that case was (based on your example):
Placing the X-template script outside the #app
passing the collection as a prop to the v-menu component:
<v-menu v-bind:data-collection="dataCollection"></v-menu>
list dataCollection as a prop inside the v-menu component:
Vue.component('v-menu', { //
template: '#first-template',
props: [ "dataCollection" ],
...
});
I hope that helps anyone.
In 2.2.0+, when using v-for with a component, a key is now required.
You can read about it here https://v2.vuejs.org/v2/guide/list.html#v-for-with-a-Component

Display value emitted from Vue component

I created two separated Vue components and I able to emit a message thru a bus.
How can I render/display the message in the component that receives the message.
Example of the Vue component that receives the message:
<template>
<div v-model="cars">
Car model: {{ model }}
<input type="button" #click="display" value="example" />
</div>
</template>
<script>
export default {
data() {
return {
cars: null
}
},
mounted() {
bus.$on('CARS_LOADED', (cars) => {
this.cars = cars;
});
},
methods: {
display()
{
console.log(this.cars);
}
}
}
</script>
I can successfully emit and received the message, however the car model is not updated. I checked the message received and it contains the "model" key with a right value.
I cannot see any error in the Vue console and however if I replace "{{ model }}" by "{{ cars }}" I can see the full message object updated.
I am using Vue 2.x.
Update:
I enclose an example:
https://jsfiddle.net/kvzvxk4f/1/
As you can see in the example I cannot render an specific field from the object, however I can render the object as string.
I think that you are misunderstanding some parts of the vue syntax.
How to access properties of an object:
You just need to write {{ car.model }} to access a property of an object.
How to iterate through an array in a template:
If you want to display all the cars in your template, you should write:
<div v-for="car in cars">
{{ car }}
</div>
As you see, the v-for directive allows you to iterate through an array.
What is v-model?
v-model is used to bind a variable to an input or a component.
<template>
<div>
<input type="text" v-model="foo" />
</div>
</template>
<script>
export default {
data () {
return {
foo: 'bar'
}
}
}
</script>
In that case, the foo property will be bound to the input text.
Last point:
In your case, to make it work, you also need to create a root element for your template, because a template can't have multiple root elements:
<template>
<div>
<div v-for="car in cars">
{{ car }}
</div>
</div>
</div>
I found the answer.
I just have to type property separated by ".". Like for example {{cars.model}}.
<template id="compo2">
<div>
<div>
{{ field.name }}
</div>
<div>
Received: {{ field }}
</div>
</div>
</template>
Example:
https://jsfiddle.net/zuhb7s8q/3/

How to bind extra props to component rendered by v-for?

This is a newbie question but I cannot find answer/hint online:
For a basic template that is rendered by a v-for loop:
<div id="app">
<ul class="list">
<v-ml_component v-for="data_item in get_data.Value"
v-bind:item="data_item"
v-bind:index="data_index"
v-bind:key="data_item.id"
v-bind:messageType="messageType">
</v-ml_component>
</ul>
</div>
<template id="list-template">
<li>
<span v-if="messageType.indexOf('inbox') >= 0">{{ item.MessageId }}</span>
<span>{{index}}</span> : {{ item.Subject }}
</li>
</template>
Everything is straight forward as in basic example, except I need to pass a prop to this component:
$.getJSON(mockAjaxUrl).done(function(ajaxdata){
var messageType = 'inbox';
Vue.component('v-ml_component', {
template:'#list-template',
props:['index', 'item', 'messageType']
});
var vm = new Vue({
el:"#app",
data:{
messageType: messageType,
get_data: ajaxdata
}
});
});
This would get error in console that messageType is not passed to this component. I wonder how the value of this property can be passed to a template that is rendered with a v-for loop?
The messageType prop will be message-type in HTML.
See camelCase vs. kebab-case in the Vue docs.