Vuetify v-autocomplete custom filtering bug - javascript

customFilter (item, queryText) {
const textOne = item.designation.toLowerCase()
const textTwo = item.reference.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
}
<v-autocomplete
dense=""
v-model="selectedProduct"
prepend-inner-icon="mdi-magnify"
rounded=""
:items="products_references_desginations"
:filter="customFilter"
item-text="designation"
filled
label="Recherche"
single-line=""
return-object=""
clearable=""
>
This works most of the time but it happens that I get this error :
I guess that it is the item variable in my custom filter that gets null sometimes but I have no idea why this happens. When it happens I have to reload the server and then it works fine and after some time, same error occurs.
FULL COMPONENT:
import {mapGetters, mapMutations} from 'vuex'
export default {
data: () => ({
selectedProduct: null,
}),
watch: {
selectedProduct(prod) {
console.log(prod)
if(prod != null) {
this.$emit('selectedProductFromSearchBar', prod)
}
}
},
methods: {
...mapMutations([]),
toProductDetailsPage(product){
console.log(product)
},
customFilter (item, queryText) {
const textOne = item.designation.toLowerCase()
const textTwo = item.reference.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
},
},
computed: {
...mapGetters(['products_references_desginations']),
},
mounted () {
},
}
<template>
<div class="">
<v-autocomplete
dense=""
v-model="selectedProduct"
prepend-inner-icon="mdi-magnify"
class="pa-0 search-bar"
rounded=""
:items="products_references_desginations"
:filter="customFilter"
item-text="designation"
filled
color="blue-grey lighten-2"
label="Recherche"
single-line=""
return-object=""
clearable=""
>
<template v-slot:item="data">
<template>
<v-list-item-content style=" overflow: hidden">
<v-list-item-title v-html="data.item.designation"></v-list-item-title>
<v-list-item-subtitle v-html="data.item.reference"></v-list-item-subtitle>
</v-list-item-content>
</template>
</template>
</v-autocomplete>
</div>
</template>
Structure of my data :

The problem was in my data, a null product was created I don't know how yet. But the problem is not from v-autocomplete as I thought.

Related

Manipulating array in vue3 using v-model [edited]

