Svelte with DaisyUI Modal not opening after promise is resolved - tailwind-css

I've got a modal which is supposed to open when a link is clicked:
<script>
import AddressForm from "../forms/AddressForm.svelte";
let openModal = false;
let formData = {};
const showModal = async () => {
const id = "bb80b8f9-1de8-431c-9b07-421e462d59e4";
openModal = true;
}
</script>
<div class="flex justify-center">
<a href="#address-modal" on:click={showModal}>
<span class="align-middle edit-color">Edit</span>
</a>
</div>
{#if openModal}
<h1>Test Ttitle</h1>
<div class="modal" id="address-modal">
<div class="modal-box relative w-11/12 max-w-5xl">
<label for="address-modal" class="btn btn-sm btn-circle absolute right-2 top-2">✕</label>
<AddressForm
formData={formData}
isModal={true}
/>
</div>
</div>
{/if}
The code above works fine, when the link is clicked the modal appears. But now I've gotta send some data to the component loaded in the modal, to do so I'm making a call within showModal to a function that returns a promise. After the promise is resolved the modal is not opening:
const showModal = async () => {
// console.log('opening modal')
const id = "bb80b8f9-1de8-431c-9b07-421e462d59e4";
let address = await getAddress(id)
console.log(address)
formData = address;
openModal = true;
}
with the code above the modal does not open. I tried the following as well:
const showModal = async () => {
// console.log('opening modal')
const id = "bb80b8f9-1de8-431c-9b07-421e462d59e4";
let address = await getAddress(id)
.then(address => {
console.log(address)
formData = address;
openModal = true;
});
}
but the result is the same.
Any idea what's going on?

Related

Not getting data through emit event. How do you communicate an array of selected IDs to the parent?

I am building this ecommerce site based on strapi and vue3/pinia.
My products come from ProductStore.js through this function:
async fillArchive() {
this.loading = true;
this.products = await fetch(
apiUrl +
"/api/products?fields=name,featured,description,price,slug&populate=image,category"
)
.then((res) => res.json())
.then((data) => (this.products = data.data))
.catch((err) => (error.value = console.log(err)));
this.loading = false;
},
I get my categories from CategoryStore.js:
async fill() {
this.loading = true;
this.categories = await fetch(apiUrl + "/api/categories?fields=name,slug")
.then((res) => res.json())
.then((data) => (this.categories = data.data))
.catch((err) => (error.value = err));
this.loading = false;
},
So the problem is that I am able to collect the selectedCategories in an array in the ProductFilters.vue child component, its also printed and shows up, but..
<script setup>
import { ref } from "vue";
import { useCategoryStore } from "#/stores/CategoryStore";
const categoryStore = useCategoryStore();
categoryStore.fill();
const checkedCategories = ref([]);
const emit = defineEmits(["changeCheck"]);
const changeCheck = function () {
emit("changeCheck", checkedCategories.value);
};
</script>
<template>
<div>
<label
v-for="category in categoryStore.categories"
:key="category.id"
class="text-sm flex flex-row items-center gap-1.5"
><input
type="checkbox"
:value="category.attributes.name"
v-model="checkedCategories"
#input="changeCheck"
/>{{ category.attributes.name }}</label
>
</div>
{{ checkedCategories }}
</template>
.. i am not getting the checkCategories array in the parent component, whereby I'd like to filter my rendered products. If a category is checked i'd like to render the related products, if all categories are checked render all, none checked also all.
In my parent component - ProductsView.vue - the emited data is not showing up, i'm always getting undefined.
<script setup>
import { computed } from "vue";
import { useProductStore } from "#/stores/ProductStore";
import IconDown from "../components/icons/IconDown.vue";
import IconUp from "../components/icons/IconUp.vue";
import ProductFilters from "../components/ProductFilters.vue";
import ProductArchiveCard from "../components/ProductArchiveCard.vue";
const imageLink = import.meta.env.VITE_STRAPI_URL;
const productStore = useProductStore();
function updateCategories(catName) {
let selected = [];
selected.push(catName);
return console.log(selected);
}
const filteredProducts = computed(() => {
// filter by catName or id and v-for filteredProducts
});
productStore.fillArchive();
</script>
<template>
<section class="px-2 flex flex-col xl:flex-row">
<div class="pt-24 xl:pt-36 xl:w-1/4 relative">
<!-- order by category -->
<ProductFilters #changeCheck="updateCategories(catName)" />
</div>
<div v-if="!productStore.loading">
<div
class="mx-auto grid grid-flow-row gap-10 md:gap-10 lg:gap-16 xl:gap-6 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4"
>
<ProductArchiveCard
class="mx-auto"
v-for="product in productStore.products"
:key="product.name"
:product="product"
:imageLink="imageLink"
/>
</div>
</div>
<div v-else class="flex justify-center py-16 min-h-screen">
<div class="spinner w-8 h-8"></div>
</div>
</section>
</template>
What am I doing wrong in the event handling? Can someone point it out? :) Thanks in advance folks!

