Dynamic generated form with checkbox in Vue - javascript

I have dynamic generated form in Vue. Every loop have v-model. Everything work fine. But when I use checkboxes v-model work for all loops not on one like in input type text. Can You help me solved this problem? Below Vue code:
<fieldset>
<div class="form-row mb-2" v-for="input, index in journal" :key="index">
<div class="col-auto">
<label for="date">Data</label>
<Datepicker v-model="input.date" input-class="form-control" :input-attr="{id: 'date', name: 'date'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="timeStart">Od</label>
<Datepicker type="time" v-model="input.timeStart" format="HH:mm" input-class="form-control" :input-attr="{id: 'timeStart', name: 'timeStart'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="timeEnd">Do</label>
<Datepicker type="time" v-model="input.timeEnd" format="HH:mm" input-class="form-control" :input-attr="{id: 'timeEnd', name: 'timeEnd'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="players">Lista obecności</label>
<div class="form-check" v-for="item in input.players">
<input v-model="item.checked" type="checkbox" class="form-check-input" :id="'id-'+item.id+'set'+index">
<label class="form-check-label" :for="'id-'+item.id+'set'+index">{{ item.fullName }}</label>
</div>
</div>
<div class="col-auto">
<label for="description">Opis</label>
<textarea v-model="input.description" class="form-control" rows="7" id="description" placeholder="Opis"></textarea>
</div>
<div class="col-auto" #click="addInput" v-show="index == journal.length-1 && journal.length < 16">
<ButtonVue style="margin-top: 30px;" title="Dodaj" type="button" cancelWidth="true" color="btn-success"><i class="fas fa-plus"></i></ButtonVue>
</div>
<div class="col-auto align-self-start" #click="removeInput(index)" v-show="index || ( !index && journal.length > 1)">
<ButtonVue style="margin-top: 30px;" title="Usuń" type="button" cancelWidth="true" color="btn-danger"><i class="fas fa-minus"></i></ButtonVue>
</div>
</div>
</fieldset>
 
data() {
return {
contact: [],
journal: [{
date: "",
timeStart: "",
timeEnd: "",
players: "",
description: ""
}],
contacts: [],
}
},
Methods:
Method for creating dynamic form
addInput() {
this.journal.push({
date: "",
timeStart: "",
timeEnd: "",
players: this.contact,
description: ""
});
},
And here is the method which gets players from contacts
getContacts() {
this.pageLoader = true;
this.$http.get('/pkpar/get-contacts')
.then(({
data
}) => {
this.contacts = data.contacts;
for(let i=0; i<this.contacts.length; i++)
{
this.contact.push({'id': this.contacts[i]['id'], 'fullName' :
this.contacts[i]['fullName'], 'checked': true});
}
this.journal[0].players = this.contact;
this.pageLoader = false;
})
.catch(error => {
console.log(error);
});
},

Your addInput method creates and pushes new object into journal array, but each object created this way has a players property which references same array (this.contact)
The Difference Between Values and References in JavaScript
Easiest (but not most optimal) way to handle this is to create a copy of the array and objects inside for each new journal:
addInput() {
this.journal.push({
date: "",
timeStart: "",
timeEnd: "",
players: this.contact.map((player) => ({ ...player })),
description: ""
});
},

Related

Vuejs get index in multiple input forms

