Vue Computed Value Filter (script setup) - vue.js

I can't find anything relevant online. The relevant answers online dont use the tag.
Does it even work with script setup?
<template>
<div class="bg-white md:container md:mx-auto w-10">
<ul class="flex flex-wrap p-2 justify-center bg-black text-white">
<li class="p-2">
Filter
</li>
<li class="p-2">
Search
</li>
</ul>
<div class="flex flex-wrap justify-center h-52 content-center">
<div class="flex justify-center">
<div class="mb-3 xl:w-96">
<label
for="exampleSearch2"
class="form-label inline-block mb-2 text-gray-700"
>Search</label
>
<input
v-model="searchValue"
type="search"
class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
id="exampleSearch2"
placeholder="Type query"
/>
<div class="flex flex-wrap">
<div>Test: {{ searchValue }}</div>
</div>
</div>
</div>
</div>
<div class="grid lg:grid-cols-4 md:grid-cols-3 gap-8">
<dogCard v-for="breed in breedsArray" :key="breed.id" :breed="breed" />
</div>
</div>
</template>
<script setup>
import dogCard from "../components/dogCard.vue";
import { ref, onMounted, computed } from "vue";
import searchBox from "./searchBox.vue";
let URL = "https://api.thedogapi.com/v1/breeds";
const searchValue = ref("");
let breedsData = ref([]);
// Fetching API Data
function getBreedsArray() {
fetch(URL)
.then((response) => response.json())
.then((data) => {
breedsData.value = data;
console.log("Data Fetched", breedsData.value);
})
.catch((error) => {
console.error(error);
});
}
onMounted(() => {
getBreedsArray();
});
const breedsArray = computed({
get() {
return breedsData.value;
},
set(val) {
breedsData.value = breedsData.value.filter((breed) => breed.name.includes() == searchValue);
},
});
let isVisible = ref(false);
</script>
Im trying to filter the dogCard through a Searchbox. I just don't know how to do it.
The Data of 'breedsData' should change on when something is entered in the Searchbox.
Thanks in advance, I'm stuck for so long now, an i dont how to fix this.

