Remove value from localStorage in Vuejs - javascript

friends
Problem
I have the problem that when I am in the video section watching a particular episode, in the local storage the src of the video that I saw before is stored.
My intention is to eliminate the value of the local storage once it leaves the video section.
Template Video
<template>
<!-- Main -->
<main class="flex-1 bg-grey-lightest z-0 py-5 px-0">
<div class="flex flex-wrap max-w-5xl mx-auto">
<!-- main col -->
<div class="w-full md:flex-1">
<!-- player -->
<div class="bg-black relative mb-3">
<video
id="video"
controls
><source :src="videos.video" type="video/mp4"></video>
</div>
<!-- video info -->
<div class="flex flex-wrap items-end">
<!-- title -->
<div class="pb-2">
<h1 class="text-xl my-2">
<div class="p-2 bg-indigo-800 items-center text-indigo-100 leading-none lg:rounded-full flex lg:inline-flex" role="alert">
<span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">video</span>
<span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">{{content}}</span>
<span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">{{state}}</span>
<span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">eps - {{eps}}</span>
<span class="font-semibold mr-2 text-left flex-auto">{{Title}}</span>
<svg class="fill-current opacity-75 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/></svg>
</div>
</h1>
<span class="text-base text-grey-darken">{{synopsis}}</span>
</div>
<!-- buttons actions -->
<div class="ml-auto">
<!-- likes -->
<div class="flex relative pb-2">
<!-- like -->
<div class="flex items-center">
<span class="text-black opacity-50 text-sm"> ライウアニミー</span>
</div>
<!-- hate -->
<div class="flex items-center ml-5">
<span class="text-xs text-grey-darken ml-1">🥇</span>
</div>
<div class="absolute w-full h-1 bg-grey pin-b t-5 rounded-full overflow-hidden">
<div class="absolute pin-l pin-t w-3/4 h-full bg-grey-darkest"></div>
</div>
</div>
</div>
<hr class="w-full border-t m-0 mb-3 "/>
</div>
</div>
</div>
</main>
</template>
Script
What I have implemented In the mounted property is that if the video exists, it eliminated the obj videos that belongs to the key vuex.
But what I have done does not do anything, when I leave the video section the video object maintains the src of the video.
<script>
import swal from 'sweetalert';
import {mapState} from 'vuex'
import store from '../store/store'
export default{
name: 'Video',
props: ['Id' , 'Title' , 'Eps' , 'Synopsis' , 'ContentType' , 'State'],
data(){
return{
id: this.Id,
eps: this.Eps,
synopsis: this.Synopsis,
content: this.ContentType,
state: this.State,
}
},
computed:{
...mapState(['videos' , 'isLoading'])
},
watch:{
"Eps": function(value){
this.eps = value;
let eps = this.eps;
let info = {id: this.id , eps: eps}
store.dispatch('GET_ANIME_VIDEO' , info)
swal("Message!", "Wait a few seconds for the video to load\nIt's normal that it takes a bit", "success");
},
"videos.video": function(value){
this.videos.video = value;
document.getElementById('video').load();
}
},
mounted(){
if(this.videos.video){
const vuex = JSON.parse(localStorage.getItem('vuex'));
delete vuex.videos;
localStorage.setItem("vuex", JSON.stringify(vuex));
}
},
};
</script>
Mutation
I think what I have to do is create a mutation to clean the localstorage
export const mutations = {
initialiseStore(state) {
// Check if the ID exists
if(localStorage.getItem('store')) {
// Replace the state object with the stored item
this.replaceState(
Object.assign(state, JSON.parse(localStorage.getItem('store')))
);
}
},
SET_LATEST_DATA(state , payload){
state.latest = payload;
},
SET_VIDEO_ANIME(state , payload){
state.videos = payload;
},
SET_ANIME_ALPHA(state , payload){
state.animesByAlpha = payload;
},
SET_ANIME_GENDER(state , payload){
state.animesByGender = payload;
},
SET_ANIME_SEARCH(state , payload){
state.searchAnimeList = payload;
},
SET_GET_SCHEDULE(state , payload){
state.schedule = payload;
},
SET_MOVIES(state , payload){
state.movies = payload;
},
SET_OVAS(state , payload){
state.ovas = payload;
},
IS_LOADING(state , payload){
state.isLoading = payload;
}
};
Action
GET_ANIME_VIDEO({commit} , info){
console.log("id: " , info.id , "chapter: " , info.eps)
A.get(`${API_URL_ENDPOINT.video}` + "/" + `${info.id}` + "/" + `${info.eps}`)
.then((res) =>{
console.log("video src = " , res.data)
commit('SET_VIDEO_ANIME' , res.data);
commit('IS_LOADING' , false);
}).catch((err) =>{
console.error(err);
});
},