I have an array of strings like:
questions: [
"Question 1?",
"Question 2?",
"Question 3?",
"Question 4?",
],
Then I have form fields in my data() like:
legitForm: {
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
},
Now, the problem I'm facing is when I fill inputs for any of questions
above the same field for other questions gets same value.
Here is my template code:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForm.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForm.duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForm.description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
And this is my post method that sends data to backend:
legitStore(question) {
this.legitForm.name = question; // set "name" in `legitForm` to value of `question` string
axios.post('/api/auth/userLegitsStore', this.legitForm, {
headers: {
Authorization: localStorage.getItem('access_token')
}
})
.then(res => {
// reset my data after success
this.legitForm = {
name: '',
answer: '',
description: '',
duration: '',
};
})
.catch(error => {
var errors = error.response.data;
let errorsHtml = '<ol>';
$.each(errors.errors,function (k,v) {
errorsHtml += '<li>'+ v + '</li>';
});
errorsHtml += '</ol>';
console.log(errorsHtml);
})
},
Here is issue screenshot:
Note: I've tried to
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
but I've got errors so i wasn't sure what I'm doing wrong, that's why
I'm asking here.
If you think of your questions as questions and answers, you can do something like this:
questions: [
{
question: 'Question 1',
answer: null,
description: null,
duration: null,
},
{
question: 'Question 2',
answer: null,
description: null,
duration: null,
},
]
Then when looping through your form, it would be more like this:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
And in the storing function you could send the data in the question instead of this.legitForm
Like you said you tried here:
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
You are supposed to be doing that.
I would change legitForm to:
//create legitForms equal to the length of questions
const legitForms = [];
for (i in questions) legitForms.push(
{
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
}
);
and in template:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForms[index].answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForms[index].duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForms[index].description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
<div v-for="(question, index) in questions" :key="index">
In your template you iterate through questions and within this tag render object legitForm it all questions will refer to the same 1 object that's why all question have the same data.
You should have had create an array of question contains it own question's content like
<template>
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<div class="col-md-12">
<p>
<strong>{{question.id}}</strong>
</p>
</div>
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
</template>
<script>
class QuestionForm {
// pass default value if you want
name = ''
answer = ''
description = ''
duration = ''
id = ''
constructor(form) {
Object.assign(this, form)
}
}
function initQuestions(num) {
// can generate a Set object cause question are unique
return Array.from({ length: num }, (v, i) => i).map(_j => new QuestionForm())
}
export default {
data() {
return {
questions: initQuestions(5), // pass number of question you want to generate
}
}
}
</script>

v-model not working with javascript functions