In the filter() in the computed setter, you do:
(breed) => breed.name.includes() == searchValue
But you probably want to use the value of the ref instead of the ref itself, and put it into includes():
(breed) => breed.name.includes(searchValue.value)
Note that this would permanently remove filtered breeds, as you change the breedsData.value. A better approach might be to just use the computed getter (I don't think the setter works the way you want it to anyway):
const breedsArray = computed(() => searchValue.value ?
breedsData.value.filter( breed => breed.name.includes(searchValue.value)) :
breedsData.value
)
Since searchValue is reactive, a change to it will trigger an update of the breedsArray.

Related

in svelte shows me variable hash in the variables

I have a problem, i am trying to search an element by id in the script but it was not working, when i go to show the values in console, i see that it adds after each variable a random hash, and that is why it does not find the id.
How can I avoid those random hashes?
Svelte
console
I tried different declarations, splits and so on, but it always adds it. Help
<script>
import { page } from '$app/stores';
import { onMount, beforeUpdate } from 'svelte';
import { Line } from 'svelte-chartjs';
import { Chart as ChartJS, Title, Tooltip, Legend, LineElement, LinearScale, PointElement, CategoryScale } from 'chart.js';
ChartJS.register(Title, Tooltip, Legend, LineElement, LinearScale, PointElement, CategoryScale);
export let param = String($page.params.slug);
let ruta_api = `${import.meta.env.VITE_WP_API}posts?slug=${param}`;
let ruta_api_chart = '';
let value = [];
let value_chart = [];
let data_chart = new Array;
let id_chart = '';
onMount(async () => {
await loadData();
});
async function loadData() {
const response = await fetch(ruta_api);
const newData = await response.json();
value = [...value, ...newData];
ifChart();
}
beforeUpdate(async () => {
});
async function ifChart(){
//comprobamos si viene una grafica en el contenido
let posicion_chart = value[0].content.rendered.indexOf('m-chart-container-');
if(posicion_chart >= 0){
console.log(id_chart);
const regex = /(?<=m-chart-container-)(.*?)(?=-)/mg;
console.log(value[0].content.rendered.match(regex)[0]);
id_chart = value[0].content.rendered.match(regex);
//recorremos los ids
id_chart.forEach(function(id) {
getChart(id);
let div_chart = 'm-chart-container-'+id+'-1';
console.log('primero');
console.log({div_chart});
console.log('segundo');
console.log(`${div_chart}`);
console.log('tercero');
console.log(div_chart);
document.getElementById(div_chart).innerHTML = '11111';
})
//<Line data={data_chart} options={{ responsive: true }} />
}
};
export async function getChart(id){
ruta_api_chart = `${import.meta.env.VITE_WP_API}m-chart/` + id;
console.log(ruta_api_chart);
const response_chart = await fetch(ruta_api_chart);
const newData_chart = await response_chart.json();
value_chart = newData_chart['m-chart'];
var datos = value_chart['data'][0][1].filter(n => n).map(Number);
data_chart =
{'labels' : value_chart['data'][0][0].filter(n => n),
'datasets': [{
'label' : 'My First dataset',
'borderColor': 'rgb(205, 130, 158)',
'data' : datos
}]
};
};
</script>
{#each value as article}
<div class="hero" style="background-image: url({article.featured_image_url ? article.featured_image_url : '../img/componentes/blog-img-defecto.png'});">
<div class="hero-overlay bg-opacity-60"></div>
<div class="hero-content text-center text-neutral-content">
<div class="max-w-md">
<h1 class="mb-5 text-5xl font-bold text-secondary">{#html article.title.rendered}</h1>
</div>
</div>
</div>
<div class="w-full mb-32 contenidoblog pt-8">
{#html article.content.rendered}
<div class="flex items-center flex-col my-20">
<button onclick="" class="btn btn-sm border-0 w-40 rounded-full bg-primary font-normal hover:bg-accent text-secondary hover:text-primary transition ease-in-out duration-500 transform hover:scale-105 px-10 mt-4 pr-10 relative">
<span class="absolute left-8">Descargar</span>
<span class="material-symbols-outlined absolute right-8">
download
</span>
</button>
</div>
<!--
<div class="flex items-center flex-col w-full bg-accent h-24">
Bloque post relacionados
</div>
-->
<div class="flex items-center flex-col my-20">
<button onclick="" class="btn btn-sm border-0 w-40 rounded-full bg-accent font-normal hover:bg-primary text-primary hover:text-secondary transition ease-in-out duration-500 transform hover:scale-105 px-10 mt-4 pr-10 relative">
<span class="absolute left-6">Suscribete</span>
<span class="material-symbols-outlined absolute right-6">
add_circle
</span>
</button>
</div>
</div>
{/each}

Vue 3 : Event, emit and props

I have a problem with event, emit and props (and probably some logic too)
I have a component A in which I have a loop with a component B.
In this loop, I have a method to open the modal (which is a component C) but this method is not part of component B.
Like this :
<a>
<!-- MODAL-->
<div v-if="showModal">
<modal-cat #cat="catId = getCatId($event)" #addTx="addTx($event)"></modal-cat>
</div>
<div v-if="transactions.length != 0" class="mx-auto">
<div v-for="tx in transactions" :key="tx">
<div class="mb-2 border border-gray-600 rounded-lg bg-white pt-2 pb-4">
<div class="flex justify-end">
<span
class="inline-flex items-center justify-center h-6 w-6 rounded-full text-lg bg-blue-800 text-white"
#click="showModal = true, txToAdd = tx">
<i class='bx bx-plus'></i>
</span>
</div>
<transaction-data :transaction="tx" :address="walletAddress"></transaction-data>
</div>
</div>
</div>
</a>
In this modal, I fetch some data (in fact, a array of categories) that I also display in a loop.
Like this :
<div class="modal-container">
<div v-for="(categorie) in categories" :key="categorie">
<p #click="$emit('cat', categorie.id)">{{ categorie.name}}</p>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" #click="$emit('addTx', 'ok')">
OK
</button>
</slot>
</div>
</div>
I need some data from my modal in my component A but I also need some data from my component B in my component A (to add a transactions to a category)
I managed to get the data I wanted like this (And I can get it):
const showModal = ref(false);
const txToAdd = ref({});
const catId = ref(0);
function getCatId(event) {
return event
}
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value); <= the value are well displayed in the console.
let data = {
tx: txToAdd.value,
catId: catId.value
}
store.dispatch("categories/addTxToCategories", data);
}
}
But in my store, when I try to get the payload, I can't access to the data and I only get the payload object.
Is there something wrong with my logic ? What am I doing wrong ?
EDIT
I just need to wrap the result in spread operator, like this :
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value);
let data = {
tx: {...txToAdd },
catId: catId.value
}
store.dispatch("categories/addTxToCategories", {...data });
}
}
And in my store, the payload MUST be the second argument :
async addTxToCategories({ commit }, payload) {}

