v-for: Array element and destructuring of properties - vue.js

When working with an array of objects, is it possible in a v-for-loop to assign the current object to a variable and destruct its properties at the same time? Something like this:
<div v-for="(person = {name, age}, index) in persons">
Ultimately I'm looking for a way to use e.g. both the whole person object and its properties in a template.

As far as I know, you can't do both.
But, you can destructure, i.e.
<div v-for="({name, age}, index) in persons">
You could then just access the correct element with the index: persons[index].
Example:
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript",
done: false
},
{
text: "Learn Vue",
done: false
},
{
text: "Play around in JSFiddle",
done: true
},
{
text: "Build something awesome",
done: true
}
]
},
methods: {
toggle: function(index) {
this.todos[index].done = !this.todos[index].done
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="({text, done}, i) in todos">
<label>
<input type="checkbox"
v-on:change="toggle(i)"
v-bind:checked="done">
<del v-if="done">
{{ text }}
</del>
<span v-else>
{{ text }}
</span>
{{todos[i]}}
</label>
</li>
</ol>
</div>

Related

Is it valid to provide to v-for unclosed tags for iteration in vue.js?

Assume, I have such vue-code:
<template>
<div v-for="(item, index) in items">
<div v-if="item.isComponent">
<component :is="item.value"/>
</div>
<template v-else>
{{item.value}}
</template>
</div>
</template>
<script>
export default {
data: function(){
return {
items: [
{
value: '<p>this is first part of paragraph
},
{
value: 'componentName',
isComponent: true,
},
{
value: 'this is the last part of paragraph</p>
},
],
//...
</script>
items - it's a parsed (which I haven't parsed yet) string for contenteditable tag editor.
If this is invalid, what workaround could be?
UPD.
items is a json which I will get from database which should be saved to database as user input to contenteditable div editor.
Html will get sanitize the Html part. So it is not a good idea. to do such things.
Html should be kept in template of vue.
But let say you wanted to show some data in tag. Instead of doing use computed prop and hide and show p tag. It also prevents the incomplete tag issue.
I am attaching jsfiddle solution[
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
items: [
{
value: '<p>this is first part of paragraph'
},
{
value: 'componentName',
isComponent: true,
},
{
value: 'this is the last part of paragraph</p>'
},
],
},
computed:{
dataVal(){
let val = "";
for(let i=0;i<this.items.length;i++){
val += this.items[i].value + " "
}
return val;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" v-html="dataVal">
</div>
]1

How to get radio button value from multiple radio button with same v-model name?

How can I get the value from my multiple radio button by same v-model name?
I expect I can check only one button for each student and put its value into the info[] array.
new Vue({
el: "#app",
data: {
info: [],
student_id: [
{ name: 'Andrew', student_id: '52346' },
{ name: 'Mathew', student_id: '86975' },
{ name: 'Max', student_id: '78654' },
{ name: 'Jhon', student_id: '36589' },
{ name: 'Flam', student_id: '74859' },
{ name: 'Meli', student_id: '62398' }
]
},
methods: {
submit() {
console.log(this.info)
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<div id="app">
<v-container>
<form #submit.prevent="submit">
<div v-for="(id,i) in student_id" class="pa-3">
<span class="blue--text">{{id.name}}</span>
<br />
<input type="radio" :name="`${id.name}`" v-model="info" :value="`${id.student_id}-P`" /> Present
<input type="radio" v-model="info" :name="`${id.name}`" :value="`${id.student_id}-A`" /> Absent
<input type="radio" v-model="info" :name="`${id.name}`" :value="`${id.student_id}-L`" /> Late
<hr />
</div>
<v-btn color="success" type="submit">Submit</v-btn>
</form>
</v-container>
</div>
View on JSFiddle
new Vue({
el: "#app",
data: {
student_id: [{
name: 'Andrew',
student_id: '52346',
value: ''
},
{
name: 'Mathew',
student_id: '86975',
value: ''
},
{
name: 'Max',
student_id: '78654',
value: ''
},
{
name: 'Jhon',
student_id: '36589',
value: ''
},
{
name: 'Flam',
student_id: '74859',
value: ''
},
{
name: 'Meli',
student_id: '62398',
value: ''
}
]
},
computed: {
info() {
return this.student_id.map(item => item.value)
}
},
methods: {
submit() {
console.log(this.info)
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<v-container>
{{info}}
<form #submit.prevent="submit">
<div v-for="(id,i) in student_id" class="pa-3">
<span class="blue--text">{{id.name}}</span>
<br />
<input type="radio" :name="`${id.name}`" v-model="id.value" :value="`${id.student_id}-P`" /> Present
<input type="radio" v-model="id.value" :name="`${id.name}`" :value="`${id.student_id}-A`" /> Absent
<input type="radio" v-model="id.value" :name="`${id.name}`" :value="`${id.student_id}-L`" /> Late
<hr />
</div>
<v-btn color="success" type="submit">Submit</v-btn>
</form>
</v-container>
</div>
Put the value of each radio group in the object in the array, and then calculate the selected value by computed.

Toggle form inside v-for using vue.js

How can i toggle form inside v-for loop,I have a form inside v-for which i want to display (toggle) on click.
But when i click all the form inside the v-for gets toggled.
Secondly is it better approach to keep the form inside the loop,when have large amount of data inside loop or load it as a separate component.
This is what i am trying to do.
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
this.show = !this.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
There is only 1 reactive variable show. Setting it to true while all form is using v-if="show", will show everything.
You can set show to something that each form uniquely have. For example, its text, and perform a v-if using its text.
demo: https://jsfiddle.net/jacobgoh101/umaszo9c/
change v-if="show" to v-if="show === todo.text"
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show === todo.text">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
change toggle method
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
if (this.show === todo.text)
this.show = false
else
this.show = todo.text
}
}
})
property "show" should be a prop of todo,not prop of data
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
].map(o=>({...o,show:false}))
},
methods: {
toggle: function(todo) {
todo.show = !todo.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="todo.show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>

Accessing Vue Component scope from external javascript

Can I access Vue components data properties and methods from external javascript? I am trying to create a hybrid application where a portion of the screen is a Vue component, and I want to call a method inside that component on click of a button which is handled by pure js. Is there a way to achieve this?
Thanks!
Yes. You need to assign your Vue object to a variable (i.e. vue) and then you can access vue.methodName() and vue.propertyName:
// add you new Vue object to a variable
const vue = new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
});
// Add event listener to outside button
const button = document.getElementById('outsideButton');
button.addEventListener('click', function() {
vue.toggle(vue.todos[1]);
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
<div class="outside">
<button id="outsideButton">
Click outside button
</button>
</div>
Yes, you can add an event listener to the Vue component that listens for the button click's event. See example here on codepen.
JS
new Vue({
el: '#app',
methods: {
clickMethod(event){
if (event.target.id === 'outsideButton') {
alert('button clicked')
}
}
},
created(){
let localThis = this
document.addEventListener('click', this.clickMethod)
}
})
HTML
<div>
<button id="outsideButton">Button</button>
<div id="app">
</div>
</div>

Assing uploaded file value to input field multiple times using vue.js

Assign filename to input field on upload and then show success message working fine,but when i try to delete file and upload again it's not working!
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" #change="uploadImage($event)">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>
I am not getting any console error as well.
This is what i have tried so far.
Can you guys please have a look at this!
The problem is that #change is not trigger when you choose the same file. The simplest solution is reset value of input when you click delete
this.$refs.fileToUpload.value = '';
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
this.$refs.fileToUpload.value = '';
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" class="custom-file-input" #change="uploadImage($event)" ref="fileToUpload">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>
You need to clear you input field first.
Update your upload method with this.
uploadImage(event) {
this.form.fileurl = 'uploaded!'
this.success = false
this.$nextTick(() => {
this.success = true
})
Have a look at this working demo
https://jsfiddle.net/asimshahzad/8c9pcfys/