Is it possible from the items array to create a variable item.id (in props)?
props: {
items: {
type: Array,
default: () => []
}
},
And layout, just in case
<div class = 'bestsellers-item' v-for = '(item, index) in items': key = 'index'>
<div class = 'bestsellers-item__sticker'>
<img: src = 'item.img'>
</div>
<span> {{item.id}}
</div>
Based on the comments and also Michal LevĂ˝'s comment, I think what you are looking for is this:
<div class = 'bestsellers-item' v-for = '(item, index) in items': key = 'index'>
<div class = 'bestsellers-item__sticker'>
<img: src = 'item.img'>
</div>
<button #click="addCartItem(item.id)">Add to cart</button>
</div>
And then, your function:
addCartItem(itemId) {
console.log("Do something with item " + itemId);
}
Related
I have this Pinia store:
import { defineStore } from 'pinia'
import axiosClient from '#/axios'
export const useTableOrderStore = defineStore( 'tableOrders', {
id : 'tableOrders',
state: () => {
return {
tableOrders: []
}
},
actions: {
addToOrder(item, quantity)
{
const searchIndex = this.tableOrders.findIndex((order) => order.id == item.id);
if(searchIndex !== -1)
{
this.tableOrders[searchIndex].quantity += quantity
}
else
{
item.quantity = quantity
this.tableOrders.push(item)
}
}
}
})
Parent component:
<script setup>
import {ref} from "vue"
import {useTableOrderStore} from "#/store/tableOrder";
import CounterInput from "#/components/Inputs/Counter.vue"
const tableOrderStore = useTableOrderStore()
const props = defineProps(['product'])
let quantity = ref(1)
let addToOrder = (product) => {
tableOrderStore.addToOrder(product, quantity.value)
quantity.value = 1
}
</script>
<template>
<div class="input-group">
<counter-input :quantity="quantity"
#quantity-event="(n) => quantity = n"></counter-input>
<button class="btn btn-primary btn-sm ms-1" #click="addToOrder(product)">
Add <font-awesome-icon icon="fas fa-receipt" class="ms-2" />
</button>
</div>
</template>
Child component:
<script setup>
import {ref} from "vue"
let props = defineProps({
quantity: {
type: Number,
required: true
}
})
let count = ref(props.quantity)
let increase = () => {
count.value++
emit('quantityEvent', count.value)
}
let decrease = () =>
{
if(count.value === 1)
return
count.value--
emit('quantityEvent', count.value)
}
const emit = defineEmits(['quantityEvent'])
</script>
<template>
<span class="input-group-btn">
<button type="button"
class="btn btn-danger btn-number"
data-type="minus"
#click="decrease"
>
<font-awesome-icon icon="fas fa-minus-circle" />
</button>
</span>
<input type="text"
name="quantity"
:value="count"
class="form-control input-number"
min="1"
>
<span class="input-group-btn">
<button type="button"
class="btn btn-success btn-number"
data-type="plus"
#click="increase"
>
<font-awesome-icon icon="fas fa-plus-circle" />
</button>
</span>
</template>
The first time method addToOrder is fired, the product is correctly added and child product renders is.
The first issue here is that the quantity is set to 1, but it is not set in the child component.
The second problem is with the quantity - first addToOrder is ok, and quantity is shown correctly, but if new quantity is added the Pinia store is updated, but it is not reflected in the component. What am I doing wrong here?
I guess you run into an vuejs caveat.
this.tableOrders[searchIndex].quantity += quantity
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue When you modify the length of the
array, e.g. vm.items.length = newLength
You directly set an item.
Instead, you could use .splice() to replace your item:
let newItem = {
...this.tableOrders[searchIndex],
quantity: this.tableOrders[searchIndex].quantity + quantity
};
//replace old item with new
this.tableOrders.splice(searchIndex, 1, newItem)
Here are the mutation methods that triggers an update:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
The first time you use addToOrders works because you used .push() witch is one of the mutations that triggers the re-render.
First issue, i dont know how props.quantity is not set in the child component. Try console props.quantity right after you defined props if it displayed the result as you want then try to console it inside watch method. If props.quantity has changed but your child component is not update then your child component somehow not updated. You can try to force update it and here's how: https://medium.com/emblatech/ways-to-force-vue-to-re-render-a-component-df866fbacf47 (Using the key changing technique)
Second issue, i think this one #quantity-event="(n) => quantity = n" should be #quantity-event="(n) => { quantity = n }"
I have a code like
<template>
<div v-for="(item, i) in items" :key="i">
<div v-for="(subitem, index) in subitems" :key="index" v-
if="item.id==subitem.item_id">
{{count(subitem)}}
</div>
</div>
</template>
<script>
data() {
return {
items: [],
subitems: [],
}
}
watch: {
items : async function() {
const itm = await('get','/item')
this.items = itm
},
subitems : async function() {
const subitm = await('get','/sub_items')
this.subitems = subitm
},
},
</script>
The items and subitems array get the values of array from the api fetch in watch property. There is item id in each subitems values, Now I want count or length of for each v if output of subitem which is equal to item id. How to do that?
Thank you
You can't put v-for and v-if on the same element can you please provide more about your array structure so i can give you the best way but from what i have you can create do this function after getting item and subitem
items.foreach((element)=>{
element.subitemCount=0;
subitem.foreach(item=>{
if(element.id==item.item_id){
element.subitemCount++;}}
And for your vuejs you can write this
<div v-for="(item, i) in items" :key="i"> {{item.subitemCount}} </div>
I am trying to replace the text "this | " with "" from the titles in an array.
What is the best way to do this?
Any help would be greatly appreciated :)
my js code:
let Feed=require('rss-to-json')
Feed.load('http://localhost:3000/news', function (err, content) {
let appTitleList = new Vue({
el: '#news',
data: {
rssItems: content.items
},
methods:{
removeFunction: function () {
this.content.title = this.content.title.replace("this | ", "");
}
})
})
the html:
<div class="card" id="news">
<ul class="list-group list-group-flush">
<li class="list-group-item" v-for="item in rssItems" >
<b>{{ item.title }}</b>
<p>{{ item.description }}</p>
</li>
</ul>
</div>
I don't see what this.content is. I don't see where you are using removeFunction, but if you are, try this:
removeFunction: function () {
const rssItems = [...this.rssItems]
for (const item of rssItems) {
item.title = item.title.replace("this | ", "");
}
this.rssItems = rssItems
}
Alternatively, mutate the rssItems before setting them in the state, and maybe you won't need the removeFunction.
data: {
rssItems: content.items.map(i => ({
...i,
title: i.title.replace("this | ", "")
}))
}
This can be a possible solution: fetching your API's posts when the Vue.JS instance is created, mutating the related titles and enqueue each post.
<head>
... your imports from CDN
</head>
<body>
<div id="app">
<div class="card" id="news">
<ul class="list-group list-group-flush">
<li class="list-group-item" v-for="item in items">
<b>{{ item.title }}</b>
<p>{{ item.description }}</p>
</li>
</ul>
</div>
</div>
<script>
new Vue({
el: '#app',
data () {
return {
items: []
}
},
created () {
Feed.load('your-api-endpoint', (err, content) => {
// TODO: error handling...
for (let i = 0; i < content.items.length; i++) {
const item = content.items[i];
// Mutate the title and enqueue the post
item.title = item.title.replace('Title | ', '');
this.items.push(item);
}
});
}
})
</script>
</body>
Also, watch out that the field data in the Vue.JS instance must be a function, not an object. More about this, here.
I am trying to create a dynamic v-model input. All seems well except for the following.
The on click event that triggers the checkAnswers method only works if you click out of the input then click back into the input and then press the button again. It should trigger when the button is pressed the first time.
Does anyone have any ideas? Thanks in advance.
<template>
<div class="addition container">
<article class="tile is-child box">
<div class="questions">
<ul v-for="n in 5">
<li>
<p>{{ randomNumberA[n] }} + {{ randomNumberB[n] }} = </p>
<input class="input" type="text" maxlength="8" v-model.number="userAnswer[n]">
<p>{{ outcome[n] }}</p>
</li>
</ul>
</div>
<div class="button-container">
<button #click="checkAnswers" class="button">Submit Answer</button>
</div>
</article>
</div>
</template>
<script>
export default {
data() {
return {
randomNumberA: [] = Array.from({length: 40}, () => Math.floor(Math.random() * 10)),
randomNumberB: [] = Array.from({length: 40}, () => Math.floor(Math.random() * 10)),
userAnswer: [],
outcome: [],
}
},
methods: {
checkAnswers() {
for (var i = 0; i < 6; i++) {
if (this.userAnswer[i] === (this.randomNumberA[i] + this.randomNumberB[i])) {
this.outcome[i] = 'Correct';
} else {
this.outcome[i] = 'Incorrect';
}
}
}
}
}
</script>
You have some basic issues with your use of the template syntax here. According to the vue docs:
One restriction is that each binding can only contain one single
expression, so the following will NOT work: {{ var a = 1 }}
If you want to populate your arrays with random numbers you would be better calling a function on page mount. Something like this:
mounted() {
this.fillArrays()
},
methods: {
fillArrays() {
for (let i = 0; i < 5; i++) {
this.randomNumberA.push(Math.floor(Math.random() * 10))
this.randomNumberB.push(Math.floor(Math.random() * 10))
this.answer.push(this.randomNumberA[i] + this.randomNumberB[i])
}
}
}
Then you can use template syntax to display your arrays.
It looks like you are setting up a challenge for the user to compare answers so I think you'd be better to have a function called on input: Something like:
<input type="whatever" v-model="givenAnswer[n-1]"> <button #click="processAnswer(givenAnswer[n-1])>Submit</button>
Then have a function to compare answers.
Edit
I have basically rewritten your whole page. Basically you should be using array.push() to insert elements into an array. If you look at this you'll see the randomNumber and answer arrays are populated on page mount, the userAnswer array as it is entered and then the outcome on button click.
<template>
<div>
<div >
<ul v-for="n in 5">
<li>
<p>{{ randomNumberA[n-1] }} + {{ randomNumberB[n-1] }} = </p>
<input class="input" type="text" maxlength="8" v-model.number="userAnswer[n-1]">
<p>{{ outcome[n-1] }}</p>
</li>
</ul>
</div>
<div class="button-container">
<button #click="checkAnswers" class="button">Submit Answers</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
randomNumberA: [],
randomNumberB: [],
answer: [],
userAnswer: [],
outcome: [],
}
},
mounted() {
this.fillArrays()
},
methods: {
checkAnswers() {
this.outcome.length = 0
for (var i = 0; i < 6; i++) {
if (this.userAnswer[i] === this.answer[i]) {
this.outcome.push('Correct');
} else {
this.outcome.push('Incorrect');
}
}
},
fillArrays() {
for (let i = 0; i < 5; i++) {
this.randomNumberA.push(Math.floor(Math.random() * 10))
this.randomNumberB.push(Math.floor(Math.random() * 10))
this.answer.push(this.randomNumberA[i] + this.randomNumberB[i])
}
}
}
}
</script>
I am attempting to create a search function in my Vue.js 2 application. However, even though my algorithm is giving me the right results, I am not getting the proper filter. Right now, whenever I run a search, I get nothing on the page. Here is my code:
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){console.log("true")} else {console.log("false")}
if(sorted.includes(item.toLowerCase())){return true}
})
} else {
return true
}
})
}
}
Here is my relevant HTML:
<div class="container">
<label for="search">Search: </label>
<input type="text" name="search" v-model="search">
</div>
<div class="index container">
<div class="card" v-for="smoothie in filteredSmoothies" :key="smoothie.id">
<div class="card-content">
<i class="material-icons delete" #click="deleteSmoothie(smoothie.id)">delete</i>
<h2 class="indigo-text">{{ smoothie.title }}</h2>
<p class="indigo-text">{{ smoothie.description }}</p>
<ul class="ingredients">
<li v-for="(ing, index) in smoothie.category" :key="index">
<span class="chip">{{ ing }}</span>
</li>
</ul>
</div>
<span class="btn-floating btn-large halfway-fab pink">
<router-link :to="{ name: 'EditSmoothie', params: {smoothie_slug: smoothie.slug}}">
<i class="material-icons edit">edit</i>
</router-link>
</span>
</div>
</div>
As the discussions in the comments, the root cause should be:
you didn't return true/false apparently inside if(this.search != null){}, it causes return undefined defaultly.
So my opinion is use Javascript Array.every or Array.some. Also you can use for loop + break to implement the goal.
Below is one simple demo for Array.every.
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
return !indivthings.every(item => { // if false, means item exists in sorted
if(sorted.includes(item.toLowerCase())){return false} else {return true}
})
} else {
return true
}
})
}
or you can still use forEach, the codes will be like:
let result = false
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){result = true}
})
return result
filteredSmoothies is not returning a list. You iterate over to see if indivthings is contained within sorted, but you don't do anything with that information. I think you need to modify your sorted list based on the results of indivthings.