How can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter

vue.js, how can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter. I want the V-if and V-else to activate only after the user has submitted their request. or if routing the results on to a different page would easier id like to go that route also.
<template>
<div class="home">
<section id="whiteClawVideo" class="videoWrapper d-block w-100">
<div class="video-container fluid">
<iframe width="100%" height="600" src="https://www.youtube.com/embed/JORN2hkXLyM?
autoplay=1&loop=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen></iframe>
</div>
</section>
<form #submit.prevent="SearchMovies()" class="search-box">
<input type="text" placeholder="What are you looking for? " v-model="search" />
<input type="submit" value="Search">
</form>
<div class="movies-list" v-if="search !== ''" >
<div class="container">
<div class="row">
<div class="col-3" v-for="movie in movies" :key="movie.imdbID">
<router-link :to="'/movie/'+movie.imdbID" class="movie-link">
<img class="movieImg" height="100%" :src="movie.Poster" alt="Movie Poster" />
<div class="type">{{ movie.Type }}</div>
<div class="detail">
<p class="year">{{movie.Year}}</p>
<h3>{{ movie.Title }}</h3>
<p>{{movie.imdbID}}</p>
</div>
</router-link>
</div>
</div>
</div>
</div>
<div class="container" v-else>
<MovieSection />
<SecondMovieSection />
</div>
</div>
</template>
import { ref } from 'vue';
import env from '#/env.js';
import MovieSection from '#/components/MovieSection.vue';
import SecondMovieSection from '#/components/SecondMovieSection.vue'
export default {
components: {
MovieSection,
SecondMovieSection
},
setup () {
const search = ref("");
const movies = ref([]);
const SearchMovies = () => {
if (search.value !== "") {
fetch(`API_HERE`)
.then(response => response.json())
.then(data => {
console.log(data)
movies.value = data.Search;
})
}
}
return {
search,
movies,
SearchMovies
}
}
}
Well, it closes once you type a single character because search is a model - it updates on every keypress you do within input it's bound to. What you wanna do instead is hide form based on whether you have entries in your movies array or not, so try changing v-if="search !== ''" to v-if="!movies.length"

Bind class item in the loop

i want to bind my button only on the element that i added to the cart, it's working well when i'm not in a loop but in a loop anything happen. i'm not sure if it was the right way to add the index like that in order to bind only the item clicked, if i don't put the index every button on the loop are binded and that's not what i want in my case.
:loading="isLoading[index]"
here the vue :
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading[index]"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
here the data :
data () {
return {
products : [],
isLoading: false,
}
},
here my add to cart method where i change the state of isLoading :
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
You can change your isLoading to an array of booleans, and your addToCart method to also have an index argument.
Data:
return {
// ...
isLoading: []
}
Methods:
addToCart(product, index) {
// ...
}
And on your button, also include index:
#click="addToCart(product, index)"
By changing isLoading to an empty array, I don't think isLoading[index] = true will be reactive since index on isLoading doesn't exist yet. So you would use Vue.set in your addToCart(product, index) such as:
this.$set(this.isLoading, index, true)
This will ensure that changes being made to isLoading will be reactive.
Hope this works for you.
add on data productsLoading: []
on add to cart click, add loop index to productsLoading.
this.productsLoading.push(index)
after http request done, remove index from productsLoading.
this.productsLoading.splice(this.productoading.indexOf(index), 1)
and check button with :loading=productsLoading.includes(index)
You can create another component only for product card,
for better option as show below
Kindly follow this steps.
place the content of card in another vue component as shown below.
<!-- Product.vue -->
<template>
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</templete>
<script>
export default {
name: "Product",
data() {
return {
isLoading: false
}
},
props: {
product: {
type: Object,
required: true
}
},
methods: {
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
}
}
</script>
Change your component content as shown below.
<template>
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<product :product="product" />
</div>
</div>
</div>
</div>
</templete>
<script>
import Product from 'path to above component'
export default {
components: {
Product
}
}
</script>
so in the above method you can reuse the component in other components as well.
Happy coding :-)