The question has three parts revolving around two files App.vue and X Array.vue :-
1.When value of input is changed, how it could be written back to the array?
2.If the value entered is empty how to remove the element from array?
3.How to show one extra input element always so that it is possible to add new values(linked with 2)?
XArray should basically be an array editor.
App.vue
<template>
<div>
<XArray v-model="myArray" />
<pre>{{ myArray }}</pre>
</div>
</template>
<script>
import XArray from './components/XArray.vue';
export default {
components: {
XArray,
},
data: () => {
return {
myArray: ['one', 'two'],
};
},
};
</script>
XArray.vue
<template>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', [...modelValue, `${$event.target.value}`])"
/>
</template>
<script>
export default {
props: {
modelValue: {
type: Array,
required: true,
},
};
</script>
Please take a look at following snippet:
const app = Vue.createApp({
data() {
return {
myArray: ['one', 'two'],
}
},
methods: {
addEl() {
this.myArray.push('new')
}
}
})
app.component('child', {
template: `
<div>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', upd(index, $event.target.value))"
:value="option"
/>
</div>
`,
props: {
modelValue: {
type: Array,
required: true,
}
},
methods: {
upd(idx, val) {
return val ? [
...this.modelValue.map((item, i) =>
i !== idx
? item
: val
),
] : this.modelValue.length > 1 ?
[ ...this.modelValue.filter((item, i) => {
if(i !== idx) return item
})] :
[ "last" ]
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<child v-model="myArray"></child>
<pre>{{ myArray }}</pre>
<button #click="addEl">add</button>
</div>

Vue i18n-$t undefined outside template

Hi so if I use {{$t('dash.port')}} inside of template the translation happens and everything works fine.
Now I have an antdv table where i have columns declared this way :
const columns = [
{
title:"pone",
dataIndex: 'pone',
key: 'pone',
},
...
]
//Here's the antdv table component :
<template>
<a-table :data-source="data" :columns="columns">
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<div style="padding: 8px">
<a-input
ref="searchInput"
:placeholder="`Search ${column.dataIndex}`"
:value="selectedKeys[0]"
style="width: 188px; margin-bottom: 8px; display: block"
#change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
#pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
/>
<a-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
#click="handleSearch(selectedKeys, confirm, column.dataIndex)"
>
<template #icon><SearchOutlined /></template>
Search
</a-button>
<a-button size="small" style="width: 90px" #click="handleReset(clearFilters)">
Reset
</a-button>
</div>
</template>
<template #filterIcon="filtered">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template>
<template #customRender="{ text, column }">
<span v-if="searchText && searchedColumn === column.dataIndex">
<template
v-for="(fragment, i) in text
.toString()
.split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))"
>
<mark
v-if="fragment.toLowerCase() === searchText.toLowerCase()"
class="highlight"
:key="i"
>
{{ fragment }}
</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
<template v-else>
{{ text }}
</template>
</template>
</a-table>
//script part where $t not working
<script>
import { SearchOutlined } from '#ant-design/icons-vue';
import { defineComponent, reactive, ref } from 'vue';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
..
];
export default defineComponent({
components: {
SearchOutlined,
},
setup() {
const state = reactive({
searchText: '',
searchedColumn: '',
});
const searchInput = ref();
const columns = [
{
title: 'pone',
dataIndex: 'pone',
key: 'pone',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) =>
record.pone.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
console.log(searchInput.value);
searchInput.value.focus();
}, 0);
}
},
},
....
];
const handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
state.searchText = selectedKeys[0];
state.searchedColumn = dataIndex;
};
const handleReset = clearFilters => {
clearFilters();
state.searchText = '';
};
return {
data,
columns,
handleSearch,
handleReset,
searchText: '',
searchInput,
searchedColumn: '',
};
},
});
</script>
What I want is to change title using $t but when I do title:"$t('dash.pone')", I get $t not defined. How can I make this work?
I did not learnt vue3 yet so I am not sure on how it works but you should probably give a look to all the examples down there: https://github.com/intlify/vue-i18n-next/tree/master/examples/composition
But maybe this one is working?
const app = createApp({
setup() {
const { t, locale } = useI18n()
t('dash.port') // this one maybe works ?
return { t, locale }
}
})
Ah, I see, you are using new Vue3 composition API. Well, vue-i18n is a bit behind, but there is repo for the next version 9. Upgrade the package and follow its migration instructions, then use your translations in setup functions like this:
import { defineComponent, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
setup() {
const { tm } = useI18n();
const columns = [
{
title: tm('dash.pone'),
dataIndex: 'pone',
key: 'pone',
// ...
},
];
];
With vue3 and and composition API i had the same issue, i solve the problem with this
Import i18n (change the path)
import i18n from '#/plugins/i18n'
Then for accessing the $t function
i18n.global.t("WordToTranslate")

Error:[vuex] do not mutate vuex store state outside mutation handlers vuex (nuxt.js)

I've already searched what causes this error message to appear but could not really get my head around it in my code. I'm using Nuxt.js with Vuex. I just can't see where am i modifying the store without mutation. Can someone please tell me what am i doing wrong ? This error message appears when i pick some date from the date picker component. This solution works as intended, it's just the error message that bugs me and i dont really want to solve it by turning off the strict mode for store.
Component.vue
<template>
<v-col class="chart">
<v-card>
<v-card-title>Daily beta trend</v-card-title>
<v-row>
<v-col cols="4">
<b-form-group label="From" class="ml-3">
<b-form-datepicker id="datepicker-from" v-model="chartDataFrom" class="mb-2" />
</b-form-group>
</v-col>
<v-col cols="4">
<b-form-group label="To">
<b-form-datepicker id="datepicker-to" v-model="chartDataTo" class="mb-2" />
</b-form-group>
</v-col>
<v-col cols="4">
<b-form-group label="OS Version" class="mr-3">
<b-form-select v-model="selectedOS" :options="OSVersions">
<template #first>
<b-form-select-option :value="null" disabled>
-- Please select OS version --
</b-form-select-option>
</template>
</b-form-select>
</b-form-group>
</v-col>
</v-row>
<v-skeleton-loader
v-if="$fetchState.pending"
class="mx-auto"
max-width="300"
type="card"
/>
<p v-else-if="$fetchState.error">
An error occurred :(
</p>
<div v-else>
<Chart :data="chartData" />
</div>
</v-card>
</v-col>
</template>
<script>
import Chart from '~/components/Trends/Charts/Chart'
export default {
name: 'BetaTrend',
components: {
Chart
},
async fetch () {
await this.$store.dispatch('beta_trend/getChartData')
},
fetchOnServer: false,
computed: {
chartData: {
get () {
return this.$store.state.beta_trend.chartData
},
set (data) {
this.$store.commit('beta_trend/SET_CHART_DATA', data)
}
},
chartDataFrom: {
get () {
return this.$store.state.beta_trend.from
},
set (value) {
this.$store.commit('beta_trend/SET_FROM', value)
this.$fetch()
}
},
chartDataTo: {
get () {
return this.$store.state.beta_trend.to
},
set (value) {
this.$store.commit('beta_trend/SET_TO', value)
this.$fetch()
}
},
OSVersions: {
get () {
return this.$store.state.beta_trend.os
}
},
selectedOS: {
get () {
return this.$store.state.beta_trend.selectedOs
},
set (value) {
this.$store.commit('beta_trend/SET_SELECTED_OS', value)
this.$fetch()
}
}
}
}
</script>
<style scoped>
</style>
the store is defined like this:
export const state = () => ({
chartData: [],
from: null,
to: null,
os: [
'All',
'1803',
'19042'
],
selectedOs: null
})
export const mutations = {
SET_CHART_DATA (state, data) {
state.chartData = data
},
SET_FROM (state, from) {
state.from = from
},
SET_TO (state, to) {
state.to = to
},
SET_SELECTED_OS (state, os) {
state.selectedOs = os
}
}
export const actions = {
async getChartData ({ commit, state }) {
const data = await this.$axios.$get('api/frontend/trend/XXX', {
params: {
from: state.from,
to: state.to,
os: state.selectedOs
}
})
commit('SET_CHART_DATA', data)
if (state.from === null) {
commit('SET_FROM', data.dates[0])
}
if (state.to === null) {
commit('SET_TO', data.dates[data.dates.length - 1])
}
}
}
I fix this issue by using Lodash's cloneDeep method on the get() of my computed option. I do have the state that is deep copied, this way it prevents any mutation of the actual object, and can therefore be modified by the set() in the same way.
The only needed thing is
import { cloneDeep } from 'lodash-es'
export default {
[...]
chartData: {
get () {
return cloneDeep(this.$store.state.beta_trend.chartData)
},
[...]
},
PS: also worth mentioning that JSON.parse(JSON.stringify(object)) is not recommended: https://flaviocopes.com/how-to-clone-javascript-object/#wrong-solutions

Inputs and models not correctly filling with data

So In my application I am trying to load a quiz and its data from an API, which works perfectly fine. Then I try to reperesent this data in the UI, which works partially fine. There are some very weird things going on which seem like magic to me.
The first example is when I am trying to load the quiz and its questions in my main page: QuizEditor.vue. This page and its loading look like this:
export default {
components: {QuestionCard, EditorGeneralBar, EditorQuestionBar},
name: 'QuizEditor',
data() {
return {
count: 1,
editorTitle: "General",
newQuiz: true,
newQuestion: true,
currentQuestion: null,
quiz: {
tn: "",
title: "My quiz",
description: "A quiz",
timeLimit: 60,
theme: 1
},
options: {
multipleDropzonesItemsDraggingEnabled: false,
dropzoneSelector: ".q-list",
draggableSelector: ".question-card"
},
currentPosition: 1,
questions: []
}
},
mounted() {
var QE = this;
QuizTemplateRepository.getTemplate("k472ahe3r", this.$store.state.X_CSRFToken).then(function (res) {
QE.quiz = {
tn: "k472ahe3r",
title: res.data.template.label,
description: res.data.template.description,
};
QuizTemplateRepository.getTemplateContent(QE.quiz.tn, QE.$store.state.X_CSRFToken).then(function (res) {
var questions = JSON.parse(res.data.content.content).questions;
QE.quiz.theme = JSON.parse(res.data.content.content).properties.theme;
QE.quiz.timeLimit = JSON.parse(res.data.content.content).properties.timeLimit;
QE.quiz.questions = questions;
questions.forEach(question => {
QuestionRepository.getQuestion(question, QE.$store.state.X_CSRFToken).then(function (resQuest) {
var vogh = resQuest.data.var[0].vogh;
AnswerRepository.getAnswer(vogh, QE.$store.state.X_CSRFToken).then(function(resAnswer) {
var quest = {
name: resQuest.data.var[0].name,
hash: resQuest.data.var[0].vh,
vogh: resQuest.data.var[0].vogh,
label: resQuest.data.var[0].label,
position: resQuest.data.var[0].position,
description: "",
answers: []
}
resAnswer.data.varoptiongroup.forEach(answer => {
answer.place = QE.getPositionString(answer.position);
if(answer.value > 0)
answer.isCorrect = true;
else
answer.isCorrect = false;
quest.answers.push(answer);
});
QE.questions.push(quest);
QE.currentPosition++;
});
})
});
});
});
},
...
}
The data above from the API calls load just fine and I have quadrupple checked this by putting console logs on practically every line. The first issue comes from EditorGeneralBar, this will have the quiz object which it will receive like this:
<EditorGeneralBar :quiz="quiz" v-if="editorTitle === 'General'" #submitQuiz="submitQuiz" #themeSelected="selectTheme"></EditorGeneralBar>
This component looks like this:
<template>
<div class="bar-content">
<q-form
#submit="submit"
class="q-gutter-md"
>
<q-input
filled
v-model="quiz.title"
label="Title"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-input
filled
type="text"
v-model="quiz.description"
label="Description"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-input
filled
type="number"
v-model="quiz.timeLimit"
label="Time limit"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-file filled bottom-slots v-model="quiz.thumbnail" label="Thumbnail">
<template v-slot:before>
<q-icon name="folder_open" />
</template>
<template v-slot:hint>
A thumbnail for the quiz.
</template>
<template v-slot:append>
<q-btn round dense flat icon="add" #click.stop />
</template>
</q-file>
<p>Themes</p>
<div class="theme-list">
<div class="theme-1 theme-preview" v-on:click="selectTheme(1)"></div>
<div class="theme-2 theme-preview" v-on:click="selectTheme(2)"></div>
<div class="theme-3 theme-preview" v-on:click="selectTheme(3)"></div>
<div class="theme-4 theme-preview" v-on:click="selectTheme(4)"></div>
<div class="theme-5 theme-preview" v-on:click="selectTheme(5)"></div>
</div>
<div>
<q-btn label="Save" type="submit" color="primary"/>
</div>
</q-form>
</div>
</template>
<script>
export default {
name: 'EditorGeneralBar',
props: ["quiz"],
components: [
'QBtn'
],
methods: {
submit:function() {
this.$emit("submitQuiz");
},
selectTheme:function(theme) {
this.$emit("themeSelected", theme);
}
}
}
</script>
Everything in this component gets filled just fine, but not the timeLimit, this only gets loaded when I edit something like the description or title.
This also happens on the QuizEditor.vue where I try to load a class based on the template's theme.
This will be done like below(thase are placed in methods):
/**
* Select a theme by its number.
* #param {Number} theme
*/
selectTheme: function(theme) {
this.quiz.theme = theme;
console.log(this.quiz.theme);
},
/**
* Get the selected theme of the quiz.
*/
getTheme: function() {
return "theme-"+this.quiz.theme;
},
and the html looks like this:
<div class="question-listing" v-bind:class="getTheme()">
...
</div>
Summary
So to summarize it all, each component only loads data partially. The full data is there when I load it from the API and the data which is not being shown in the UI comes when I manipulate something in that component.
Can somebody tell me what is going wrong and how I can load ALL of the data correctly into my UI.

Passing data to the modal in Vue not working

I am trying to pass data to the UserModal. But the issue I am facing here is that the value of
user_clicked field is set when the openuserdialog method runs(checked in console: the value is assigned) but I am not able to pass it as an argument to the modal. Please help me solve the problem.
<v-data-table :items="users" :disable-initial-sort="true" :mustSort="true" hide-actions>
<template slot="items" slot-scope="props">
<td>{{ props.item.file_type.name }}</td>
<td>{{ props.item.created_at | moment }}</td>
<td><a #click="openUserDialog(props.item.id, props.item.user_type)" href='javascript:void(0);' class="details-link"><span class="hidden-xs-only">UserTypes</span><span class="hidden-sm-and-up">User Types</span></a></td>
</template>
</v-data-table>
<v-dialog v-model="userDialog" max-width="1275">
<UserModal :document="user_clicked" />
<div class="text-xs-right">
<v-btn class='vue-file-button text-right' #click="closeUserDialog" >Close</v-btn>
</div>
</v-dialog>
<script>
import UserModal from 'views/users/shortlisted_users.vue';
export default {
components: {
UserModal
},
data: function() {
return {
userDialog: false,
user_clicked: ''
}
}
methods: {
openUserDialog(document_id, user_type) {
this.userDialog = true;
this.user_clicked = user_type;
console.log(this.user_clicked);
},
closeUserDialog(document_id) {
this.userDialog = false;
}
}
</script>
Update 1
openUserDialog(document_id, user_type) {
this.user_clicked = user_type;
this.userDialog = true;
console.log(this.user_clicked);
}
Update 2
<template>
<div>
<v-card id="users-card">
<Users :users="users"></Users>
</v-card>
</div>
</template>
<script>
import 'vue-awesome/icons';
import Icon from 'vue-awesome/components/Icon';
import Users from 'views/user/_user_table.vue';
export default {
components: {
Icon,
Users
},
props: ['document'],
data: () => ({
users: [],
tab_view: 'tab-users-card'
}),
created: function() {
console.log(this.document);
this.fetchUsers(this.document);
},
methods: {
fetchUsers(document) {
this.$axios.get('/my_account/users/document_suggested_users.json', {
params: {
document: document.id
}
})
.then(response => {
this.users = response.data;
})
},
}
};
</script>
The problem is that you are trying to use document in the created handler of the component which is far too early in its life-cycle.
Instead, one approach is to use a watch handler in your UserModal like this:
watch: {
document: function () {
console.log(this.document);
if (this.document) {
this.fetchUsers(this.document);
}
}
}
Try to declare your prop like so:
props: {
document: Object
}

Categories