I am new to Vue JS and have been learning it from the documentation provided. My project is a simple task adding web-app. On using the v-model directive, I'm not getting any output. My javascript function to add the task is apparently not being called.
<template>
<div id="text">
TASKS:
<form onsubmit="return addTodo()">
<input type="text" class="todo-input" placeholder="What's up" v-model="message">
<input type="date" class="todo-input" v-model="ddate" >
<input type="submit" value="Add">
</form>
<div v-for="(todo, index) in todos" :key="todo.id" class="todo-item">
<div>
{{todo.id}}{{todo.title}}{{todo.date}}
</div>
</div>
</div>
</div>
</template>
export default {
name: 'todo-list',
data () {
return {
message: '',
ddate: '',
idForTodo: 1,
todos: [
]
}
},
methods: {
addTodo(){
if(this.message.trim().length == 0){
return
}
this.todos.push({
id: this.idForTodo,
title: this.message,
completed: false,
editing: false,
date: this.ddate,
})
this.ddate = ''
this.message = ''
this.idForTodo++
},
}
}
Looks like someone edited the question with correct code while I was writing the answer. I tried and tested same code in code snippet and it's working.
const app = new Vue({
el: '#text',
data() {
return {
message: '',
ddate: '',
idForTodo: 1,
todos: [
]
}
},
methods: {
addTodo(){
console.log(this.message)
if(this.message.trim().length == 0){
return
}
this.todos.push({
id: this.idForTodo,
title: this.message,
completed: false,
editing: false,
date: this.ddate,
})
this.ddate = ''
this.message = ''
this.idForTodo++
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.0/vue.js"></script>
<div id="text">
TASKS:
<form>
<input type="text" class="todo-input" placeholder="What's up" v-model="message">
<input type="date" class="todo-input" v-model="ddate" >
<button v-on:click.prevent="addTodo">Add</button>
</form>
<div v-for="(todo, index) in todos" :key="todo.id" class="todo-item">
<div>
{{todo.id}} {{todo.title}} {{todo.date}}
</div>
</div>
</div>

Make calculation from v-for in vue js

i'm trying to make simple calculation from data given in vuejs. The calculation is just going well. But, when i started to make sum from calculation total from it, it keeps returning NaN value.
Here is the current code of it:
<div class="row" v-for="fields in data" :key="field.id_m_all_ded">
<div class="col-md-5">
<div class="form-group">
<label>{{ field.name }}</label>
<input type="field.type" class="form-control" #change="calculated(field)" v-model="field.value" />
</div>
</div>
<div class="col-md-7">
<label>Total Deductions</label>
<input type="number" class="form-control" v-model="field.total" disabled />
</div>
</div>
<div class="row">
<div class="col-md-5">
<label>Total Allowance</label>
<input type="number" class="form-control" v-model="grandTotal" id="total" disabled />
</div>
</div>
I retrieve the data from my API, and it saved in fields[]:
data() {
return {
model: {
nik: "",
name: "",
basic_salary: "",
period: "",
},
modals: {
modal: false,
},
fields: [{
total: ''
}],
totalData: 11,
page: 1,
mode: "",
};
},
And here's the function:
computed: {
grandTotal: function() {
let temp = 0
for (var i = 0; i < this.fields.length; i++) {
temp = temp + Number(this.fields[i].total)
console.log(temp)
}
console.log(temp)
return temp;
}
},
methods: {
calculated(field){
field.total = 4000000 * (field.value / 100)
console.log(field.total)
}
}
For addtional info, i get the data from the api, but it won't calculate automatic. Therefore, i tried it with manual input first to calculate the value.
What code should i fix from it, because i have no clue.
Thanks

How to remove character "T" in a DateTimeField in Django Vue.js?

I have a list with comments, those comments have a moment field, which is a DateTimeField. In this DateTimeField I have for example this --> 2020-06-03T15:32:01.803027 and I want to delete that T character, or replace with a blank space.
I'm using Django for the backend and Vue.js for the frontend.
I was able to remove the Z character by changing the USE_TZ = True to False option in the settings.py file. But I can't remove the T character, that's all I need. I have tried from the frontend to remove the T character using methods and computed properties, but I can't use the replace("T", " ") method on a DateTimeField, I can only use it on a String
<template lang="html">
<div class="container">
<div class="row">
<div class="col text-left">
<h2>Detalles del partido</h2>
</div>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<form>
<form #submit="onSubmit">
<div class="form-group row">
<label for="nombreLocal" class="col-sm-2 col-form-label">Local</label>
<div class="col-sm-6">
<input type="text" name="nombreLocal" class="form-control" readonly v-model.trim="form.nombreLocal">
</div>
</div>
<div class="form-group row">
<label for="nombreVisitante" class="col-sm-2 col-form-label">Visitante</label>
<div class="col-sm-6">
<input type="text" name="nombreVisitante" class="form-control" readonly v-model.trim="form.nombreVisitante">
</div>
</div>
<div class="form-group row">
<label for="resultado" class="col-sm-2 col-form-label">Resultado</label>
<div class="col-sm-6">
<input type="text" name="resultado" class="form-control" readonly v-model.trim="form.resultado">
</div>
</div>
<div class="form-group row">
<label for="pronosticoSistema" class="col-sm-2 col-form-label">Pronóstico del sistema</label>
<div class="col-sm-6">
<input type="text" name="pronosticoSistema" class="form-control" readonly v-model.trim="form.pronosticoSistema">
</div>
</div>
<div class="form-group row">
<label for="premio" class="col-sm-2 col-form-label">Premio</label>
<div class="col-sm-6">
<input type="number" name="premio" class="form-control" readonly v-model.trim="form.premio">
</div>
</div>
<div class="form-group row">
<label for="dificultad" class="col-sm-2 col-form-label">Dificultad</label>
<div class="col-sm-6">
<input type="text" name="dificultad" class="form-control" readonly v-model.trim="form.dificultad">
</div>
</div>
<div class="rows">
<div class="col text-left">
<b-button size="sm" variant="primary" :to="{ name:'CreatePronostico', params: {partidoId: this.partidoId} }">
Pronosticar
</b-button>
<b-button size="sm" variant="primary" :to="{ name:'CreateComentario', params: {partidoId: this.partidoId}}">
Comentar
</b-button>
<b-button size="sm" type="submit" class="btn-large-space" :to="{ name: 'PronosticadorUsuario'}">Atrás</b-button>
</div>
</div>
</form>
<br>
<h2>Comentarios</h2>
<br>
<div class="comentarios">
<b-table striped hover
:items="comentarios"
:fields="fields"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc">
<template v-slot:cell(action)="data">
<b-button size="sm" variant="primary" :to="{ name:'CreateComentarioRespuesta', params: {comentarioId: data.item.id} }">
Responder
</b-button>
<b-button size="sm" variant="primary" :to="{ name:'DarMeGusta', params: {comentarioId: data.item.id} }">
Me gusta
</b-button>
<b-button size="sm" variant="primary" :to="{ name:'EditComentario', params: {comentarioId: data.item.id} }">
Editar
</b-button>
<b-button size="sm" variant="danger" :to="{ name:'DeleteComentario', params: {comentarioId: data.item.id} }">
Eliminar
</b-button>
</template>
</b-table>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import swal from 'sweetalert'
import router from "../../router";
export default {
mounted() {
this.checkLoggedIn();
},
data() {
return {
sortBy: 'momento',
sortDesc: false,
partidoId: this.$route.params.partidoId,
form: {
nombreLocal: '',
nombreVisitante: '',
resultado: '',
pronosticoSistema: '',
premio: '',
dificultad: ''
},
fields: [
{ key: 'id', label: 'Número de comentario' },
{ key: 'momento', label: 'Momento', sortable: true},
{ key: 'texto', label: 'Texto' },
{ key: 'meGustas', label: 'Número de "me gustas"', sortable: true},
{ key: 'autor', label: 'Autor' },
{ key: 'comentarioRespuesta', label: 'Responde a' },
{ key: 'action', label: '' }
],
comentarios: []
}
},
methods: {
checkLoggedIn() {
this.$session.start();
if (!this.$session.has("token")) {
router.push("/auth");
}
},
onSubmit(evt){
evt.preventDefault()
const path = `http://localhost:8000/api/v1.0/partidos/${this.partidoId}/`
axios.get(path, this.form).then((response) =>{
this.form.nombreLocal = response.data.nombreLocal
this.form.nombreVisitante = response.data.nombreVisitante
this.form.resultado = response.data.resultado
this.form.pronosticoSistema = response.data.pronosticoSistema
this.form.premio = response.data.premio
this.form.dificultad = response.data.dificultad
})
.catch((error) => {
console.log(error)
})
},
getPartido (){
const path = `http://localhost:8000/api/v1.0/partidos/${this.partidoId}/`
axios.get(path).then((response) =>{
this.form.nombreLocal = response.data.nombreLocal
this.form.nombreVisitante = response.data.nombreVisitante
this.form.resultado = response.data.resultado
this.form.pronosticoSistema = response.data.pronosticoSistema
this.form.premio = response.data.premio
this.form.dificultad = response.data.dificultad
})
.catch((error) => {
console.log(error)
})
},
getComentarios (){
const path = 'http://localhost:8000/api/v1.0/comentarios/'
axios.get(path).then((response) => {
this.comentarios = response.data
})
.catch((error) => {
console.log(error)
})
},
},
computed: {
comentariosFormateados: function (){
var res = [];
for(var i = 0; i < this.comentarios.length; i++){
this.comentarios[i].momento.toISOString().replace('T', '');
res.push(comentarios[i]);
}
return res;
}
},
created() {
this.getPartido(),
this.getComentarios()
}
}
</script>>
<style lang="css" scoped>
</style>
Just return the Django DateTime object formatted however you want, for example:
myDateTime = strftime(MyModel.objects.get(id=id).moment, "%Y-%m-%d %H:%M:%S.%f")
Or:
comment = MyModel.objects.get(id=id)
moment = strftime(comment.moment, "%Y-%m-%d %H:%M:%S.%f")
comment.formatted_moment = moment
...
Then return it to your view in that format (a formatted string).
I've solved with this:
formatMomento(value) { const formattedDate = DateTime.fromISO(value).toLocaleString(DateTime.DATETIME_SHORT); return formattedDate; },
And calling it from the formatter attribute:
fields: [
{ key: 'id', label: 'Número de comentario' },
{ key: 'momento', formatter: "formatMomento", label: 'Momento', sortable: true},
{ key: 'texto', label: 'Texto' },
{ key: 'meGustas', label: 'Número de "me gustas"', sortable: true},
{ key: 'autor', formatter: "nombreDeUsuario", label: 'Autor' },
{ key: 'comentarioRespuesta', label: 'Responde al comentario:' },
{ key: 'action', label: '' }
],

How do I push items into an array in the data object in Vuejs? Vue seems not to be watching the .push() method

I am attempting to add objects into an array I declared in Vue instance data object. I can set the values in the state's purchase object, but when I push data into the orders queue array, the empty array is not populated. The function is being triggered, but the array does not update.
Here is my form:
<form
v-on:submit.prevent="queuePurchase"
class="form-inline row"
id="order-creation-form"
method="POST"
>
#csrf
<autocomplete-field
sizing="col-xs-12 col-sm-3 col-md-3"
name="customer"
label="Customer"
:data="{{ json_encode($customers) }}"
v-on:setcustomer="setCustomer($event)"
></autocomplete-field>
<div class="col-xs-12 col-sm-3 col-md3 form-group d-flex flex-column align-items-start">
<label for="phone">Product</label>
<select
v-model="purchase.product"
class="form-control w-100"
name="product"
aria-describedby="productHelpBlock"
required
>
#foreach ($products as $product)
<option :value="{{ json_encode($product) }}">
{{ $product->name }}
</option>
#endforeach
</select>
<small id="productHelpBlock" class="form-text text-muted">
Select a product
</small>
</div>
<div class="col-xs-12 col-sm-3 col-md-3 form-group d-flex flex-column align-items-start">
<label for="phone">Quantity</label>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
class="form-control w-100"
aria-describedby="productHelpBlock"
required
>
<small id="productHelpBlock" class="form-text text-muted">
Product quantity
</small>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success icon-button d-flex">
<i class="material-icons">add</i>
<span> Q U E U E</span>
</button>
</div>
</form>
And here is my javascript:
require("./bootstrap");
window.Vue = require("vue");
Vue.component("queue-table", require('./components/QueueTable.vue'));
Vue.component("autocomplete-field", require('./components/AutocompleteField.vue'));
const purchaseApp = new Vue({
el: "#purchase-app",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
}
},
methods: {
setCustomer: function(customerObj) {
this.purchase.customer = customerObj;
},
queuePurchase: function() {
this.queue.push( this.purchase );
}
}
});
Could someone please explain what & why it is happening?
The push() method ought to add purchase objects to the queue array, but as #FK82 pointed out in his comment, push() is adding multiple references to the same purchase object. This means that if you change the object by increasing the quantity, every purchase's quantity property will be updated.
You can give that a try here:
const exampleComponent = Vue.component("example-component", {
name: "exampleComponent",
template: "#example-component",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
};
},
methods: {
queuePurchase() {
this.queue.push( this.purchase );
}
}
});
const page = new Vue({
name: "page",
el: ".page",
components: {
"example-component": exampleComponent
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<template id="example-component">
<div>
<p>The Queue has {{ this.queue.length }} items.</p>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
placeholder="Quantity"
>
<button #click="queuePurchase">
Add to Queue
</button>
<pre>{{ JSON.stringify(this.queue, null, 2) }}</pre>
</div>
</template>
<div class="page">
<example-component></example-component>
</div>
Instead of push()ing a reference to the same purchase object, try creating a shallow copy with Object.assign({}, this.purchase) or by using the spread operator. Here's an example that uses the spread operator and then push()es the copy onto the queue:
const exampleComponent = Vue.component("example-component", {
name: "exampleComponent",
template: "#example-component",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
};
},
methods: {
queuePurchase() {
this.queue.push({...this.purchase});
}
}
});
const page = new Vue({
name: "page",
el: ".page",
components: {
"example-component": exampleComponent
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<template id="example-component">
<div>
<p>The Queue has {{ this.queue.length }} items.</p>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
placeholder="Quantity"
>
<button #click="queuePurchase">
Add to Queue
</button>
<pre>{{ JSON.stringify(this.queue, null, 2) }}</pre>
</div>
</template>
<div class="page">
<example-component></example-component>
</div>

Categories