Vuejs search doesn't refresh list - vue.js

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.

Related

[Vue warn]: Error in render: "TypeError: _vm.stepProgression is not a function"

I'm creating a stepper component with a v-for loop, which currently works without an issues:
<li
v-for="(step, index) in stepper"
:key="step.id"
class="goals-modal__step"
:class="[{'is--selected': index === activeSlide }, {'is--visited': activeSlide > index}]"
>
{{ step.stage }}
</li>
Data object:
data: () => ({
activeSlide: 0,
}
This is working as expected.
However, when I try to pass the argument(index) from the v-for loop to a computed method so that I can return the class bindings for dynamic class logic (this will become more complex), I get an error: "[Vue warn]: Error in render: "TypeError: _vm.getClasses is not a function".
Updated code:
<li
v-for="(step, index) in stepper"
:key="step.id"
:class="stepProgression(index)"
>
{{ step.stage }}
</li>
And this is the computed method:
stepProgression(index) {
return {
'is--selected': index === this.activeSlide,
'is--visited': this.activeSlide > index
}
}
Does someone know what the issue us here? Any help much appreciated :)
Computed properties cannot receive arguments, so all you need to fix you code is move your property(function/method actually) to methods section like so:
methods: {
stepProgression(index) {
return {
'is--selected': index === this.activeSlide,
'is--visited': this.activeSlide > index
}
},
}

Display computed results in child component in VueJS

I pull data in from an Axios request. I have approx 500 records each with several results. I have simplified it for ease.
I display the data on the main component as a drop-down menu and it displays one entry for 'John' and one for 'Jane'. It splits the JSON data and removes duplicates and this works fine.
What Im stuck with is how to display the same data in the child component. At the moment it just displays 'Jane,Jane,Jane,John,John,John,Jane,'.
Do I have to do another methods in the child component for this? I can pass in a prop with uniquenames but it just displays them all in each result.
Any help would be most appreciated.
JSON
"results": [
"result": {
"name": "Jane,Jane,John,John,John,John,Jane,Jane,"
}
"result": {
"name": "Jane,Jane,Jane,John,John,John,Jane,"
}
"result": {
"name": "John,Jane,"
}
"result": {
"name": "Jane"
}
]
Main component
<Result
v-for="result in Results"
:result="result"
:key="result.docNum"
/>
<ul v-for="name in uniquenames" :key="name">
<li>
<label class="pr-2" for="name">{{ name }}</label>
<input
type="checkbox"
v-model="names"
:value="name"
/>
</li>
</ul>
methods: {
const metanames = this.Results
.filter((result) => results.result && results.result.name)
.map((item) => item.name)
.filter((names, i, arr) => arr.indexOf(names) === i)
let names = []
metanames.forEach((item) => {
const splitArr = item.split(', ')
names = names.concat(splitArr)
})
this.uniquenames = names.filter(
(names, i, arr) => arr.indexOf(names) === i,
)
}
Child component
<template>
<div>
{{ name }}
</div>
</template>
<script>
export default {
name: "result",
props: {
result: Object,
},
data: function () {
return {
name: this.results.result.name
}
}
Your <Result> component in the Parent template is passing in the items of Result in a loop, but you have not modified Result, hence why the original non-unique array of names is passed in. Create and pass down a computed property instead which should be a modified version of Result in the format you want. A computed property can update/reformat automatically whenever its dependencies (the original Result) changes. Whenever the computed property updates so will the prop
<Result
v-for="result in modifiedResults"
:result="result"
:key="result.docNum"
/>
computed: {
modifiedResults() {
// unfinished code for returning modified Results containing array of unique names
return [...this.Results].map(r => {
let uniquenames = getUniqueNames(r.names)
return { ...r, names: uniquenames }
})
},
},
as the comment says you'll need to finish/change the computed property to return exactly what you want. I can't do it as I'm not completely sure how all the different arrays and object properties in your code fit together, but computed property is definitely the way to go!

Check input filed that is looped over and set status for each element accordingly

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!

Vue not updating v-for info array

I am using v-model="item1" in a form which calls a method AddItem().
<form #submit.prevent="AddItem">
<input type="text" name="items" v-model="item1">
...
The method AddItem sends the input data to the database and also pushes to an array called info[].
This array is defined in data(){...}.
data () {
return {
info: [],
item1: null
}
},
The method,
AddItem(){
db.collection('things').add({
item1: this.item1,
}).then(() => {
this.info.push(this.item1)
}).catch(err => {
console.log(err);
})
}
But the info is also used in a v-for in next in the template just after this form. I thought if the info array was updated that any use of it in the template would also update. But after I successfully send this data to the db and update info, the new item is not visible in v-for iteration in the template.
<li v-for="(item, index) in info" :key="index">
<span class="item">{{ item.item1 }}</span>
</li>
How can I get this for loop to take account of the updated info array?
I am new to vue but guess that maybe I should be using data binding some way.
Solved
I was pushing the value to the info array instead of pushing an object like this,
this.info.push({item1: this.item1})
Try
<li v-for="(item, index) in info" :key="index">
<span class="item">{{ item }}</span>
</li>
The value that you push into the array is a string instead of an object (this.item1).
Also to make it more simple try to only do this in AddItem(). Then you are sure its not a faulth with your backend.
AddItem(){
this.info.push(this.item1)
}
Otherwise your code looks correct. In the future it is always handy to create a fiddle so its easier for others to help you.

VUEJS remove Element From Lists?

it is possible to remove specific element from lists. i tried this functions for remove element
pop() = remove last element
$remove(index) = not remove any element from lists
remove( index ) = undefined function
unshift( index ) = add new and empty element
splice( index ) = remove all element from index
please help me to remove specific element from lists.
below is my js code
var example2 = new Vue({
el: '#example-2',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' },
{ message: 'Bar1' },
{ message: 'Bar2' },
{ message: 'Bar3' },
{ message: 'Bar4' }
]
},
methods : {
removeElement : function(index){
this.items.$remove(index);
}
}
})
below is my HTML code
<ul id="example-1">
<li v-for="(key, item) in items">
{{ item.message }}
<button v-on:click="removeElement(key)">remove</button>
</li>
</ul>
$remove is deprecated in Vue.js 2.0 and replaced by splice as stated in the docs. Make sure you add the 2nd parameter of splice for it to work.
Migration From Vue 1.x - 2.0
methods: {
removeElement: function (index) {
this.items.splice(index, 1);
}
}
You can use Vue.delete if your Vue version is 2.2.0+
Vue.delete(this.items, index);
The $.remove feature has been replaced with $.delete.
You can call it like so:
this.$delete(this.someItems, itemIndex).
It works on Object as well as Array. With objects, you need to use a keyed object. With arrays, you pass in the index of the item you want to delete.
Here is a fiddle example: https://jsfiddle.net/james2doyle/386w72nn/
EDIT
I added an example for an array as well
$delete can use inline in #click:
<ul id="example">
<li v-for="(item, key) in items">
{{ item.message }}
<button #click="$delete(items, key)">remove</button>
</li>
</ul>
https://v2.vuejs.org/v2/api/#vm-delete
Firstly, you should fix the methods key.
Then, you should pass the item to the $remove method, not the index. [ref]
https://jsfiddle.net/790og9w6/
If anyone wonders: $delete has been removed in VueJS 3 ( https://v3-migration.vuejs.org/breaking-changes/introduction.html#removed-apis ). Now it is possible to use javascripts native splice(index, count of objects to be deleted).