Vuejs - v-model with multiple checkboxes - vue.js

I have dynamic list of options which comes from api:
<tr v-for="(option, index) in options">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="toggle" v-model="option.value" #click="toggleOption(option.id, index)">
<label class="custom-control-label" for="toggle">{{ option.value }}</label>
</div>
Method:
toggleOption(id, index) {
let app = this;
let option = this.options[index];
app.loading = true;
option.value = !option.value;
axios.patch('/apiendoint' + id, option)
.then(function (resp) {
app.loading = false;
})
.catch(function (resp) {
});
}
When checkbox is clicked all checkboxes changes, if only one item comes from api everything is working. How to make to work it with multiple checkboxes?

I created basic working example
new Vue({
el: '#app',
data: {
loading: false,
options: [
{id: 1, value: true},
{id: 2, value: true},
{id: 3, value: true},
]
},
methods: {
/*
instead of passing `id` and `index`, just pass `option`
*/
toggleOption(option) {
let app = this;
app.loading = true;
option.value = !option.value;
// REMOVE NEXT LINE to send ajax
return;
axios.patch('/apiendoint/' + option.id, option)
.then(function (resp) {
app.loading = false;
})
.catch(function (resp) {});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="(option, index) in options">
<td>
<div class="custom-control custom-switch">
<input
:id="'toggle-'+option.id"
type="checkbox"
class="custom-control-input"
v-model="option.value"
#click="toggleOption(option)"
>
<label class="custom-control-label" :for="'toggle-'+option.id">
{{ option.value }}
</label>
</div>
</td>
</tr>
</table>
</div>

Related

Not able to automatically set a checkbutton as checked on a value getting updated

I have a Vue js template that contains two radio buttons that are initially unchecked. Once the widgets get rendered, I want one of the radio buttons to be checked automatically on a value being updated after retrieving it from the backend server.
Here is the code as seen below:
<template>
<div>
<CCard class="card">
<CCardHeader>{{ $t("SETTINGS.NOTIFICATIONS.HEADER") }}</CCardHeader>
<CCardBody>
<table>
<tr>
<td class="index">
<strong>
{{ $t("SETTINGS.NOTIFICATIONS.DISBURSEMENT_INDEX") }}
</strong>
<img
src="#/assets/img/blue-circle.svg"
class="info pointer-on-hover"
v-c-tooltip="{
html: true,
content: getDisbursementTooltipContent,
active: false,
placement: 'top',
}"
>
</td>
<td class="category">
<fieldset id="disbursement-group">
<input
id="whatsapp"
type="radio"
name="disbursement-group"
value="whatsapp"
#change="selectDisbursementPreference"
:checked="disbursementPreference === whatsapp"
>
<span class="text">Whatsapp</span>
</fieldset>
</td>
<td class="category">
<fieldset id="disbursement-group">
<input
id="email"
type="radio"
name="disbursement-group"
value="email"
#change="selectDisbursementPreference"
:checked="disbursementPreference === email"
>
<span class="text">Email</span>
</fieldset>
</td>
</tr>
<br>
<tr hidden>
<td class="index">
<strong>
{{ $t("SETTINGS.NOTIFICATIONS.SETTLEMENT_INDEX") }}
</strong>
<img
src="#/assets/img/blue-circle.svg"
class="info pointer-on-hover"
v-c-tooltip="{
html: true,
content: getSettlementTooltipContent,
active: false,
placement: 'bottom',
}"
>
</td>
<td class="category">
<fieldset id="settlement-group">
<input
type="radio"
name="settlement-group"
value="email"
#change="selectSettlementPreference"
:checked="settlementPreference === email"
>
<span class="text">{{ $t("SETTINGS.NOTIFICATIONS.YES") }}</span>
</fieldset>
</td>
<td>
<fieldset id="settlement-group">
<input
value="none"
type="radio"
name="settlement-group"
#change="selectSettlementPreference"
:checked="settlementPreference === none"
>
<span class="text">{{ $t("SETTINGS.NOTIFICATIONS.NO") }}</span>
</fieldset>
</td>
<td
v-if="isEmailInputVisible"
class="email-column"
>
<strong>Email</strong>
<input
type="text"
v-model="emailRecepients"
>
</td>
</tr>
</table>
</CCardBody>
</CCard>
<CButton
color="durianprimary"
class="button"
#click="savePreferences"
:disabled="isSaveButtonDisabled"
>
{{ $t("SETTINGS.NOTIFICATIONS.SAVE") }}
</CButton>
</div>
</template>
<script>
export default {
name: "Notifications",
data() {
return {
disbursementPreference: "",
whatsapp: constant.WHATSAPP,
email: constant.EMAIL,
none: constant.TEXT_NONE,
};
},
methods: {
selectDisbursementPreference(event) {
this.disbursementPreference = event.target.value;
},
setDisbursementPreference(data) {
if (!data.is_available) {
return;
}
const length = data.types.length;
for (let i = 0; i < length; i++) {
if (data.types[i].is_enabled) {
this.disbursementPreference = data.types[i].type;
return;
}
}
},
createDisbursementPreferencePayload() {
const disbursementPreferenceLength =
constant.DISBURSEMENT_PREFERENCE_TYPES.length;
const types = [];
for (let i = 0; i < disbursementPreferenceLength; i++) {
if (
constant.DISBURSEMENT_PREFERENCE_TYPES[i] ===
this.disbursementPreference
) {
types.push({
type: constant.DISBURSEMENT_PREFERENCE_TYPES[i],
is_enabled: true,
});
} else {
types.push({
type: constant.DISBURSEMENT_PREFERENCE_TYPES[i],
is_enabled: false,
});
}
}
return { is_available: true, types: types };
},
createRequestPayload() {
const disbursementPayload = this.createDisbursementPreferencePayload();
return {
disbursement: disbursementPayload,
};
},
async savePreferences() {
....
},
async getPreferences() {
....
},
},
computed: {
getDisbursementTooltipContent() {
return `${this.$t("SETTINGS.NOTIFICATIONS.DISBURSEMENT")}`;
},
getSettlementTooltipContent() {
return `${this.$t("SETTINGS.NOTIFICATIONS.SETTLEMENT")}`;
},
isEmailInputVisible() {
return this.settlementPreference === this.email;
},
getEmailRecipients() {
let recipientsString = "";
const length = this.settlementEmailReceipients.length;
for (let i = 0; i < length; i++) {
recipientsString += this.settlementEmailReceipients[i] + ";";
}
return recipientsString;
},
isSaveButtonDisabled() {
return (
this.emailRecepients.length === 0 &&
this.settlementPreference === constant.EMAIL
);
},
},
mounted() {
this.getPreferences();
},
};
</script>
I have tried to automatically set either of the check buttons with the following code pieces:
watch : {
disbursementPreference:function(val) {
console.log("watch value has been changed => ",val);
document.getElementById(val).checked = true;
},
},
and
updated() {
console.log("value that I am trying to test => ",this.disbursementPreference);
document.getElementById(this.disbursementPreference).checked = true;
},
But the thing is, while the above code captures the changes made to the disbursementPreference variable, the result still remains the same because the widgets have not been rendered yet.
Is there a way to solve the above problem? I tried window.onload but I think rendering in vue.js is somewhat different as far as I know.
Is there any other way to solve this problem? Please do let me know thanks.
P.S : I have not added all of the template code for proprietary reasons.

Vuejs + onClick doesn't work on dynamic element loaded after mounted

I'm stucked with this issue. When I click on some element it push an item to an array, and I show this array in a table. I want to add an action to delete any row of the table on this way for example:
Table
My code:
<div id="pos">
<div class="container-fluid" style="font-size: 0.8em;">
<div class="row grid-columns">
<div class="col-md-6 col">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>Descripcion</th>
<th>Stock</th>
<th>Precio uni</th>
<th>Precio alt</th>
<th>Cant</th>
<th>Subtotal</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<pos-products
:products="products"
v-on:remove-product="removeProduct"
>
</pos-products>
<!-- <tr v-for="item in products" :key="item.id">
<th scope="row">${item.id}</th>
<td>${ item.descripcion }</td>
<td>${ item.stock }</td>
<td>${ item.precio } $</td>
<td>${ item.precio_alt } $</td>
<td>
<v-minusplusfield :value="1" :min="1" :max="100" v-model="item.cant"></v-minusplusfield>
</td>
<td>${ getSubtotal(item) }</td>
<td> Borrar </td>
</tr> -->
</tbody>
</table>
</div>
<div class="col-md-6 col">
<div>
<div id="grid-header" class="p-2 border-b ">
<input class="form-control" name="searchString" placeholder="Buscar producto" type="text" v-model="searchString" />
</div>
</div>
<div style="background-color:#fff">
<div class="col-md-3" v-for="item in searchResults">
<a
href="#"
class="list-group-item"
:key="item.id"
#click="loadItem(item)"
>
<img src="//images03.nicepage.com/a1389d7bc73adea1e1c1fb7e/af4ca43bd20b5a5fab9f188a/pexels-photo-3373725.jpeg" alt="" class="u-expanded-width u-image u-image-default u-image-1" width="25" height="30">
<h6 class="u-text u-text-default u-text-1">${item.descripcion}</h6>
<h4 class="u-text u-text-default u-text-2">${item.precio}$ / ${item.precio_alt}$</h4>
</a>
</div>
</div>
</div>
</div>
</div>
Vue code:
const app = new Vue({
el: "#pos",
delimiters: ["${", "}"],
data () {
return {
products: [],
total: 0,
client: "",
user: "",
paymentDetail: [],
errors: {},
garantia: false,
saveButton: false,
seller: "",
searchString: "",
searchTypingTimeout: "",
searchResults: [],
}
},
methods: {
getSubtotal: function (item) {
return parseInt(item.cant) * parseFloat(item.precio);
},
loadItem: function (item) {
this.products.push({
id: item.id,
descripcion: item.descripcion,
stock: item.stock,
precio: item.precio,
precio_alt: item.precio_alt,
cant: 1,
});
},
removeItem: () => {
products = products.filter((el) => el !== item);
},
searchProducts: function (value) {
axios
.post("/v2/producto/search", {
query: value
})
.then((response) => {
if (!response.status == 200 || response.data.error) {
console.log('error')
const errorMessage = response.data.error
? response.data.error
: "Ha ocurrido un error";
console.log("mensaje: " + errorMessage);
this.$swal({
icon: "error",
title: "Oops...",
text: errorMessage,
});
return;
}
this.searchResults = response.data.data;
})
.catch((error) => {
console.log("catch error", error);
});
},
},
mounted() {
var csrf = document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content");
this.products = [];
},
computed: {},
watch: {
total(val) {
this.total = parseFloat(val);
},
searchString(val) {
if (this.searchTypingTimeout) clearTimeout(this.searchTypingTimeout);
this.searchTypingTimeout = setTimeout(
() => this.searchProducts(this.searchString),
850
);
},
},
});
I got this:
vue.js?3de6:634 [Vue warn]: Property or method "removeItem" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
Try using classic function like this :
removeItem(item){
const index = this.items.findIndex(x => x.id === item.id)
this.items.splice(index, 1)
},
I've here loaded the data with the jsonplaceholder.typicode.com api
new Vue({
el: '#app',
data: () => ({
items: []
}),
async mounted(){
await axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
this.items = res.data
})
},
methods: {
removeItem(item){
const index = this.items.findIndex(x => x.id === item.id)
this.items.splice(index, 1)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js" integrity="sha512-odNmoc1XJy5x1TMVMdC7EMs3IVdItLPlCeL5vSUPN2llYKMJ2eByTTAIiiuqLg+GdNr9hF6z81p27DArRFKT7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<h1>List </h1>
<ul>
<li v-for="item of items" :key="item.id">
<a #click="removeItem(item)">{{item.id}} - {{item.title}}</a>
</li>
</ul>
</div>

VUEX Filtered computed property does not update at state change

I am using vuex , and with getters Iam filtering a array of data in the store.
In a parent component I am fetching the array and send it to a child with props.
The child component resieve filtered array with getters and save it in computed property.
But when I make changes by calling actions, store is updated but filtered array stayed the same.
When I send to the child component original unfiltered array it's okey.
It the vue dev tool I see correct updated getters.
Some of the code is below.
STORE
const getDefaultState = () => {
return {
activities: [],
error: null,
isActivitiesLoading: false,
isActivityUpdating: false,
}
}
const mutations = {
[FETCHING_ACTIVITIES](state) {
state.isActivitiesLoading = true;
state.error = null;
},
[FETCHING_ACTIVITIES_SUCCESS](state, activities) {
state.error = null;
state.isActivitiesLoading = false;
state.activities = activities
},
[FETCHING_ACTIVITIES_ERROR](state, error) {
state.error = error;
state.isActivitiesLoading = false
},
[UPDATING_ACTIVITY](state) {
state.isActivityUpdating = true;
state.error = null;
},
[UPDATING_ACTIVITY_SUCCESS](state, activity) {
state.error = null;
state.isActivityUpdating = false;
const index = state.activities.findIndex(a => a.id === activity.id)
state.activities[index] = activity;
},
[UPDATING_ACTIVITY_ERROR](state, error) {
state.error = error;
state.isActivityUpdating = false
},
}
const actions = {
async fetchActivities({ commit }) {
commit(FETCHING_ACTIVITIES);
try {
const response = await ActivitiesApi.fetchActivities();
const activities = response.data.data;
commit(FETCHING_ACTIVITIES_SUCCESS, activities);
return response.data.data;
} catch (error) {
commit(FETCHING_ACTIVITIES_ERROR, error);
return null;
}
},
async updateActivity({ commit }, payload) {
commit(UPDATING_ACTIVITY);
try {
const response = await ActivitiesApi.updateActivity(payload);
const activity = response.data.data;
commit(UPDATING_ACTIVITY_SUCCESS, activity);
return response.data.data;
} catch (error) {
commit(UPDATING_ACTIVITY_ERROR, error);
return null;
}
},
};
const getters = {
getActivities(state) {
return state.activities;
},
getRunningActivities(state) {
let today = new Date();
const activities = state.activities;
const filteredActivities = activities.filter(function(activity) {
let activityDate = new Date(activity.start_date)
return activityDate <= today
});
return filteredActivities;
},
};
export default {
namespaced: true,
state: getDefaultState(),
getters,
actions,
mutations,
}
PARENT COMPONENT
<template>
<div class="container">
<h3>Running Activities</h3>
<ActivitiesComponent
:initialActivitiesFromStore="runningActivities"
/>
</div>
</template>
import ActivitiesComponent from "../components/Activities";
export default {
components: {
ActivitiesComponent
},
mounted() {
this.$store.dispatch('activities/fetchActivities').then(
() => {
if (this.hasError) {
console.log(this.error)
} else {
}
}
);
},
computed: {
activitiesFromStore() {
return this.$store.getters['activities/getActivities'];
},
runningActivities() {
return this.$store.getters['activities/getRunningActivities']
},
},
}
</script>
CHILD COMPONENT
<template>
<div class="container">
<div v-if="isActivitiesLoading" class="spinner-border spinner"></div>
<div class="row">
<div class="col">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Activities</th>
<th scope="col">Period</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="(activity, activityId) in $v.activities.$each.$iter" :key="activityId">
<th scope="row">{{ parseInt(activityId) + 1 }}</th>
<td>
<input type="text" class="form-control" v-model="activity.name.$model">
<div class="alert alert-danger" v-if="!activity.name.required">Print Name</div>
<div v-if="activitiesFromStore[activityId].is_paused" class="alert alert-warning">
Activity is paused
</div>
</td>
<td>
<input type="text" class="form-control" v-model="activity.activity_period.$model">
<div class="alert alert-danger" v-if="!activity.activity_period.required">Print period</div>
<div class="alert alert-danger" v-if="!activity.activity_period.integer || !activity.activity_period.minValue">Period > 0</div>
</td>
<td class="d-flex border-0">
<button #click="activity.$model.is_paused = ! activity.$model.is_paused" class="btn btn-light mr-1" v-bind:class="{ active: !activity.$model.is_paused }">
<span v-if="activity.$model.is_paused">Убрать с паузы</span>
<span v-else>Make pause</span>
</button>
<button #click="updateActivity(activity.$model)" :disabled="
isActivityUpdating || !activitiesChanged(activityId) || !activity.name.required || !activity.activity_period.required || !activity.activity_period.integer || !activity.activity_period.minValue
" type="button" class="btn btn-success mr-1">
<span v-if="isActivityUpdating && activityActed.id == activity.$model.id" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Change
</button>
<button #click="deleteActivity(activity.$model)" type="button" class="btn btn-danger">
<span v-if="isActivityDeleting && activityActed.id == activity.$model.id" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Delete
</button>
</td>
</tr>
</tbody>
</table>
<div class="collapse" id="collapseExample">
<div class="form-group row">
<div class="col-4">
<label for="newPassword-input">Name</label>
<input v-model="activityToAdd.name" class="form-control">
<div v-if="$v.activityToAdd.period.$dirty && !$v.activityToAdd.name.required" class="alert alert-danger">Print name</div>
</div>
<div class="col-4">
<label for="newPassword-input">Period</label>
<input v-model="activityToAdd.period" class="form-control">
<div class="alert alert-danger" v-if="$v.activityToAdd.period.$dirty && !$v.activityToAdd.period.required">Print period</div>
<div class="alert alert-danger" v-if="(!$v.activityToAdd.period.integer || !$v.activityToAdd.period.minValue)">period > 0</div>
</div>
</div>
<button #click="addActivity" :disabled="!$v.activityToAdd.name.required || !$v.activityToAdd.period.required || !$v.activityToAdd.period.integer || !$v.activityToAdd.period.minValue" type="button" class="btn btn-primary">
<span v-if="isActivityAdding" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
add
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import {required, minValue, integer} from "vuelidate/lib/validators"
export default {
props: ['initialActivitiesFromStore'],
data() {
return {
activityActed: null,
justEdited: false,
justAdded: false,
justDeleted: false,
activityToAdd:{
name: '',
period: '',
isPaused: ''
}
}
},
computed: {
activitiesFromStore() {
return this.initialActivitiesFromStore
},
activities() {
return JSON.parse(JSON.stringify(this.initialActivitiesFromStore));
},
},
methods: {
activitiesChanged(id) {
if(this.activitiesFromStore[id] && this.activities[id].name == this.activitiesFromStore[id].name && this.activities[id].activity_period == this.activitiesFromStore[id].activity_period && this.activities[id].is_paused == this.activitiesFromStore[id].is_paused)
return false;
else
return true
},
updateActivity(activity){
this.activityActed = activity;
this.$store.dispatch('activities/updateActivity', activity).then(
() => {
if (this.hasError) {
console.log(this.error)
} else {
this.justEdited = true;
// still the same
console.log(this.$store.getters['activities/getRunningActivities']);
}
}
);
},
},
validations: {
activities: {
$each: {
name: {
required,
},
activity_period: {
required,
integer,
minValue: minValue(0)
},
is_paused: {
required,
},
}
},
}
}
</script>
The problem was that I did not follow the vue specification about modification of an array. I used vm.items[indexOfItem] = newValue which is not reactive.

Multiple Dynamic Checkboxes with input groups in vue js

I am trying to make a form which contains an input group section, in this group, there are one select box and multiple checkboxes. Checkboxes are populated based on the select box selection. There is also an add and remove button to generate and remove input group. The select box is used with v-model to filtered the checkboxes. But when I generate a new input group and make changes, all checkboxes are changed.
I want them to be isolated. How can I achieve?
Here is my Template.
<template>
<form #submit.prevent="onSubmit">
<div v-for="(variationProduct, index) in variationProducts" :key="variationProduct.id">
<div class="from-group mb-4">
<label class="col-form-label"><b>Categories :</b></label>
<select class="form-control mr-2" ref="categories" v-model="category">
<option value="0">Please select category...</option>
<option v-for="category in categories" :key="category.id" :value="category.id">
{{ category.name }}
</option>
</select>
<div v-if="hasError">
<validation-errors v-if="errors['categories.'+index]" :errors="errors">
{{ errors['categories.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="form-group mb-4">
<label class="col-form-lablel"><b>Variation Products :</b></label>
<div class="row">
<div v-for="filteredVariationProduct in filteredVariationProducts" :key="filteredVariationProduct.id">
<div class="col-12">
<input :id="filteredVariationProduct.id" :value="filteredVariationProduct.id" type="checkbox" ref="variationProducts">
<label :for="filteredVariationProduct.id">{{ filteredVariationProduct.name }}</label>
</div>
</div>
</div>
<div v-if="hasError">
<validation-errors v-if="errors['variationProducts.'+index]" :errors="errors">
{{ errors['variationProducts.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="float-right">
<button #click.prevent="add" class="btn btn-success">Add</button>
<button #click.prevent="remove(index)" class="btn btn-danger">Remove</button>
</div>
<br>
<br>
<hr>
</div>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</template>
Here is my JS.
<script>
import ValidationErrors from './ValidationErrors.vue';
export default {
components: {
'validation-errors': ValidationErrors,
},
data () {
return {
variationProducts: [],
categories: [
{ id: 1, name: 'Technology'},
{ id: 2, name: 'Business'},
{ id: 3, name: 'Marketing'},
{ id: 4, name: 'Development'},
{ id: 5, name: 'Engineering'},
],
category: 0,
activeVariationProducts: [],
count: 1,
errors: {},
hasError: false,
}
},
methods: {
add: function() {
this.count++;
this.errors = {};
this.hasError = false;
this.variationProducts.push({ id: this.count });
},
remove: function (index) {
if ( this.variationProducts.length > 0 && index > -1) {
this.variationProducts.splice(index, 1);
} else {
alert('Must have at least one input!')
}
},
onSubmit: function() {
console.log(this.$refs.variationProducts.value);
},
generateVariationProducts: function(num) {
for(let i = 1; i <= num; i++) {
let activeVariationProduct = {
id: i,
name: 'Product '+ i,
category_id: i
};
this.activeVariationProducts.push(activeVariationProduct);
}
},
},
computed : {
filteredVariationProducts: function () {
let categoryId = parseInt(this.category);
if (categoryId !== 0) {
let filteredVariationProducts = this.activeVariationProducts.filter((variationProduct) => {
return variationProduct.category_id === categoryId;
});
return filteredVariationProducts;
} else {
let filteredVariationProducts = this.activeVariationProducts;
return filteredVariationProducts;
}
}
},
created () {
this.variationProducts.push({ id: this.count });
this.generateVariationProducts(10);
},
}
</script>
Here is a sample code. This code Shows how you can use multiple Checkboxes that is generated dynamically and how to make them isolated -
new Vue({
el : "#app",
data : {
Items : ["One", "Two", "Three"],
newCheckbox : "",
SelectedItems : {
'One' : "",
'Two' : "",
'Three' : "",
},
},
methods:{
add:function(){
Vue.set(this.SelectedItems, this.newCheckbox, false);
this.Items.push(this.newCheckbox);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js" type="text/javascript"></script>
<div id="app">
<div>
<label v-for="(item, index) in Items">
<input type="checkbox" v-bind:key="index" v-model="SelectedItems[item]"> {{ item }}
</label>
</div>
<div>
<input type="text" v-model="newCheckbox">
<button #click="add">Add</button>
</div>
<div>
Output : {{ SelectedItems }}
</div>
</div>
You can dynamically add new checkbox and still they are isolated

Click-to-Edit text field with Vue

I am working on Vue js and having an issue editing a field. When I click on a field to edit it, all the editable fields become active. Here is my code.
export default {
props: ['profileHeight'],
data() {
return {
User: User,
isEditing: false,
form:{
name:'',
email: '',
},
};
},
mounted() {
},
methods: {
activateInEditMode() {
this.isEditing = true
},
deActivateInEditMode() {
this.isEditing = false
}
}
}
<span>Profile settings</span>
<p>Full name<span v-on:click="activateInEditMode" v-show="!isEditing">{{User.state.auth.name}}</span>
<span v-show="isEditing" >
<input v-model="form.name" type="text" class="form-control" >
</span>
</p>
<p>E-mail<span>{{User.state.auth.email}}</span>
<span v-show="isEditing" >
<input v-model="form.email" type="text" class="form-control" >
</span>
</p>
Try using focus and blur methods to show/hide form elements!
Hope this helps!
new Vue({
el: '#app',
data(){
return {
user : {
name: '',
email: ''
},
editField : ''
}
},
methods : {
focusField(name){
this.editField = name;
},
blurField(){
this.editField = '';
},
showField(name){
return (this.user[name] == '' || this.editField == name)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<h1>Profile settings</h1>
<label for="user-name">Full name</label>
<div class="field">
<span class="field-value" v-show="!showField('name')" #click="focusField('name')">{{user.name}}</span>
<input v-model="user.name" v-show="showField('name')" id="user-name" type="text" class="field-value form-control" #focus="focusField('name')" #blur="blurField">
</div>
<label for="user-email">Email address</label>
<div class="field">
<span class="field-value" v-show="!showField('email')" #click="focusField('email')">{{user.email}}</span>
<input v-model="user.email" v-show="showField('email')" type="email" class="field-value form-control" #focus="focusField('email')" #blur="blurField">
</div>
</div>
There are are dozens of ways to do this. I might recommend a component.
console.clear()
Vue.component("editable",{
props:["label", "value"],
template:`
<p>
{{label}}
<span #click="editing=true" v-show="!editing">
{{value}}
</span>
<span v-show="editing" >
<input :value="value"
#input="$emit('input', $event.target.value)"
#keydown.enter="editing=false"
type="text"
class="form-control" >
</span>
</p>
`,
data(){
return {
editing: false,
}
}
})
const User = {
name: 'bob',
email: 'bob#example.com'
}
new Vue({
el:"#app",
data() {
return {
form: User
};
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<span>Profile settings</span>
<editable label="Full name" v-model="form.name"></editable>
<editable label="E-mail" v-model="form.email"></editable>
<br>
{{form}}
</div>
I have written a component for this, I call it Click-to-Edit.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue:
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>