Solution
For something like this, I'd use the Vue lifecycle hook beforeDestroy:
Called right before a Vue instance is destroyed. At this stage the instance is still fully functional.
Example:
export default {
// ... vue component stuff ...
beforeDestroy () {
localStorage.removeItem('vuex');
}
}
Explanation
This will ensure that before the instance is removed from the virtual AND real DOM, we are removing an item from the localStorage called vuex.
Please note, that this does not clear the state that's stored in Vuex. For that, you'd want to create a mutation and call that mutation in the same way from beforeDestroy().
Hope this helps!

My Solution
I found the solution, but I do not know if it is the most optimal. But he does the cleaning.
beforeDestroy(){
const vuex = JSON.parse(localStorage.getItem('vuex'));
if(vuex.videos){
localStorage.removeItem(vuex.videos);
}
}
Actual Problem
When I leave the video session and I want to go see another video, the screen of the previous video remains
My intention is to keep the screen black and the minutes of the video at zero

Related

Cannot read properties of undefined when in React component but works fine when in getStaticProps function

I'm trying to create a blog with Node.js, React and graphql. I'm new to all those technologies (including Java-/Typescript itself). I'd greatly appreciate your help.
Currently I'm stuck getting the data of my articles to show.
In the function getStaticProps I can read the data of my graphql response fine.
But when I pass that data as a parameter to the React component (as getStaticProps provides that data during build-time) I can't access that data anymore and get the error message "TypeError: Cannot read properties of undefined (reading 'articles')".
Below is my code:
import {GetStaticProps} from "next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import {ssrGetBlog} from "../../generated/page";
// #ts-ignore implicitly has any type
export default function Index(data) {
console.log(data.attributes) //TypeError: Cannot read properties of undefined (reading 'articles')
// below in getStaticProps this worked!?
return (
<div className="relative bg-gray-50 px-4 pt-16 pb-20 sm:px-6 lg:px-8 lg:pt-24 lg:pb-28">
<div className="absolute inset-0">
<div className="h-1/3 bg-white sm:h-2/3"/>
</div>
<div className="relative mx-auto max-w-7xl">
<div className="text-center">
<h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
{data.blogHeading}
</h2>
<p className="mx-auto mt-3 max-w-2xl text-xl text-gray-500 sm:mt-4">
{data.blogDescription}
</p>
</div>
<div className="mx-auto mt-12 grid max-w-lg gap-5 lg:max-w-none lg:grid-cols-3">
{data.attributes.articles.data.map((article) => (
<div
key={article.attributes.title}
className="flex flex-col overflow-hidden rounded-lg shadow-lg"
>
<div className="flex-shrink-0">
<img
className="h-48 w-full object-cover"
src={article.attributes.articleImg.data.attributes.url}
alt=""
/>
</div>
<div className="flex flex-1 flex-col justify-between bg-white p-6">
<div className="flex-1">
<p className="text-sm font-medium text-indigo-600">
<a href={"not implemented: article.attributes.articleCategory.data.attributes.slug"} className="hover:underline">
{"none existent: article.attributes.articleCategory.data.attributes.categoryName"}
</a>
</p>
<a href={article.attributes.slug} className="mt-2 block">
<p className="text-xl font-semibold text-gray-900">
{article.attributes.title}
</p>
<p className="mt-3 text-base text-gray-500">
{article.attributes.shortDescription}
</p>
</a>
</div>
<div className="mt-6 flex items-center">
<div className="flex-shrink-0">
<a href={"not implemented: article.attributes.author.href"}>
<span className="sr-only">{"not implemented: article.author.authorName"}</span>
<img
className="h-10 w-10 rounded-full"
src={"not implemented: article.attributes.author.authorImg"}
alt=""
/>
</a>
</div>
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">
<a href={"not implemented: article.attributes.author.authorUrl"} className="hover:underline">
{"not implemented: article.attributes.author.name"}
</a>
</p>
<div className="flex space-x-1 text-sm text-gray-500">
<time dateTime={article.attributes.publishedOn}>{article.attributes.publishedOn}</time>
<span aria-hidden="true">·</span>
<span>{"not implemented: article.attributes.readingTime"} read</span>
</div>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
export const getStaticProps: GetStaticProps = async (context) => {
const {locale} = context;
// #ts-ignore
const blogQuery = await ssrGetBlog.getServerPage({}, context);
const data = blogQuery.props.data?.blog?.data; //props.data?.blog?.data?.attributes;
//console.log(data); // works fine
//console.log(data.attributes.articles.data[0]); // works fine
return {
props: {
data,
...(await serverSideTranslations(locale || "", ["common"])),
},
};
};
Try this, maybe you're not accessing the component's props properly
export default function Index(props) {
console.log(props.data.attributes)
}
or
export default function Index({ data }) {
console.log(data.attributes)
}
export const getStaticProps: GetStaticProps = async (context) => {
const {locale} = context;
// #ts-ignore
const blogQuery = await ssrGetBlog.getServerPage({}, context);
const data = blogQuery.props.data?.blog?.data; //props.data?.blog?.data?.attributes;
//console.log(data); // works fine
//console.log(data.attributes.articles.data[0]); // works fine
const comething = await serverSideTranslations(locale || "", ["common"])
return {
props: {
data,
...comething,
},
};
};
Try this maybe?

Passing Function between components Vue

I'm currently trying to pass the getUser function from a file called Login.vue which gets the login details to an Authenticated.vue page to display the user and their email. How can I go about doing this. I've tried following the official vue documents but can't get it to work. Not sure whats going wrong.
Login.Vue
<template>
<form #submit.prevent="submitLogin">
<div>
<!-- Email -->
<div>
<label for="email" class="block font-medium text-sm text-gray-700">
Email
</label>
<input v-model="loginForm.email" id="email" type="email" class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required autofocus autocomplete="username">
<!-- Validation Errors -->
<div class="text-red-600 mt-1">
<div v-for="message in validationErrors?.email">
{{ message }}
</div>
</div>
</div>
<!-- Password -->
<div class="mt-4">
<label for="password" class="block font-medium text-sm text-gray-700">
Password
</label>
<input v-model="loginForm.password" id="password" type="password" class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required autocomplete="current-password">
<!-- Validation Errors -->
<div class="text-red-600 mt-1">
<div v-for="message in validationErrors?.password">
{{ message }}
</div>
</div>
</div>
<!-- Remember me -->
<div class="block mt-4">
<label class="flex items-center">
<input type="checkbox" name="remember" v-model="loginForm.remember" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" />
<span class="ml-2 text-sm text-gray-600">Remember me</span>
</label>
</div>
<!-- Buttons -->
<div class="flex items-center justify-end mt-4">
<button class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150 ml-4" :class="{ 'opacity-25': processing }" :disabled="processing">
Log in
</button>
</div>
</div>
</form>
</template>
<script>
import { useRouter} from 'vue-router';
import { reactive } from '#vue/runtime-core'
export default {
data() {
return {
categories: {},
router: useRouter(),
loginForm: reactive({
email: '',
password: '',
remember: false
}),
user: reactive({
name: '',
email: '',
}),
processing: false,
validationErrors: {},
}
},
mounted() {
},
methods: {
submitLogin() {
if (this.processing) return
this.processing = true;
axios.post('/login', this.loginForm)
.then(response => this.loginUser(response))
.catch(error => {
if (error.response?.data) {
this.validationErrors = error.response.data.errors
}
})
.finally(this.processing = false)
},
loginUser(response){
this.user.name = response.data.name
this.user.email = response.data.email
localStorage.setItem('loggedIn', JSON.stringify(true))
this.router.push({name: 'posts.index'})
},
getUser(){
axios.get('/api/user')
.then(response => {
loginUser(response)
})
this.user.name = response.data.name
this.user.email = response.data.email
console.log(this.user.name);
console.log(this.user.email);
},
},
}
</script>
Authenticated.vue
<template>
<div class="min-h-screen bg-gray-100">
<nav class="bg-white border-b border-gray-100">
<!-- Primary Navigation Menu -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="/">
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" class="block h-10 w-auto fill-current text-gray-600">
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<router-link :to="{name:'posts.index'}" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Posts
</router-link>
<router-link :to="{name:'posts.create'}" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Create Post
</router-link>
</div>
</div>
<div class="flex items-center">
<div>
<div>Hi, {{user.name}}</div>
<div class="text-sm text-gray-500"> {{user.email}} </div>
</div>
</div>
<button #click="logout" type="button" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150 ml-4" :class="{ 'opacity-25': processing }" :disabled="processing">
Log out
</button>
</div>
</div>
</nav>
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{currentPageTitle}}
</h2>
</div>
</header>
<!-- Page Content -->
<main>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<router-view></router-view>
</div>
</div>
</div>
</div>
</main>
</div>
</template>
<script>
import { reactive } from '#vue/runtime-core'
export default {
props: {
getUser: {type: Function}
},
computed: {
currentPageTitle(){
return this.$route.meta.title;
}
},
mounted() {
this.getUser()
},
methods: {
}
}
</script>
App.js
require('./bootstrap');
import { createApp, onMounted } from 'vue'
//import App from './layouts/App'
import LaravelVuePagination from 'laravel-vue-pagination';
import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
import router from './routes/index'
const app = createApp({
})
app.use(router)
app.use(VueSweetalert2)
app.component('Pagination', LaravelVuePagination)
app.mount('#app')
you should vuex for this perpose.
getUser(){
axios.get('/api/user')
.then(response => {
this.$store.commit('storeUser',response)
this.router.push({name: 'posts.index'})
})
},
in store/index.js (create folder of store in root )
import { createApp } from 'vue'
import { createStore } from 'vuex'
// Create a new store instance.
const store = createStore({
state () {
return {
userData:{}
}
},
getters: {
userData(state){
return state.userData
}
},
mutations: {
storeUser (state,payload) {
state.userData= payload
}
}
})
export default store
in main.js
import store from 'store/index.js'
const app = createApp({ /* your root component */ })
// Install the store instance as a plugin
app.use(store)
in Authenticated.vue (or whereevet you want to use any value from store)
computed:{
userData(){
return this.$store.getters.userData // here you can get your
response object from api
which you call during login
}
}

How to set up Vue 3 parent component to emit event to child component

I am attempting to set up a button in a Vue 3 Tailwind parent component to trigger a HeadlessUI transition event in a child component. My goal is to enable the button in the parent to emit an event, while the child component "watches" for the event before triggering the transition event as part of the callback function in the watch. So far, I have the parent component set up to trigger the emit, while the child component is set up to watch for the "transition" event. However, the event is not being executed. I'm afraid I don't have the watch in the child component set up correctly, so as to watch for the button click in the parent component. How can I go about enabling the child component to watch for the click of the button in the parent component?
Here is my code so far:
Parent:
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<div class="min-h-full">
<Disclosure as="nav" class="bg-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<button type="button" #click="transition" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Click to transition</button>
</div>
</div>
</div>
</div>
</div>
</Disclosure>
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
<HelloWorld :event="transition" />
</div>
</div>
</main>
</div>
</template>
<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '#headlessui/vue'
import { BellIcon, MenuIcon, XIcon } from '#heroicons/vue/outline'
import HelloWorld from './components/HelloWorld.vue'
</script>
Child:
<template>
<div class="flex flex-col items-center py-16">
<div class="w-96 h-96">
<TransitionRoot
appear
:show="isShowing"
as="template"
enter="transform transition duration-[400ms]"
enter-from="opacity-0 rotate-[-120deg] scale-50"
enter-to="opacity-100 rotate-0 scale-100"
leave="transform duration-200 transition ease-in-out"
leave-from="opacity-100 rotate-0 scale-100 "
leave-to="opacity-0 scale-95 "
>
<div class="w-full h-full bg-gray-400 rounded-md shadow-lg" />
</TransitionRoot>
</div>
</div>
</template>
<script setup>
import { ref, toRefs, watch } from 'vue'
import { TransitionRoot } from '#headlessui/vue'
const props = defineProps({
transition: Function
})
const { transition } = toRefs(props)
const isShowing = ref(true)
watch(transition, () => {
isShowing.value = false
setTimeout(() => {
isShowing.value = true
}, 500)
})
</script>
events should go up and state should go down.
make your child component to watch a property and the button in parent should change the state of that property
update:
const { transition } = toRefs(props)
you might be losing reactivity here.
more info: https://stackoverflow.com/a/64926664/420096
update2:
the way you made it should work, but point directly to the prop is fine too:
https://codesandbox.io/s/relaxed-sea-y95x6c?file=/src/App.vue
Based on Sombriks' feedback, here is the answer:
Parent:
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<div class="min-h-full">
<Disclosure as="nav" class="bg-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<button type="button" #click="transition" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Click to transition</button>
</div>
</div>
</div>
</div>
</div>
</Disclosure>
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
<HelloWorld :show="show" />
</div>
</div>
</main>
</div>
</template>
<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '#headlessui/vue'
import { BellIcon, MenuIcon, XIcon } from '#heroicons/vue/outline'
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
const show = ref(true)
const transition = () => {
show.value = !show.value
}
</script>
Child:
<template>
<div class="flex flex-col items-center py-16">
<div class="w-96 h-96">
<TransitionRoot
appear
:show="isShowing"
as="template"
enter="transform transition duration-[400ms]"
enter-from="opacity-0 rotate-[-120deg] scale-50"
enter-to="opacity-100 rotate-0 scale-100"
leave="transform duration-200 transition ease-in-out"
leave-from="opacity-100 rotate-0 scale-100 "
leave-to="opacity-0 scale-95 "
>
<div class="w-full h-full bg-gray-400 rounded-md shadow-lg" />
</TransitionRoot>
</div>
</div>
</template>
<script setup>
import { ref, toRefs, watch } from 'vue'
import { TransitionRoot } from '#headlessui/vue'
const props = defineProps({
show: Boolean
})
const { show } = toRefs(props)
const isShowing = ref(true)
watch(show, () => {
isShowing.value = false
setTimeout(() => {
isShowing.value = true
}, 500)
})
</script>

Filter product price using input range and render in the DOM

Can anyone help me to solve my problem with filtering. I'm trying to filter the products price using the input range slider. I have an array state which I stored all the products coming from the API. My problem is when I move the slider it doesn't move and when I move the slider to the left the products array starts to filter the items where price is less than equal to the input range value. Another problem is when I move the range slider to the right the products array doesn't filter anymore. I'm trying to render it but it doesn't render. I'm using publisher-subscriber pattern for filtering the price.
model.js
export const state = {
products: [],
};
export const loadAllProducts = async function () {
try {
const data = await getJSON(`${API_URL}`);
state.products = data.map(product => {
return {
id: product.id,
name: product.name,
image: product.image,
brand: product.company,
category: product.category,
price: product.price,
};
});
} catch (error) {
console.error(error);
throw error;
}
};
export const loadFilterPrice = function (value) {
const filtered = state.products.filter(product => product.price <= value);
if (filtered.length < 1) throw new Error('Sorry, no products matched your search!');
state.products = filtered;
console.log(state.products);
};
productsView.js This function is for filtering the product price. Publisher-subscriber pattern.
addHandlerFilterPrice(handler) {
this._parentElement.addEventListener('input', function (e) {
const priceFilter = e.target.closest('.price__filter');
const priceText = document.querySelector('.price__text');
const value = parseInt(priceFilter.value);
if (!priceFilter) return;
priceFilter.value = value;
priceText.textContent = formatPrice(value);
handler(value);
});
}
Controller
import * as model from '../model';
import ProductsView from '../Views/productsView';
//This is for rendering all the products
const controlProducts = async function () {
try {
ProductsView.renderSpinner();
await model.loadAllProducts();
ProductsView.render(model.state.products);
} catch (error) {
console.error(`${error} 💥💥💥`);
}
};
//This is for filtering the products by price using the input range slider
const controlFilterPrice = function (value) {
try {
model.loadFilterPrice(value);
ProductsView.render(model.state.products);
} catch (error) {
console.error(`${error} 💥💥💥`);
}
};
const init = () => {
controlProducts();
ProductsView.addHandlerFilterPrice(controlFilterPrice);
};
init();
This is how I render the product. I call this _generateMarkup in the parent class
_generateMarkup() {
return /*html*/ `
<a
href="${detailsPage}&id=${this._data.id}"
class="relative flex flex-col overflow-hidden rounded-lg shadow-customSm transition duration-300 hover:shadow-lg"
>
<figure class="relative h-48 sm:h-52 xl:h-44 w-full overflow-hidden xl:w-full">
<img
src="${this._data.image}"
alt="${this._data.name}"
class="absolute inset-0 h-full w-full object-cover object-center transition duration-300 hover:scale-105"
/>
</figure>
<div class="flex flex-col">
<div class="px-6 pb-8 pt-6">
<span
class="mb-3 inline-block rounded-full bg-amber-100 px-3 py-1 text-xs font-bold uppercase tracking-wider text-amber-500"
>${this._data.brand}</span
>
<h4 class="mb-2 border-b pb-3 text-base font-bold uppercase text-neutral-600">
${this._data.name}
</h4>
<div class="mt-4 flex items-center justify-between">
<div class="flex items-baseline space-x-1 text-amber-500">
<p class="text-lg font-medium">${formatPrice(this._data.price)}</p>
</div>
<div class="flex items-center space-x-3">
<button class="icon h-6 w-6" aria-label="Add to Wishlist">
<svg class="h-5 w-5 fill-current">
<use xlink:href="${icons}#icon-wishlist"></use>
</svg>
</button>
</div>
</div>
</div>
</div>
</a>
`;
}

Scroll down when clicked with Vue.js

What I want to do is that when the user clicks on an article, it scrolls down to a sibling section. Code looks something like this.
<template>
<article #click="handleCardClick" class="text-center mr-8 mb-12 cursor-pointer hover:opacity-50 w-1/5">
<picture class="flex justify-center items-center mb-4 w-full" style="height: 320px">
<img :src="source" :alt="imgAlt" class="shadow-md" style="" />
</picture>
<h4 class="font-bold mb-1">{{ title }}</h4>
<h6 class="text-sm text-gray-600">{{ tags.length > 0 ? tags[0].name : '' }}</h6>
</article>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
props: {
title: {
type: String,
required: true,
},
},
computed: {
...mapState({
previewIndex: state => state.templates.hasTemplate
}),
},
methods: {
...mapActions({
setActiveTemplate: 'templates/setActive',
setPreview: 'templates/setPreview',
removePreview: 'templates/removePreview',
}),
handleCardClick () {
this.setActiveTemplate(this.template);
this.selectTemplate(this.pos);
},
}
}
</script>
And the other file looks like this
<template>
<section v-if="template" class="flex justify-between w-full pt-10 pl-10 pr-5 pb-12 relative border-b border-t border-black my-4" style="height: 75vh">
<article class="flex flex-col justify-between" style="width: 25%">
<button #click="changeSection('invite')" class="h-1/3 pb-4">
<picture class="h-full w-full flex justify-center items-center bg-gray-100">
<img :src="template.url || ''" class="bg-gray-200 shadow-lg" style="min-height: 20px; min-width: 20px; height:80%" alt="Preview de la invitacion">
</picture>
</button>
</article>
</section>
</template>
I'm a bit new to Vue, so maybe it's really simple and I just can't find how to do it :) Thanks in advance!
You only need to assign a reference ref to each article and then build a method to go to any of your referenced articles:
<article #click="goto('art1')">Go to article 1</article>
For earch sibiling declare it's reference so you can call them on the goto method
<article ref="art1">
Article 1
</article>
Declare the goto method, it has a parameter, the reference of where you want to go.
methods: {
goto(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
},
And this is it.
If you have the click action inside a child component then you'll have to use $emit to perform the click action on the parent, here is an example following the above:
Parent
<template>
<Article #scrollto="goto"></Article>
<Section ref="art1">
...
</Section>
</template>
<script>
import Article from "./article";
import Section from "./section";
export default {
methods: {
goto(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
}
</script>
Article.vue
<template>
<div>
<button #click="$emit("scrollto", 'art1')">
Go to the Article!
</button>
</div>
</template>
Documentation about vue ref function
Documentation about window.scrollTo function

Categories

Resources