Data Reactivity On Post Request Vue

Im having issues with my data not reacting correctly when a new object is added to the array of data. I am currently looping through an array of engagements that belong to a client like this
<div class="row mx-2 px-2 justify-content-between" v-if="!engagementLoaded">
<div class="card mb-3 shadow-sm col-lg-5 col-md-3 p-0" v-for="(engagement, index) in engagement" :key="index">
<div class="d-flex justify-content-between card-header">
<h3 class="m-0 text-muted">{{ index + 1 }}</h3>
<h5 class="align-self-center m-0"><span>Return Type: </span> {{ engagement.return_type }} </h5>
</div>
<div class="card-body text-left p-0 my-1">
<h5 class="p-4"><span>Year: </span> {{ engagement.year }} </h5>
<hr class="my-1">
<h5 class="p-4"><span>Assigned To: </span> {{ engagement.assigned_to }} </h5>
<hr class="my-1">
<h5 class="p-4"><span>Status: </span> {{ engagement.status}} </h5>
</div>
<div class="card-footer d-flex justify-content-between">
<router-link to="#" class="btn">View</router-link>
<router-link to="#" class="btn">Edit</router-link>
</div>
</div>
</div>
Everything works fine until I add a new engagement to the array. My AddEngagement Component is seperate but this is the form and script for it.
<form #submit.prevent="addEngagement" class="d-flex-column justify-content-center">
<div class="form-group">
<select class="form-control mb-3" id="type" v-model="engagement.return_type">
<option v-for="type in types" :key="type.id" :value="type">{{ type }}</option>
</select>
<input type="text" class="form-control mb-3" placeholder="Year" v-model="engagement.year">
<input type="text" class="form-control mb-3" placeholder="Assign To" v-model="engagement.assigned_to">
<input type="text" class="form-control mb-3" placeholder="Status" v-model="engagement.status">
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary d-flex justify-content-start">Create</button>
<router-link v-bind:to="'/client/' +client.id+ '/engagements'" class="btn btn-secondary float-right">Dismiss</router-link>
</div>
</div>
</form>
This is the addEngagement Method for the form
methods: {
addEngagement(e) {
if(!this.engagement.return_type || !this.engagement.year ){
return
} else {
this.$store.dispatch('addEngagement', {
id: this.idForEngagement,
client_id: this.client.id,
return_type: this.engagement.return_type,
year: this.engagement.year,
assigned_to: this.engagement.assigned_to,
status: this.engagement.status,
})
e.preventDefault();
}
e.preventDefault();
this.engagement = ""
this.idForEngagement++
this.$router.go(-1);
},
},
I think the issue is happening here but im not sure
this.$router.go(-1);
Ive tried this as well
this.$router.push({path: 'whatever route'})
and it did not change either
Somehow I need the parent component EngagementsList to react correctly to my newly added engagement that is submitted from the AddEngagement componenet if that makes sense..
here is the code for the addEngagement in the Vuex
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagement', response.data)
})
.catch(error => {
console.log(error)
})
},
Although I can't see your code, I'm pretty certain I know the problem. You have an array but it's not reactive to the objects within. This is because Vue doesn't know to observe this array.
If you want your array to be reactive, use Vue.set from within your store model.
Vue.set(this/state, 'array_name', Array<Of objects>)
This will make sure vue watches for changes within the Array.
For your use case, you will need to append the engagement object to the existing array and then use that array:
const engagements = state.engagements
engagements.push(engagement)
Vue.set(state, 'engagements', engagements)
So i've tried different configurations of this. I changed my state description that the v-for is iterating to clientengagements
engagement now equals clientengagements
here is the mutation currently
getClientEngagements(state, engagement, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},
Which is not working. Ive also tried
getClientEngagements(state, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},
Which will tell me that "engagement" is not defined
If anyone is still able to help this is the Action
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagements', response.data)
})
.catch(error => {
console.log(error)
})
},
The response.data is mutating my clientengagements array to just a single object of the newly added engagement..
context.commit('getClientEngagements`, response.data)
Is commiting to this mutation
getClientEngagements(state, engagement, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},