unable to render text to DOM from state vue 3

I am trying to render a name to my component, which I get from an axios response. I am able to print the name in the console but {{username}} is never updated.
setup() {
const state = reactive({
username: '',
})
const submit = async () => {
try {
const response = await api.getTest()
if (response != null) {
state.username = response.name
console.log("I am the state " + state.username)
}
} catch (error) {
console.log('Error while getting the response:', error)
}
}
return {
...state,
submit
}
},
template
<template>
<button v-on:click="submit()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
<div class="text-white">
Name: {{username}}
</div>
</template>
why is the username not updating?
is this the preferred way to do this?
You are using a reactive object, so you have to use that object in the template. username is not defined in the template scope, it would be {{state.username}}.
One other approach would be to define the username as a ref, but then you have to set it's value:
const username = ref('');
And in the async function:
username.value = response.name

How to download image with css filters in React

Hi I'm trying to make photo filter edotor with react and css I'm adding css filters on photo and now need to download edited photo but I only managed to download just uploaded photo
import { saveAs } from 'file-saver'
//upload
const [file, setFile] = useState();
function photoChanged(e) {
setFile(URL.createObjectURL(e.target.files[0]));
}
// photo download
const downloadImage = () => {
saveAs(`${file}`, 'image.jpg') // img url
}
return (
<div className="main-image">
<h2>Add Image:</h2>
<button onClick={downloadImage}>Download!</button>
<input type="file" onChange={photoChanged} />
<img src={file} style={getImageStyle()}/> //from here I added filters as props
</div>
)
any advice? Thanks
I solved it just use htmlToImage package
const domEl = useRef(null);
const downloadImage = async () => {
const dataUrl = await htmlToImage.toPng(domEl.current);
// download image
const link = document.createElement('a');
link.download = 'html-to-img.png';
link.href = dataUrl;
link.click();
};
return <button onClick={downloadImage}>Download!</button>
<img ref={domEl} src={file} style={getImageStyle()}/>

Typescript: Clicking 'X' doesn't successfully close my popup banner

