v-on:keyup not firing function - vuejs2

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

Related

Cannot read properties of undefined on formatter, but data is showing fine

I have a bootstrap table that shows a list of appliances. I am importing my data with Axios and for this specific table I am outputting data from two database tables, so I have one object which is called applianceReferences which stores another object called activeAppliances.
Not sure if it is relevant for this question, but just so you know.
Before talking about the problem, let me just post the whole code and below I will talk about the section that is giving me issues.
<template>
<b-container class="my-2">
<b-card v-if="showTable" class="ml-4 mr-4">
<b-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</b-container>
</template>
<script>
import {applianceService} from "#/services/appliance";
import CommonCollapsible from "#/components/common/CommonCollapsible";
import moment from 'moment';
export default {
components: { CommonCollapsible, CommonTable },
props: {
ownerId: String,
ownerType: String,
showDocuments: Boolean,
goToAppliances: "",
importAppliances: ""
},
data() {
return {
applianceReferences: [],
showTable: true
}
},
computed: {
fields() {
return [
{
key: 'referenceName',
label: this.$t('referenceName'),
sortable: true
},
{
key: 'activeAppliance.type',
label: this.$t('type'),
sortable: true,
},
{
key: 'activeAppliance.brandName',
label: this.$t('brand'),
sortable: true
},
{
key: 'activeAppliance.purchaseDate',
label: this.$t('purchaseDate'),
sortable: true,
template: {type: 'date', format: 'L'}
},
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
{
key: 'activeAppliance.purchaseAmount',
label: this.$t('amount'),
sortable: true,
template: {
type: 'number', format: {minimumFractionDigits: '2', maximumFractionDigits: '2'},
foot: sum
}
},
{
key: 'actions',
template: {
type: 'actions',
head: [
{
text: 'overviewOfAppliances',
icon: 'fas fa-fw fa-arrow-right',
action: this.createAppliance
},
{
icon: 'fas fa-fw fa-file-excel',
action: this.importAppliance,
tooltip: this.$t('importAppliances'),
}
],
cell: [
{
icon: 'fa-trash',
variant: 'outline-danger',
action: this.remove
},
]
}
}
]
},
},
methods: {
load() {
Object.assign(this.$data, this.$options.data.apply(this));
this.applianceReferences = null;
applianceService.listApplianceReferences(this.ownerId).then(({data: applianceReferences}) => {
this.applianceReferences = applianceReferences;
this.applianceReferences.forEach( reference => {
applianceService.listAppliances(reference.id).then(result => {
this.$set(reference, 'appliances', result.data);
this.$set(reference, 'activeAppliance', result.data.find(appliance => appliance.active))
this.loaded = true
})
})
}).catch(error => {
console.error(error);
})
},
createAppliance(){
this.goToAppliances()
},
importAppliance(){
this.importAppliances()
},
},
watch: {
ownerId: {
immediate: true,
handler: 'load'
}
},
}
</script>
Okay, so the error occurs in this specific property:
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
What I am basically doing here is combining two values from the object: warrantyDuration and warrantyDurationType and putting them in one single row in my bootstrap table.
The problem is that this is giving me an error: Cannot read properties of undefined (reading 'warrantyDurationType'
Yet the data actually outputs normally.
So what exactly does it want me to do?
I tried wrapping a v-if around the table to make sure that the application checks if the data exist before outputting it, but this does not solve the issue.
<div v-if="applianceReferences && applianceReferences.activeAppliance">
<b-card v-if="showTable" class="ml-4 mr-4">
<common-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</div>
Last, just to give you a full overview, my array looks like this:
Any ideas?

Default props value are not selected in vue3 options api

I created a select2 wrapper in vue3 with options API everything working fine but the problem is that when getting values from calling API it's not selected the default value in the select2 option. but when I created a static array of objects it does. I don't know why it's working when it comes from the API
Parent Component
Here you can I passed the static options array in options props and my selected value is 2 and it's selected in my Select2 component, but when passed formattedCompanies it's not which is the same format as the static options array then why is not selected any reason here..?
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
:options="options"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : [],
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
Select2 Component
Here is what my select2 component look like, did I do anything wrong here, please anybody help me
<template>
<select class="form-control">
<slot/>
</select>
</template>
<script>
export default {
name: "Select2",
props: {
options: {
type: [Array, Object],
required: true
},
modelValue: [String, Number],
placeholder: {
type: String,
default: "Search"
},
allowClear: {
type: Boolean,
default: true
},
},
mounted() {
const vm = this;
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
},
watch: {
modelValue(value) { // update value
$(this.$el)
.val(value)
.trigger("change");
},
options(options) { // update options
$(this.$el)
.empty()
.select2({data: options});
},
},
destroyed() {
$(this.$el)
.off()
.select2("destroy");
}
}
</script>
Probably when this Select2 mounted there is no companies. It is empty array after that it will make API call and it it populates options field and clear all options.
Make:
companies : null,
Change it to
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
It should be like this:
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : null,
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
The problem was that my parent component and Select2 component mounted at the same time that's why my computed value is not initialized so the selected value is not selected in the option,
problem solved by setTimeOut function in mounted like this
Select2 Component
<script>
mounted() {
const vm = this;
setTimeout(() => {
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
}, 500)
},
</script>

Dropdown down item required

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
},
}

