Reputation: 971
I have a table that was generated dynamically using Vuejs. It has input elements in cells that render as readOnly. Each element has an 'edit' button and when it is click, it changes to 'save' and enables the input elements of that row for editing. When save is clicked, I would like to capture the new value entered and the previous value. I formatted my date field as mm/dd/yy
from the original which is in this format 2019-10-10T07:00:00Z
I am able to successfully format the date using momentjs but it doesn't seem to stick. The value entered is different from when I alert it. Any ideas of what I'm doing wrong? Do I need to refactor my code to allow me to do this because this has to be done for each field, which is to get access to new value and previous value.
<div id="app">
<table border=1 width=100%>
<tr>
<td>EDIT</td>
<td v-for="editableKey in editableKeys" :key="editableKey" class="label">{{ editableKey }}</td>
</tr>
<tr v-for="(program, index) in programs">
<td><button v-if="program.isReadOnly" v-on:click="editItem(program)">edit</button> <button @click="saveItem(program)" v-else>save</button></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="formatDate(program)"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="program.company"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="program.funding"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="program.funded"></td>
<td><select :class="bgColor(program)" type="text" v-bind:data-id="program.id" :disabled="program.isReadOnly" v-model="program.Recruit">
<option>Internal</option>
<option>Recruiting</option>
</select>
<!--<input :class="bgColor(program)" type="text" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="program.Recruit">--></td>
<td><input type="text" v-on:change="" v-bind:data-id="program.id" :readonly="program.isReadOnly" v-model="program.program"></td>
</tr>
</table>
</div>
new Vue({
el:"#app",
data: () => ({
programs: [],
editableKeys: ['date', 'company', 'funding', 'funded', 'recruit', 'program'],
}),
created () {
this.getPrograms();
},
methods: {
getPrograms() {
axios.get("https://my-json-server.typicode.com/isogunro/jsondb/Programs").then(response => {
this.programs = response.data.map(program => ({
...program,
isReadOnly: true
}));
}).catch(error => {
console.log(error);
});
},
editItem (program) {
program.isReadOnly = false
},
saveItem (program) {
program.isReadOnly = true
console.log(program)
alert("New Value: "+program.Date)
alert("Previous Value: "+program.Date)
},
bgColor (program) {
return program.funded === program.funding ? 'yellow' : 'white'
},
formatDate(program){
var formatL = moment.localeData().longDateFormat('L');
var format2digitYear = formatL.replace(/YYYY/g,'YY');
return moment(program.Date).format(format2digitYear);
},
updateField(program){
console.log(program)
alert(program)
}
}
})
Here's a pen for some clarity. Thanks for any help that can be provided.
Upvotes: 0
Views: 1046
Reputation: 35684
If you want it to behave in a way that the data in the input is different than what is being shown you'll have to decouple the input from the data.
v-model
needs to havea a variable assigned so that the input can update its value. Right now you're using v-model="formatDate(program)"
which cannot work, because passing a static result of the function cannot be reactive.
There are several ways of accomplishing this. Here is an example that just gets it done. There's certainly room for improvement and alternative implementations...
v-model
with value
and event listenersThis defines where the value comes from; a function generating a formated date. (note that using a function in template for rendering is not good practice. it's better to do this once and use a cached value, so that the value doesn't have to be recalculated)
<input
type="text"
v-bind:data-id="program.id"
:readonly="program.isReadOnly"
:value="formatDate(program)"
@input="updateDate($event, program)"
>
editItem (program) {
program.isReadOnly = false
program.tempDate = null; // added tempDate to be set to null initially
},
saveItem (program) {
program.isReadOnly = true
if(program.tempDate !== null) {
// if tempDate is set, update Date
program.Date = program.tempDate;
// and clear tempDate
program.tempDate = null;
}
console.log({...program})
},
// new method to convert value of the input from string to Date (if date passed is valid)
updateDate(e, program ) {
if(new Date(e.target.value).toString() !== 'Invalid Date'){
program.tempDate = new Date(e.target.value)
} else {
program.tempDate = null
}
},
As mentioned, using functions to format values in template is not a good idea. Instead, reformat the data before you use it (see map function)
once it is in the right format, you can use v-model
just note that this example does not check for correctness of date format.
function toDDMMYY(date) {
const [y, m, d] = (new Date(date)).toISOString().slice(0, 10).split('-')
return `${d}/${m}/${y%100}`
}
new Vue({
el: "#app",
data() {
return {
test: "hello",
programs: "",
hide: true,
editable: [],
}
},
created: function() {
this.getPrograms();
},
methods: {
getPrograms: function() {
axios.get("https://my-json-server.typicode.com/isogunro/jsondb/Programs").then((response) => {
this.programs = response.data.map(row => ({
...row,
dateFormatted: toDDMMYY(row.Date),
editable: false
}));
})
.catch(function(error) {
console.log(error);
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<div id="app">
<table border=1 width=100%>
<tr>
<td width=10px>EDIT</td>
<td>Date</td>
<td>Program</td>
<td>Company</td>
<td>Funding</td>
<td>Funded</td>
<td>Recruit</td>
</tr>
<tr v-for="program in programs">
<td>
<button v-if="program.editable == false" @click="program.editable = true">edit</button>
<button v-else @click="program.editable = false">save</button>
</td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.dateFormatted"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.program"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.company"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.funding"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.funded"></td>
<td><input type="text" v-bind:data-id="program.id" :readonly="!program.editable" v-model="program.Recruit"></td>
</tr>
</table>
</div>
Upvotes: 1