validating multiple fields with same name not working in vue.js - vue.js

I would like to validate multiple fields with same name but for some reason its not working.If i make first input blank,nothing happens.If i make second input blank ,i get errors message on first input.Not sure what i am doing wrong.
<tr v-for="(name, index) in form"
:key="index">
<td>
<div>
<input
class="input"
:class="{
'is-danger': hasErrorName[index] === true
}"
#keyup.prevent="validateField('name',index)"
type="text"
v-model="name.description"
>
</div>
<span v-if="hasErrorName[index] === true"
class="help is-danger">
{{ msgName[index] }}
</span>
</td>
</tr>
data() {
return {
form: {},
hasErrorName: {},
msgName: {},
};
},
method() {
validateField(field,index) {
if (field === 'name') {
if (!this.form[index].description) {
this.hasErrorName[index] = true;
this.msgName[index] = 'Name is required.';
} else {
this.hasErrorName[index] = false;
this.msgName[index] = null;
}
}
}

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.

How to close a dropdown list when clicking anywhere

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>

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.

Create autocomplete input Vue Js

I try to create a method that let the user to insert just value that exists in database, like an autocomplete.
Right now returns a list, but that list is not sorted, doesn't matter what I type the data from the list are the same.
<b-form-group label="Name" label-for="name-input">
<b-form-input
id="name-input"
v-model="query"
#keyup="autoComplete"
></b-form-input>
<div v-if="results.length">
<ul>
<li class="list-group-item" v-for="(result, index) in results" :key="index" id="display-none" #click="suggestionClick(result.name)">
{{ result.name }}
</li>
</ul>
</div>
</b-form-group>
autoComplete() {
this.results = [];
if (this.query.length > 2) {
get("/datafromapi", {
params: {
q: this.query
}
}).then(response => {
this.results = response.data.data;
});
}
},
suggestionClick(index) {
this.query = index
var element = document.getElementById("display-none");
element.classList.add("display-none");
},

Vuejs - v-model with multiple checkboxes

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>