Nuxt Computed property "itemCost" was assigned to but it has no setter - vue.js

I have a method like this:
methods: {
itemCost(item) {
let total = 0;
for(let choice of item.choices) {
if(choice.cost) total += choice.cost;
}
return total
}
}
And my vue file looks like this:
index.vue
<div class="container">
<div class="item" v-for="item in cart.items">
{{itemCost(item)}}
</div>
</div>
It's working great, however I get this warning I don't understand:
Computed property "itemCost" was assigned to but it has no setter.
It's a method, not a computed property so not sure why i'm getting the warning.
I looked at similar questions to this but all the answers mentioned that it was because it was a v-model so it needed a setter, in this case i'm trying to output the calculation so no user input is necessary.
Any ideas how to get rid of the warning?

It because of the weird construction of your component. You should do the calculation before, inside a computed, and then use it to print out:
computed: {
itemsWithCost: function() {
return this.cart.items.map( item => {
let total = 0;
for(let choice of item.choices) {
if(choice.cost) total += choice.cost;
}
item.total = total;
return item;
}
})
}
and in your template:
index.vue
<div class="container">
<div class="item"
v-for="(item,index) in itemsWithCost"
:key="index">
{{item.total}}
</div>
</div>

Related

Why v-model is not updating in text

<template>
<div class="card-deck mb-3 text-center">
<div class="card mb-3 box-shadow">
<div class="card-header">
Numbers Checked
</div>
<div class="card-body card-info color-accent" v-model="numbers_checked" v-text="numbers_checked">
</div>
</div>
</div>
</template>
<script>
export default {
props:
[
'overviewAnalytics',
],
data() {
return {
numbers_checked: this.overviewAnalytics.numbers_checked
};
},
created() {
this.channelTemperatureReading.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(resp => {
this.numbers_checked = 12; //resp.data.numbers_checked + 100;
});
});
},
computed: {
channelTemperatureReading() {
return window.Echo.channel('temperature-reading');
},
},
};
</script>
I've tried everything but text is not updating. Confirmed from every aspect that data does change.
Changes from AXIOS are coming just ok. I even tried to put custom value but no avail.
I don't what is issue here.
v-model only works on input, textarea, and select elements
You appear to be misusing computed properties which rely on reactive dependencies to execute however yours is wrapping window.Echo.channel('temperature-reading') which Vue knows nothing about.
I suggest you remove the computed property and use something like this
created() {
const channel = window.Echo.channel('temperature-reading')
channel.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(({ data }) => {
// console.log('get-overview-analytics', data.numbers_checked)
this.numbers_checked = data.numbers_checked + 100
})
})
}
As others have mentioned, v-model is not appropriate here so you should also remove that.
Don't use v-model with div, it's for inputs.
<div v-text="numbers_checked"></div>
From the documentation on v-model:
Usage: Create a two-way binding on a form input element or a
component.

Vue JS Using local variable without defining in data() function

I am a beginner to Vue JS. I have to use a variable inside a component whose value changes often.
So when I declare and define it under data() the following warn is coming in Chrome console
Since when there is a change in data() variables automatically Vue framework calls render function.
Is there any way to declare and use a variable other than declaring it in data() method ??
<template>
<ul>
<div v-for="(list,index) in itemlist" :key="index">
<div v-if="!isFirstCharSame(list.label)" >{{ firstChar }} </div>
<li>
<span>{{ list.label }}</span>
</li>
</div>
</ul>
</template>
<script>
export default {
data() {
return {
itemlist: [
{"label":"Alpha"},
{"label":"Beta"},
{"label":"Charlie"},
{"label":"Delta"}],
firstChar:"$"
}
},
methods : {
isFirstCharSame: function(str) {
if(str.startsWith(this.firstChar)) {
return true;
}
this.firstChar = str.charAt(0);
return false;
}
}
}
</script>
Expected output should be like this
Inside Group A It should display all the elements starting with A
Below we will render using a computed property to make sure its sorted alphabetically and then render your first char. Though You should be using grouping imo.
<template>
<ul>
<div v-for="(list, index) in sortedlist" :key="`people_${index}`">
<div v-if="!isFirstCharSame(list.label)" >{{ firstChar }} </div>
<li>
<span>{{ list.label }}</span>
</li>
</div>
</ul>
</template>
<script>
export default {
data() {
return {
itemlist: [
{"label":"Alpha"},
{"label":"Beta"},
{"label":"Charlie"},
{"label":"Delta"},
],
firstChar: '',
};
},
methods: {
isFirstCharSame(char) {
if (str.startsWith(this.firstChar)) {
return true;
}
this.firstChar = str.charAt(0);
return false;
},
},
computed: {
sortedList() {
return this.itemList.sort((a, b) => {
if (a.label > b.label) {
return 1;
}
if (b.label > a.label) {
return -1;
}
return 0;
});
},
},
};
</script>
And yes, You can update your data any time you wish and the component will do a re render to reflect it.
You can declare variables in your component within your methods or inside computed properties, etc., but they won't be reachable from the template or the rest of the code nor they would be reactive.
The only way for them to be reactive and reachable from the higher scope is adding the data property to the component in the following way:
data: function () {
return {
foo: 'bar'
}
},
or
data () {
return {
foo: 'bar'
}
},
Besides this, the reason of your error is that you are mutating the state of your variables inside the render. When this happens, Vue re-renders the template because the values have mutated and calls again to the function and voilĂ : there you have an infinite loop.
You should probably check the function you are calling and try to replace the changing variables from the data property with local variables that take their data from the actual data variables.

