Hello I have a dropdown.
I need to make sure an option is selected, thus I want to make a client validation for required field.
<b-dropdown id="clientData"
name="clientData"
v-model="$v.clientData.selectedOption.$model"
:text="clientData.selectedOption"
class="m-2 col-2" no-flip>
<b-dropdown-item v-for="option in clientData.options"
:key="option.value"
:value="option.value"
#click="clientData.selectedOption = option.value">
{{option.text}}
</b-dropdown-item>
</b-dropdown>
<script>
import { required } from 'vuelidate/lib/validators'
const selectedOptionCheck = (selectedOption) => selectedOption !== 'Choose data type'
data () {
return {
clientData: {
csvFile: null,
selectedOption: 'Choose data type',
options: [
{
value: 'PhoneNumber',
text: 'Phone Number'
},
{
value: 'Email',
text: 'Email'
},
{
value: 'DeviceToken',
text: 'Device Token'
}
]
}
}
},
validations: {
clientData: {
selectedOption: {
required,
selectedOptionCheck
}
}
},
</script>
This is because I do not want to send incorrect request to the server.
I have used Vuelidate and it does not work. I have tried searching for a required tag and I have tried aria-required='Please choose an option' on the b-dropdown, however, again it does not work.
You can do following - set your v-model in your data return equal null.
Than try to use computed and check if something is selected like this:
computed: {
validData: function () {
return this.yourvmodelname
},
}
Related
I have a parent component that lists all the tasks:
<template>
<div class="tasks-wrapper">
<div class="tasks-header">
<h4>{{ $t('client.taskListingTitle') }}</h4>
<b-button variant="custom" #click="showAddTaskModal">{{ $t('client.addTask') }}</b-button>
</div>
<b-table
striped
hover
:items="tasks"
:fields="fields"
show-empty
:empty-text="$t('common.noResultsFound')">
</b-table>
<AddTaskModal />
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import AddTaskModal from '#/components/modals/AddTaskModal'
import moment from 'moment'
export default {
name: 'TaskListing',
components: {
AddTaskModal
},
data () {
return {
tasks: [],
fields: [
{ key: 'createdOn', label: this.$t('tasks.tableFields.date'), formatter: 'formatDate' },
{ key: 'domain', label: this.$t('tasks.tableFields.task') },
{ key: 'comment', label: this.$t('tasks.tableFields.comment') },
{ key: 'status', label: this.$t('tasks.tableFields.status') }
]
}
},
computed: {
...mapGetters('users', ['user'])
},
methods: {
...mapActions('tasks', ['fetchTasks']),
...mapActions('users', ['fetchUserById']),
formatDate: function (date) {
return moment.utc(date).local().format('DD.MM.YYYY HH:mm')
},
showAddTaskModal () {
this.$bvModal.show('addTaskModal')
}
},
async mounted () {
const currUserId = this.$router.history.current.params.id
if (this.user || this.user.userId !== currUserId) {
await this.fetchUserById(currUserId)
}
if (this.user.clientNumber !== null) {
const filters = { clientReferenceNumber: { value: this.user.clientNumber } }
this.tasks = await this.fetchTasks({ filters })
}
}
}
</script>
Inside this component there is a child which adds a task modal.
<template>
<b-modal
id="addTaskModal"
:title="$t('modals.addTask.title')"
hide-footer
#show="resetModal"
#hidden="resetModal"
>
<form ref="form" #submit.stop.prevent="handleSubmit">
<b-form-group
:invalid-feedback="$t('modals.requiredFields')">
<b-form-select
id="task-type-select"
:options="taskTypesOptions"
:state="taskTypeState"
v-model="taskType"
required
></b-form-select>
<b-form-textarea
id="add-task-input"
:placeholder="$t('modals.enterComment')"
rows="3"
max-rows="6"
v-model="comment"
:state="commentState"
required />
</b-form-group>
<b-button-group class="float-right">
<b-button variant="danger" #click="$bvModal.hide('addTaskModal')">{{ $t('common.cancel') }}</b-button>
<b-button #click="addTask">{{ $t('modals.addTask.sendMail') }}</b-button>
</b-button-group>
</form>
</b-modal>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'AddTaskModal',
data () {
return {
comment: '',
commentState: null,
taskTypesOptions: [
{ value: null, text: this.$t('modals.addTask.taskType') },
{ value: 'OnBoarding', text: 'Onboarding' },
{ value: 'Accounts', text: 'Accounts' },
{ value: 'TopUp', text: 'Topup' },
{ value: 'Overdraft', text: 'Overdraft' },
{ value: 'Aml', text: 'Aml' },
{ value: 'Transfers', text: 'Transfers' },
{ value: 'Consultation', text: 'Consultation' },
{ value: 'TechnicalSupport', text: 'TechnicalSupport' },
{ value: 'UnblockPin', text: 'UnblockPin' },
{ value: 'Other', text: 'Other' }
],
taskType: null,
taskTypeState: null
}
},
computed: {
...mapGetters('users', ['user']),
...mapGetters('tasks', ['tasks'])
},
methods: {
...mapActions('tasks', ['addNewTask', 'fetchTasks']),
...mapActions('users', ['fetchUserById']),
async addTask (bvModalEvt) {
bvModalEvt.preventDefault()
if (!this.checkFormValidity()) { return }
const currUserId = this.$router.history.current.params.id
if (this.user || this.user.userId !== currUserId) {
await this.fetchUserById(currUserId)
}
const data = {
clientPhone: this.user.phoneNumber,
comment: this.comment,
clientReferenceNumber: this.user.clientNumber,
domain: this.taskType
}
await this.addNewTask(data)
if (this.user.clientNumber !== null) {
const filters = { clientReferenceNumber: { value: this.user.clientNumber } }
this.tasks = await this.fetchTasks({ filters })
// this.tasks may be useless here
}
console.log(this.tasks)
this.$nextTick(() => { this.$bvModal.hide('addTaskModal') })
},
checkFormValidity () {
const valid = this.$refs.form.checkValidity()
this.commentState = valid
this.taskTypeState = valid
return valid
},
resetModal () {
this.comment = ''
this.commentState = null
this.taskTypeState = null
}
}
}
</script>
When I add a task I call getalltasks to mutate the store so all the tasks are added. Then I want to render them. They are rendered but the property createdOn on the last task is InvalidDate and when I console log it is undefined.
The reason I need to call gettasks again in the modal is that the response on adding a task does not return the property createdOn. I do not want to set it on the front-end, I want to get it from the database.
I logged the store and all the tasks are added to the store.
Why is my parent component not rendering this particular createdOn property?
If I refresh the page everything is rendering fine.
If you add anything into a list of items that are displayed by v-for, you have to set a unique key. Based on your explanation, I assume that your key is the index and when you add a new item, you mess with the current indexes. Keys must be unique and unmutateable. What you need to do is to create a unique id for each element.
{
id: Math.floor(Math.random() * 10000000)
}
When you create a new task, use the same code to generate a new id, and use id as key. If this doesn't help, share your d-table and related vuex code too.
I am using nuxt-vue-select to allow users to select multiple objects form an array locations and then I want to use Vuelidate to try and validate that at least one item in the array has been selected/set on blur however I can not get vuelidate do do this. What am I doing wrong here?
Template
<b-form-group label="Locations" label-for="locations">
<client-only>
<v-select
v-model="userCopy.locations"
name="locations"
filterable
multiple
placeholder="Select locations"
label="label"
:options="locations"
:class="{ 'is-invalid': v.form.locations.$error }"
#blur="v.form.locations.$each[index].$touch()"
/>
</client-only>
</b-form-group>
Script
data() {
return {
form:{
locations: []
}
}
},
validations() {
return {
form: {
locations: {
$each: {
required
}
}
}
}
}
Array data
{ label: 'Avon' },
{ label: 'Bedfordshire' },
{ label: 'Berkshire' },
{ label: 'City of Brighton and Hove' },
{ label: 'City of Bristol' },
For vuelidate you have to use the same filed name as $data, so you need to replace the name form of validations' object to userCopy at first, and apply minLength for your locations length:
validations() {
return {
userCopy: { // change this
locations: {
// ... to try and validate that at least one item in the array
// use minLength to do that
minLength: minLength(1)
$each: {
required
}
}
}
}
}
To deal with array structure, I recommend to try form-validation.js.
i am new to VueJs and Vuetify and i have a script that read data from a Json and display it into a Table.is a glossary type app. I have the option to select from different languages to be shown into the table. The problem is that when i click on a row i would like to display into a popup (alert) the item information. What i did is not working at all, is just showing the Alert popup but without information.
The format of the Json is:
{"glossary":[
{"id":2,"English":{"term":"contact","definition":"the state of physical touching"},"Vietnamese":{"term":"tiếp xúc"},"Simplified_Chinese":{"term":"接触"},"Arabic":{"term":"ملامسة"},"Swahili":{"term":"mgusano"}}]}
<v-data-table dense light :headers="selectedHeaders" :item-key="id" #click:row="showAlert(item)" :items="glossary.glossary" class="elevation-1" :single-expand="true" :disable-sort=true :search="search">
<template #item.ar.term="{item}">
<div style="text-align:right;">
<span>{{item.ar.term}}</span>
</div>
</template>
</v-data-table>
<script>
import About from '#/views/About.vue'
import json from '#/assets/data/glossary.json'
export default {
name: 'App',
components: { About },
data () {
return {
publicPath: process.env.BASE_URL,
glossary: json,
search: '',
value: [],
expanded: [],
selectedHeaders: [],
dialog: false,
headers: [
{ text: 'English', value: 'English.term' },
{ text: 'Vietnamese', value: 'Vietnamese.term' },
{ text: 'Arabic', value: 'Arabic.term' },
]
}
},
methods: {
filter(value, search, item) {
return value != null &&
search != null &&
typeof value === 'string' &&
value.toString().toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1
},
showAlert(a){
if (event.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+this.English.term );
console.log(this);
}
watch: {
value(val) {
this.selectedHeaders = val ;
}
},
created() {
this.selectedHeaders = this.headers;
}
}
</script>
You are not using a which is the item passed. this just returns the vue object. What you might want is this
showAlert(a){
if (a.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+a.English.term );
console.log(a);
}
Here is a codepen that I found that should help you understand it better https://codepen.io/nsiggel/pen/KRdGgE. Note: I did not write this codepen.
Im using Vuetify, and have a form where im using VeeValidate for form validation...
When im using this:
this.$validator.validateAll().then((result) => {
console.log("result form", result);
//result ? this.onSubmit() : scrollTo(0, 250);
});
It always returns true, even if the validation on my input field isn't valid...
The input looks like:
<v-textarea
filled
name="string"
:label="placeholderText"
auto-grow
single-line
:placeholder="placeholderText"
v-model="answer"
:required="isRequired"
v-validate:computedProp="checkRequired"
:error-messages="errors.collect('string')"
data-vv-name="string"
:hint="hintText"
#blur="updateAnswer"
></v-textarea>
The code for the input component:
export default {
$_veeValidate: {
validator: 'new'
},
name: 'String',
props: {
placeholderText: {
default: 'Add a value'
},
hintText: {
default: 'Add a value'
},
isRequired: {
default: true
}
},
data: () => ({
answer: ''
}),
computed: {
checkRequired() {
return this.isRequired ? 'required' : ''
}
},
methods: {
updateAnswer() {
this.$validator.validateAll();
this.$emit('updateAnswer', this.answer);
}
}
}
Im calling this.$validator.validateAll() in another component... The input component is a standalone component... I have it all wrappet in a form tag, and running the validate function on a submit
You have two choice:
Pass to the component the v-validate from the $attrs
Inject the $validator to the component
Parent
export default {
name: "App",
components: {
YourComponent
},
provide() {
return {
$validator: this.$validator
};
},
Child
$_veeValidate: {
validator: "new"
},
inject: ["$validator"],
name: "String",
You can also simplify your html code, see the VeeValidate Syntax
Html
v-validate="{ required: this.isRequired }"
And you can safely remove
:required="isRequired"
For some reason I cannot get my keyup event to fire. I would like it to listen to any and all keyup events as I am trying to make a live search function
Here is my code:
<template>
...
<b-form-input id="search"
v-model="query"
type="text"
placeholder="Search"
v-on:keyup="find(search, query)">
</b-form-input>
....
</template>
<script type="text/javascript">
export default {
data() {
return {
options: [
{ text: 'All', value: 'All' },
{ text: 'Members', value: 'Members' },
{ text: 'Groups', value: 'Groups' },
{ text: 'Events', value: 'Events' }
],
search: 'All',
query: '',
results: []
}
},
methods: {
find: function (param = 'All', string = '') {
axios.get('/api/search/' + param.toLowerCase() + '?query=' + string)
.then(({data}) => {
this.results = data.data;
});
}
}}
</script>
I've only been able to do v-on:change to get it to fire.
b-form-input only supports input and change event.
To use keyup event listener, add .native to it.
#keyup.native="..."
From https://bootstrap-vue.js.org/docs/components/form-input#component-reference