I have a problem with input search field in vue.js.
In my data I have nested arrays, which have titles. When I search the titles and hit space, nothing appears. And titles should be searched with spaces. For example I have a title "Pencils in store" and when I write "Pencils" in input, it does appear with all other information. When I write "Pencils in" nothing appears. Also, in some of the titles I can't reach to the blank space, results appears only while I write "Pen". Here is my code, I have tried with trim and split, but it did not work.
Very thanks in advance!
computed: {
getfiltered() {
const search = this.search;
return (
this.categories.filter(category =>
category.title.includes(search.toLowerCase()) ||
category.infos.some(info => info.name.includes(search)
)
)
)
},
Here is the input field
<input type="text" v-model="search" placeholder="Search" />
<div v-for="(category, categoryIndex) in getfiltered" :key="categoryIndex">
<h2>{{category.title}}</h2>
</div>
And the data
export default {
data: () => ({
search: '',
categories: [
{
title: 'Pencils in store',
infos: [{name:'Bic'},
{name:'Crayola'}]
}, //...and so on
The problem is solved in a way that #Estus Flask commented.
сategory.title.toLowerCase().includes(search.toLowerCase())
Related
I am using bootstrap table to display some error messages that are stored in a database. I fetch them with Axios.
When showing the error messages in the table row, I use a substring to minimize the output to 30 characters, as they can often be over 1000 characters long.
Then I have a modal component that takes in the array as a prop and output the same error message when you click on a specific message in the table.
The problem is that I do not want the modal to show the substring when I click on one of the messages in the table. I would like the message to pop up in the modal WITHOUT the substring while still keeping it in the table, so that the user is able to see the full message when click on the substringed message.
How can I accomplish this?
Parent:
<template>
<b-container>
<b-card class="mt-4">
<h5>{{ $t('events') }}</h5>
<b-table
:items="errors"
:fields="fields"
:per-page="[5, 10]"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
#row-clicked="showModal"
sort-desc
/>
</b-card>
<error-log-entry-modal ref="errorLogEntryModal" :selected-error-log="selectedRows"/>
</b-container>
</template>
<script>
import {axiosService} from '#/services/error';
import ErrorLogEntryModal from '#/components/error-log/ErrorLogEntryModal';
export default {
components: {
ErrorLogEntryModal,
},
data() {
return {
errors: null,
selectedRows: []
};
},
computed: {
fields() {
return [
{
key: 'errorMessage',
label: this.$t('message'),
sortable: true
},
]
},
},
methods: {
load(){
if
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.errorMessage = log.errorMessage.substring(0,30));
this.errors = result.data
})
},
onRowSelected(fields){
this.selectedRows = fields
},
showModal(){
if (this.selectedRows) {
this.$refs.errorLogEntryModal.show()
}
},
},
created() {
this.load()
}
};
</script>
child:
<template>
<b-modal
modal-class="error-log-modal"
v-model="showModal"
size="xl"
title="Error Log">
<b-col class="lg-12" v-for="log in selectedErrorLog">
{{ log.errorMessage }}
</b-col>
</b-modal>
</template>
<script>
export default {
props: {
selectedErrorLog: Array
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
}
}
};
</script>
Sounds like you want to undo making errorMessage a substring of its initial value. But that is not possible, once data is gone, it is lost.
If you want to show a shortened message in the table, but the full message in the modal, you can just store the shortened message in another property instead of overriding the errorMessage property:
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.errorMessageShort = log.errorMessage.substring(0,30));
this.errors = result.data
})
Then you can use errorMessageShort in the table and errorMessage in the modal.
You don't need to re-assign the error message after trimming that. You can keep the error message as it is, and at the time of rendering, display only 30 characters but when you will pass this string as a prop, it will be passed as a whole value.
My meaning is-
Remove this code-
log.errorMessage = log.errorMessage.substring(0,30));
and simply keep-
this.errors = result.data
At the time of rendering, show the error message like this-
<div v-for="error in errors">{{ error.substring(0, 30) }}</div>
And when you need to pass the error to the modal component, you can pass it as it is because it didn't trim actually.
<Modal :error="error" />
This approach is moreover like toggling between trimmed and full text. By doing this, you don't need to use multiple variables to store trimmed errors and full errors individually.
I am working in a vue component and have a v-for loop in the html that creates some v-text-fields. I want to be able to verify that the v-text-field matches one of the elements in an answer array. I have it set up like this below right now.
<v-expansion-panel
v-for="(element, index) in prompts"
:key="'bucket-a-' + index"
>
<v-expansion-panel-header>
<v-container>
<v-expansion-panel-content>
<v-text-field
label="Answer must match one of the answer values from above"
clearable
v-model="element.answer"
:error="validator"
#input="answerInputValidator($event, element)"
></v-text-field>
</v-expansion-panel-content>
</v-container>
</v-expansion-panel-header>
</v-expansion-panel>
The answer input validator function is set up like this below:
answerInputValidator(evt, prompt) {
this.answerObjects.forEach(element => {
if(element.value === evt){
prompt.answer = evt;
return this.validator = false;
}else{
return this.validator = true;
}
});
}
The function works to validate the v-text-field and links to :error with the property this.validator. However, the issue I am having is that this.validator is a variable declared on the whole of the component so if one input area is found to be valid and the user moves onto the next one and starts inputting and invalid response the previous input area will also be set to invalid. Because this.validtor then gets set to true because the input is invalid on the element being manipulated. But I don't want it to affect all other v-text-fields. I need to be able to check the input against an array of information but treat each field differently. Any ideas on how to achieve this?
You'll need as many error validator flags as you have v-text-field elements. I don't know the structure of the prompts array but maybe each element of it can have its own isError property initialized to false.
Then each v-text-field can have the error prop set as :error="element.isError" Then the validator method can receive each element and toggle that element's individual isError flag on or off without affecting any others.
I don't know how v-text-field works since I have never user Vuetify, but as another answer says each of the prompt could have a property to check is the answer match.
Here is a snippet of how I would do it using plain vue.
Template
<template>
<main>
<div v-for="option in options" :key="option.id">
<input
type="text"
v-model="option.userAnswer"
#input="handleAnswer(option)"
/>
</div>
</main>
Data
data() {
return {
options: [
{
id: 1,
question: "question 1",
userAnswer: "",
rightAnswer: "hello 1",
isAnswerCorrect: false,
},
{
id: 2,
question: "question 2",
userAnswer: "",
rightAnswer: "hello 2",
isAnswerCorrect: false,
},
{
id: 3,
question: "question3",
userAnswer: "",
rightAnswer: "hello3",
isAnswerCorrect: false,
},
],
};
},
Methods
methods: {
handleAnswer(option) {
if (option.userAnswer === option.rightAnswer) {
console.log("right");
option.isAnswerCorrect = true;
} else {
console.log("error");
option.isAnswerCorrect = false;
}
},
},
I hope it helps!
Hi I am building an app and part of that app is to search for creators according to artistName. When the listing page loads first time it shows the list of all the creators.
But there is an input field when the list can be filtered according to artist name and although I can see that it returns the correct search result, the page doesn't refresh and the full list is still showing.
And in the javascript console i get this error:
TypeError: Cannot read properties of null (reading 'id')
whereas the console.log in the method gives me the correct value.
Here it the input field
<input
type="text"
placeholder="Search by artistName"
v-model="artistName"
/>
<button
#click="searchArtistName"
>
Search
</button>
And this is the function that filters the results
data() {
return {
creators: [],
currentCreator: null,
currentIndex: -1,
artistName: "",
errors: [],
};
},
methods: {
searchArtistName() {
DataService.findByArtistName(this.artistName)
.then((response) => {
this.creators = response.data;
console.log(this.creators);
})
.catch((e) => {
console.log(e);
});
},
},
And this is what draws the list
<ul>
<li
:class="{ active: index == currentIndex }"
v-for="(item, index) in creators"
:key="index"
#click="setActiveCreator(item, index)"
>
id: {{ item.id }}, {{ item.artistName }}
</li>
</ul>
Can anyone please tell me why this is not working?
Check your response, and items
You have an item with value of null (like creator100: null),
And when you iterate through creators you get and item = null and when trying to access item.id you get this error TypeError: Cannot read properties of null (reading 'id')
use id: {{ item ? item.id : index }}, {{ item ? item.artistName : '' }} to null free in rendering and know when you get null value.
You can try null.id in Browser console to achieve a error like that.
if you wanna look deeper, expand error log in console, and attach an screenshot to question.
OK I found the answer. The problem was with the data format from the api. It was returning an object, not an array hence it could not be assigned to creators array correctly.
I changed it into an Iterable object and now the list filters correctly.
I have a vue component that adds a search bar and search bar functionality. It contains this line:
<input class="input" type="text" placeholder="Address" v-model="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
This captures the text in the search bar and emits it.
In my vue, this triggers my updateSearchQuery function:
this.searchQuery = event.data which merely saves the users input in the searchQuery property in my vue. Everything works fine when I do this, until, I make a search and then, make another call using the same this.searchQuery data.
For example, I'm trying to filter results with the search query '956'. I enter it and this call is made: GET /users?cp=1&pp=20&se=956, just like it should. Then after the page loads, if I go to page 2 of the results, this is the call that is made to the server: GET /users?cp=2&pp=20&se=6. Instead of saving 956 as the queryStr in the the view, it only saves the most recent character entered, instead of the entire content of the serch text.
This happens every time I type in multiple characters as a search query, and then make another call to the server using the unchanged this.searchQuery variable. If my initial search query is only a single character, it works just fine.
What am I doing wrong here? How can I emit the entirety of the text in the search bar, after any change, so that I can always save the whole search query, instead of the just the most recent change?
EDIT: I've add some more code below so the data flow is easier to follow:
Here is the template and script for the search component:
<template>
<div class="level-item">
<div class="field has-addons">
<div class="control">
<input class="input" type="text" placeholder="Address" v-model.lazy="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
</div>
<div class="control">
<div class="button is-light" #click="clearInput">
<span class="icon is-small">
<i class="fa fa-times" style="color:#ffaaaa"></i>
</span>
</div>
</div>
<div class="control">
<button class="button is-info" #click="onSearch(searchQuery)">Search</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Search',
props: {onSearch: Function},
data () {
return {
searchQuery: ''
}
},
watch: {},
methods: {
clearInput () {
this.searchQuery = ''
}
}
}
</script>
the emitted queryChange event is caught and listened to in the vue page:
<Search :onSearch="onSearch" v-on:queryChange="updateSearchQuery"> and this triggers the updateSearchQuery function:
updateSearchQuery (event) {
this.searchQuery = event.data
console.log(event.data + ' || event.data')
console.log(this.searchQuery + ' || this.searchQuery')
}
Theoretically, the searchQuery data in my vue should be a copy of the searchQuery data in my component, which is itself merely a copy of whatever the user has input in the search bar.
Then when I make a call to the server I'm using the value in this.searchQuery in my vue:
onSearch (search) {
this.makeServerQuery(1, search)
},
onPaginate (page) {
this.makeServerQuery(page, this.searchQuery)
},
makeServerQuery (page = null, search = null) {
let queryStr = ''
if (page !== null) {
queryStr += '?cp=' + page + '&pp=' + this.perPage
}
if (this.searchQuery !== '') {
queryStr += '&se=' + this.searchQuery
} .....
The on onSearch(search) function is called whenever the search button is pressed. That seems to work fine, because when the button is pressed the entire searchQuery is passed, not just the last change.
An input event's data value appears to be the last typed character, and not the current value of the input. A simple fix is:
#input="$emit('queryChange', searchQuery)"
This works because the model will always be updated before the input event handler runs.
Here's a complete working component example:
<input
v-model="searchQuery"
type="text"
placeholder="Address"
#input="onInput"
/>
export default {
data() {
return { searchQuery: '' };
},
methods: {
onInput() {
console.log(this.searchQuery);
this.$emit('queryChange', this.searchQuery);
},
},
};
I tried to stay away from the Vue components of Spark as much as possible but I discovered I had to implement a certain mail settings so I can't hold it much longer.
Luckily the Spark documentation contains a small cookbook for adding profile fields:
https://spark.laravel.com/docs/4.0/adding-profile-fields
Most parts are within my (limited PHP) comfort zone:
First the blade php:
Mail settings
<div class="col-md-6">
<label class="radio-inline"><input type="radio" value="profile" v-model="form.type" name="profile">Profile</label>
<label class="radio-inline"><input type="radio" value="website" v-model="form.type" name="website">Website</label>
<label class="radio-inline"><input type="radio" value="combined" v-model="form.type" name="combined">Combined</label>
<span class="help-block" v-show="form.errors.has('mail-settings')">
#{{ form.errors.get('mail-settings') }}
</span>
</div>
</div>
Which is integrated:
<!-- Update Mail settings -->
#include('settings.profile.update-mail-settings')
So as can be seen in the previous code block, I wish to store the result of 3 radio buttons.
However the linked Vue js file is giving my headaches:
Vue.component('update-mail-settings', {
props: ['user'],
data() {
return {
form: new SparkForm({
profile: ''
website: ''
combined: ''
})
};
},
mounted() {
this.form.mailsettings = this.user.mailsettings;
},
methods: {
update() {
Spark.put('/settings/profile/mail-settings', this.form)
.then(response => {
Bus.$emit('updateUser');
});
}
}
});
But how on earth do I integrate the radio buttons in the SparkForm?
In Vue, data binding occurs when you v-model to the object by name. Or in other words, you call v-model="object.property" on an input. When the user fills out the form, the value of form.type will match the form input. So simply change your form object to read:
data() {
return {
form: new SparkForm({
type: '' <- this can now be v-modeled to "form.type"
})
};
},
Your radio buttons don't need to change because they are bound correctly: v-model="form.type"
https://v2.vuejs.org/v2/guide/forms.html#Radio