I've created a popup banner with the following code (sorry if the format is off)
//this is in folder Banner.tsx
import React, {useCallback} from "react";
type Properties = {
close: () => void;
text: string;
const Banner: React.FC<Properties> = ({close, text}) => {
const onClick = useCallback(() => {
close();},
[close, text]);
return (
<div className = "BannerBox">
<div className = "banner">
<span className = "popup"> {onClick}{close}[x]
</span>
{text}
</div>
</div>
);
};
export default Banner;
//this is App.tsx
import Banner from "./Components/Banner";
function App(): JSX.Element {
const [isOpen, setIsOpen]=useState(false);
const toggleBanner = () => {
SetIsOpen(!isOpen);
};
return (
<div>
<input type = "button"
value = "popup"
onClick={toggleBanner}/>
<p>hi</p>
{isOpen && <Banner text = {"hello"} close={function (): void { throw new Error("Function not implemented.");
} }/>}
</div>
export default App;
//this is my Banner.css file (let me know if you need the full code but this is some of it)
.BannerBox{
position: fixed;
background: blue;
width:50%;
}
.banner{
position: relative;
width: 100%;
}
.popup{
content: 'x';
position: fixed;
background: green;
}
the code compiles just fine, I'm not getting any errors but the problem is that when the banner pop-ups, I can't close it by clicking 'X' i have to refresh the page in order to close the banner and I'm not sure how to fix that. Any help is appreciated!
The close function needs to be passed to the onClick callback for the span which is acting as the button. These are added as "attributes" for the jsx element. See below onClick={onClick} where your onClick callback function is passed by reference (notice no invoking parentheses within the curly braces)
In the return of Banner.tsx
<span className="popup" onClick={onClick}>
[x]
</span>
close is passed into your Banner component, so this needs to be implemented in your App component. I ensured it always closes by explicitly setting isOpen to false (instead of calling toggle)
in the return of App.tsx
{isOpen && <Banner text={"hello"} close={() => setIsOpen(false)} />}
so in total
Banner.tsx
import React, { useCallback } from "react";
import "./Banner.css";
type Properties = {
close: () => void;
text: string;
};
const Banner: React.FC<Properties> = ({ close, text }) => {
const onClick = useCallback(() => {
close();
}, [close]);
return (
<div className="BannerBox">
<div className="banner">
<span className="popup" onClick={onClick}>
[x]
</span>
{text}
</div>
</div>
);
};
export default Banner;
and App.tsx
import React, { useState } from "react";
import Banner from "./Components/Banner";
function App(): JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const toggleBanner = () => {
setIsOpen(!isOpen);
};
return (
<div>
<input type="button" value="popup" onClick={toggleBanner} />
<p>hi</p>
{isOpen && <Banner text={"hello"} close={() => setIsOpen(false)} />}
</div>
);
}
export default App;
See codesandbox here
Let's assume that close() will actually close the popup banner since you did't show the implementation of it.
This line causes the issue
<span className = "popup">{onClick}{close}[x]</span>
You are supposed to pass a function to the onClick listener. Something like:
<span className = "popup" onClick={close}>[x]</span>

render dynamically modal based on row onclick - ReactJS

working on a project for a undisclosed in which data on patients gets pulled from their api and gets loaded as modal on the page . When you click on a modal more info of the threat gets pulled up as a modal. The goal here is for them to render when someone clicks for it based on div.
how do I proprly send the data from api to modal component on each click ?
div table-alike on click
import React, {useState,useEffect} from 'react';
const keys = Object.keys(arr[0]);
// const handleOnClick = param => {
// console.log('do something: ', param);
// }
export default function Demo() {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const toggleModal = () => {
setModalIsOpen(!isModalOpen);
};
const handleOnClick = () => {
toggleModal()
};
useEffect(() => {
const fetchUsers = async () => {
try {
const { data } = await axios.get('https://gist.githubusercontent.com/SkyBulk/a75a32254d58aea2cf27cbb43117a2f4/raw/eb5f85560c0dfd74a4aab9db755ac5a06f0627c2/api.json').results;
setUsers(data);
} catch (err) {
console.error("failed", err);
}
setModalIsOpen(false);
};
fetchUsers();
}, []);
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={handleOnClick} data={users}/>}
</div>
);
}
modal
import React, { useEffect } from "react";
const Modal = ({ onRequestClose, data }) => {
// Use useEffect to add an event listener to the document
useEffect(() => {
function onKeyDown(event) {
if (event.keyCode === 27) {
// Close the modal when the Escape key is pressed
onRequestClose();
}
}
// Prevent scolling
document.body.style.overflow = "hidden";
document.addEventListener("keydown", onKeyDown);
// Clear things up when unmounting this component
return () => {
document.body.style.overflow = "visible";
document.removeEventListener("keydown", onKeyDown);
};
});
return (
<div className="modal__backdrop">
<div className="modal__container">
<div className="modal-header">
<div className="modal-close" onClick={onRequestClose}>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<div className="job-title-wrapper">
<div className="job-card-title">{data}</div>
</div>
</div>
</div>
);
};
export default Modal;
If I understand this correctly, you want to make a particular API call and set it's data into the modal when the user clicks on a row, correct?
What you are currently doing through your useEffect is simply always fetching the user data every time something in your component updates, which is probably not what you want based off of your description. So you can probably remove that block completely.
Instead, you want that API called to be made whenever you click on the row, so it would seem that the more appropriate place to do so would be on your handleOnClick function. It should look something like this:
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://gist.githubusercontent.com/SkyBulk/a75a32254d58aea2cf27cbb43117a2f4/raw/eb5f85560c0dfd74a4aab9db755ac5a06f0627c2/api.json').results;
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
Like this, the data should be all set in your user state by the time the modal is opened. With these changes, your Demo component should look something like this:
export default function Demo () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://gist.githubusercontent.com/SkyBulk/a75a32254d58aea2cf27cbb43117a2f4/raw/eb5f85560c0dfd74a4aab9db755ac5a06f0627c2/api.json').results;
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/>}
</div>
);
}

Resources