Vue.js: Child Component mutates state, parent displays property as undefined

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.

vee validate on custom select2 component not works

I try to use vee-validate on a custom component where if nothing selected on submit should show the validation error
the template looks as it follows
<div id="validate" class="container">
<form #submit.prevent="store()" data-vv-scope="time-off" autocomplete="off">
<div class="col-lg-6">
<div class="form-group">
<select2
:options="options"
placeholder='Select...'
v-validate="'required|in:sick, vacation'"
v-model="form.category"
id="categorywidget"
class="form-control">
</select2>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary pull-right">
Send Request
</button>
</div>
</div>
</div>
</form>
</div>
and here is my vue code
Vue.component('Select2', {
props: ['options', 'value', 'id', 'placeholder', 'clear'],
template: '<select><slot></slot></select>',
mounted: function () {
var vm = this
$(this.$el)
.select2({
data: this.options,
placeholder: this.placeholder,
theme: "bootstrap",
width: '100% !important',
dropdownAutoWidth : true,
allowClear: this.clear !== '' ? this.clear : false
})
.val(this.value)
.attr("id", this.id !== null ? this.id : Date.now() + Math.random() )
.trigger('change')
// emit event on change.
.on('change', function () {
vm.$emit('input', $(this).val())
})
},
watch: {
value: function (value) {
// update value
$(this.$el).val(value).trigger('change');
},
options: function (options) {
// update options
$(this.$el).select2({data: options})
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
});
Vue.use(VeeValidate)
new Vue({
el: '#validate',
data: {
form: {
scopes: [],
category: 'null',
daterange: '',
note: ''
},
options: [
{text: 'Vacation', id: 'vacation'},
{text: 'Sick', id: 'sick'},
{text: 'Not ok', id: 'not-ok'},
]
},
methods: {
store: function() {
this.$validator
.validateAll()
.then(function(response) {
alert('is ok!')
// Validation success if response === true
})
.catch(function(e) {
// Catch errors
alert('not ok!')
})
}
}
});
here is a pen what I created
https://codepen.io/anon/pen/gXwoQX?editors=1111
on submit null the validation is passes. what is wrong with this codes
There are issues raised on this subject in github - #590, #592.
None of them lead me to a solution, so I'd suggest a check inside the promise
.then(response => {
if(this.errors.items.length === 0) {
alert('is valid')
} else {
alert('is not valid')
}
A couple of other notes:
See below, catch() is technically the wrong place to handle validation errors, that should be inside the then(response) and when invalid response === false (but not working because of bug).
The catch() is probably for 'I blew up' errors.
.catch(function(e) {
// Catch errors
// alert('not valid') // don't process these here
})
Also this
v-validate="'required|in:sick, vacation'"
should be this (remove space before vacation)
v-validate="'required|in:sick,vacation'"