how to render array stored in vuex

I am trying to render array of objects stored in store in the form of cards. But, I am not able to. Since it shows typeError.
It states
"error in render: "TypeError: Cannot read property 'item' of undefined"
I tried using this keyword and shifting the code to mounted() hook.
But, the error keeps on showing.
Here is the code:
CardRenderer.vue:
<template lang="html">
<div>
<b-container class="bv-example-row">
<b-row v-for="(row, i) in this. rows" v-bind:key="i">
<b-col v-for="(item, j) in row" v-bind:key="j" >
<!-- you card -->
<b-card
:title="item.title"
img-src="item.icon"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2"
>
<b-card-text>
<h1>{{item.name}}</h1>
<pre>{{item.description}}</pre>
</b-card-text>
<b-button :href="'/dashboard/'+this.item.name" variant="primary">More</b-button>
</b-card>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script lang="js">
export default {
name: 'CardRenderer',
props: {
},
data() {
return {
// rows: []
}
},
mounted() {
},
methods: {
},
computed: {
rows() {
const itemsPerRow = 3
var rows = []
let arr = this.$store.getters.responseAPI.apps
// eslint-disable-next-line
console.log(arr)
for (let i = 0; i < arr.length; i += itemsPerRow){
let row = []
for (let z = 0; z < itemsPerRow; z++) {
row.push(arr[z])
}
rows.push(row)
}
// eslint-disable-next-line
// console.log(this.rows)
return rows[0]
}
}
}
</script>
<style scoped>
</style>
This is how the error looks like.
and this is how the rows object looks like
How do i remove the error and the render the card.
I would love to have the changed code as the answer.
Thanks :)
I think you have a mistake at the computed property return statement.
Try replacing return rows[0] to return rows to return an array instead of the first item :)
I've finally caught an error))
This is my code sample at sandbox: https://codesandbox.io/embed/vue-template-sm0yx
You've got a mistake at the template, just remove this from :href="'/dashboard/'+this.item.name" to make it look like this: :href="'/dashboard/'+item.name"
That should work!))

Conditionally rendering icons only one time depending on property

I am trying to write a template that displays either Icon AAA or Icon BBB, depending on whether or not the item in the current iteration has a specific flag. Here is my code:
<div v-for="(item, itemIndex) in items">
<div v-if="item.hasUnreadComments">
<span>Display Icon AAA</span>
</div>
<div v-else>
<span>Display Icon BBB</span>
</div>
</div>
The issue here is that I need either icon displayed ONCE. If more than one item has it set to item.hasUnreadComments === true, Icon AAA will be displayed equally as many times, which is not what I want. Couldnt find anything in the docs and I dont want to bind it to a v-model.
Can this be done in Vue without a third data variable used as a flag?
You will have to do some sort of intermediate transformation. v-if is just a flexible, low-level directive that will hide or show an element based on a condition. It won't be able to deal directly with how you expect the data to come out.
If I'm understanding what you're asking for, you want an icon to only be visible once ever in a list. You can prefilter and use an extra "else" condition. This sounds like a use case for computed properties. You define a function that can provide a transformed version of data when you need it.
This example could be improved upon by finding a way to boil down the nested if/elses but I think this covers your use case right now:
const app = new Vue({
el: "#app",
data() {
return {
items: [{
hasUnreadComments: true
},
{
hasUnreadComments: true
},
{
hasUnreadComments: false
},
{
hasUnreadComments: true
},
{
hasUnreadComments: false
},
]
}
},
computed: {
filteredItems() {
let firstIconSeen = false;
let secondIconSeen = false;
return this.items.map(item => {
if (item.hasUnreadComments) {
if (!firstIconSeen) {
item.firstA = true;
firstIconSeen = true;
}
} else {
if (!secondIconSeen) {
item.firstB = true;
secondIconSeen = true;
}
secondIconSeen = true;
}
return item;
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, itemIndex) in filteredItems">
<div v-if="item.firstA">
<span>Display Icon AAA</span>
</div>
<div v-else-if="item.firstB">
<span>Display Icon BBB</span>
</div>
<div v-else>
<span>Display no icon</span>
</div>
</div>
</div>

v-if doesn't recognize value from array of object

I'm working on a vueJS file and have a component :
<div class="panel-heading" v-on:click="displayValue(feature.id)">
{{ feature.nom }}
</div>
<div class="panel-body" v-if="getDisplay(feature.id)">
foo
</div>
my function displayValue(id) :
displayValue(id){
for(let i =0; i<this.product_site.displayFeatures.length;i++){
if (this.product_site.displayFeatures[i].idFeature === id){
this.product_site.displayFeatures[i].show = !this.product_site.displayFeatures[i].show
}
}
console.log(this.product_site.displayFeatures)
},
First I am not very happy with that. I trie to do a .find but it didn't work :
this.product_site.displayFeatures.find(function(a){
a.idFeature === id
}).show = true
But I had an error telling me can not read 'show' of undefined
and my function getDisplay(id)
getDisplay(id){
this.product_site.displayFeatures.forEach(function(a){
if(a.idFeature === id){
return a.show
}
})
}
same as before if I try with a find.
Anyway, I thought it would work with that, but when I do console.log(this.product_sites.displayFeatures) I have the array with the modified value but foo is not shown