How to close a dropdown list when clicking anywhere - vue.js

How do I close the dropdown that opens when I click anywhere?
This code:
<tr class="inputs-table">
<td>Type object: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(1)">
<span class="select__current">Please select one option</span>
</div>
<addForm v-if="addedForm === 1" />
</div>
</td>
</tr>
<tr class="inputs-table">
<td>Type business-model: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(2)">
<span class="select__current">Please select one option</span>
</div>
<addForm v-if="addedForm === 2" />
</div>
</td>
</tr>
export default {
name: 'Index',
data() {
return {
addedForm: 0,
}
},
methods: {
AddForm(number) {
this.addedForm = number;
},
closeForm() {
this.addedForm = false;
}
},
components: {
addForm,
}
}
Drop-list:
What can I try next?

You can make use if window.onClick and see whether or not it matches you'r element. If it doesn't then close it; call the function that checks clicks at mounted and beforeDestroy or breforeRouteLeave.
E.g:
mounted(){
this.hideNav()
},
methods: {
hideNav() {
window.onclick = function(event) {
if (!event.target.matches('.select__body')) {
this.closeForm()
}
}
}
},
beforeRouteLeave() {
this.hideNav()
}

<template>
<div
#click="dropdownIsActive = !dropdownIsActive"
ref="dropdown"
>
</div>
</template>
<script>
export default {
data: () => ({
dropdownIsActive: false
}),
created () {
window.addEventListener('click', this.closeDropdown)
},
methods: {
closeDropdown (e) {
if (!this.$refs.dropdown.contains(e.target)) {
this.dropdownIsActive = false
}
}
},
beforeDestroy () {
window.removeEventListener('click', this.closeDropdown)
}
}
</script>

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>

Unable to register child component in vue.js

Getting following error.
Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
I am not sure what is wrong with the code. I have followed this link https://michaelnthiessen.com/solve-unknown-custom-element-vue/
I have used Local registration for child component. ( RobotBuilder.vue)
<template>
<div class="content">
<button class="add-to-cart" #click="addToCart()">Add to Cart</button>
<div class="top-row">
<PartSelector />
</div>
<div class="middle-row">
<PartSelector />
<PartSelector />
<PartSelector />
</div>
<div class="bottom-row">
<PartSelector />
</div>
<div>
<table>
<thead>
<tr>
<th>Robot</th>
<th class="cost">Cost</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(robot,index) in cart" :key="index">
<td>{{robot.head.title}}</td>
<td>{{robot.cost}}</td>
<td>
<button #click="removeItem([index])">X</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import availableParts from '../data/parts';
import { PartSelector } from './PartSelector.vue';
export default {
name: 'RobotBuilder',
components: { PartSelector },
data() {
return {
availableParts,
cart: [],
selectedRobot: {
head: {},
leftArm: {},
rightArm: {},
torso: {},
base: {},
},
};
},
computed: {},
methods: {
addToCart() {
const robot = this.selectedRobot;
const cost = robot.head.cost
+ robot.leftArm.cost
+ robot.torso.cost
+ robot.rightArm.cost
+ robot.base.cost;
this.cart.push({ ...robot, cost });
},
removeItem(index) {
this.cart.splice(index, 1);
},
},
};
</script>
<style scoped>
</style>
PartSelector.vue
<template>
<div class="part">
<img :src="selectedPart.src" title="arm"/>
<button #click="selectPreviousPart()" class="prev-selector"></button>
<button #click="selectNextPart()" class="next-selector"></button>
<span class="sale" v-show="selectedPart.onSale">Sale!</span>
</div>
</template>
<script>
import availableParts from '../data/parts';
const parts = availableParts.heads;
function getPreviousValidIndex(index, length) {
const deprecatedIndex = index - 1;
return deprecatedIndex < 0 ? length - 1 : deprecatedIndex;
}
function getNextValidIndex(index, length) {
const incrementedIndex = index + 1;
return incrementedIndex > length - 1 ? 0 : incrementedIndex;
}
export default {
name: 'PartSelector',
data() {
return { selectedPartIndex: 0 };
},
computed: {
selectedPart() {
return parts[this.selectedPartIndex];
},
},
methods: {
selectNextPart() {
this.selectedPartIndex = getNextValidIndex(
this.selectedPartIndex,
parts.length,
);
},
selectPreviousPart() {
this.selectedPartIndex = getPreviousValidIndex(
this.selectedPartIndex,
parts.length,
);
},
},
};
</script>
You are exporting as default but importing as named import.
In Robot builder, import like this :
import PartSelector from './PartSelector.vue';

[Vue warn]: Error in event handler for "update-user": "TypeError: Cannot set property 'name' of undefined"

I have 2 vue components one is a list users and the other is the details of a user.
What I'm trying to do is, if I have to update a user's details then I want it to automatically update.
The problem I'm getting is that I'm getting this error
[Vue warn]: Error in event handler for "update-user": "TypeError: Cannot set property 'name' of undefined"
Here is my code for the user details component
<template>
<div class="card">
<div class="card-header">
<span class="text-success" #click="updateUser">Save Changes</span>
</div>
<div class="card-body p-0">
<div class="card card-primary card-outline card-outline-tabs">
<div class="card-body">
<div class="form-group">
<label for="name">Full Name</label>
<input type="text" class="form-control" id="name" v-model="name">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'user'],
data() {
return {
name: null,
}
},
mounted() {
},
methods: {
updateUser() {
axios.put('/admin/users/'+this.user.id, this.name).then(response => {
this.emit.$emit('update-user', {
user: this.name
});
});
}
}
}
</script>
and my vue component with the list of users
<template>
<div class="card">
<div class="card-body>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="user in showUsers">
<td>{{user.name}}</td>
<td style="text-align: right;"><button class="btn btn-sm btn-outline-info" #click="detail(user)">Manage</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
perPage: 10,
currentPage: 1,
}
},
computed: {
filterUsers() {
return (this.userFilter === '') ? this.users : this.users.filter(u => { return u.name.toLowerCase().indexOf(this.userFilter.toLowerCase()) > -1; });
},
showUsers() {
let start = (this.currentPage - 1) * this.perPage;
let end = start + this.perPage;
return this.filterUsers.slice(start, end);
}
},
methods: {
detail(user) {
this.emit.$emit('manage-user', { user: user });
}
},
mounted(){
this.emit.$on('update-user', payload => {
this.user.name = payload.user.name;
});
}
}
</script>
The error you met is caused by this.user doesn't exists in the context inside mounted() { this.emit.$on('update-user', ....}.
As my understanding, you'd like to update the name of a specific user, but in that context, you only have all users (this.users) inside mounted(), you have to filter out that specific user then apply the update.
Below is one solution:
If user.id is unique, you can emit user.id and user.name from updateUser, then in mounted, find the specific user by user.id, then update it.
updateUser() {
axios.put('/admin/users/'+this.user.id, this.name).then(response => {
this.emit.$emit('update-user', {
name: this.name, // new user.name
id: this.user.id // assuming user.id is unique
});
});
}
this.emit.$on('update-user', payload => {
this.users.find(user => user.id===payload.id).name = payload.name; // find the user then update its name
});

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.