solid js - Phát hiện nhấp chuột bên ngoài và thay đổi trạng thái của component trên Typescript (SolidJS)
Tôi học framework SolidJS và gặp vấn đề. Tôi muốn thay đổi trạng thái của phần tử bằng các chỉ thị, nhưng nó không hoạt động. Tôi làm sai chỗ nào? Toàn bộ dự án - https://github.com/XPahanX/editor (không phải phiên bản hiện tại)
Thành phần :
import { Component, createSignal, ParentComponent, Show } from 'solid-js';
import clickOutside from '../directives/click-outside';
clickOutside;
const EditorBlockMenu: Component<{fun: () => false}> = (prop) => {
return (
<div class='editor-block-menu' use:clickOutside={prop.fun}>
<div><button>Delete</button></div>
<div><button>Move Up</button></div>
<div><button>Move Down</button></div>
</div>
)
}
const EditorBlockBtn: ParentComponent = () => {
const [toggle, setToggle] = createSignal(false)
const isToggle = () => setToggle(!toggle())
return (
<div classList={{'editor-block-btn': !toggle()}}>
<button onClick={isToggle}>
</button>
<Show
when={toggle()}
fallback={<></>}
>
<EditorBlockMenu fun={() => setToggle(false)}/>
</Show>
</div>
)
}
export default EditorBlockBtn;
Chỉ thị :
import { onCleanup } from "solid-js";
// https://www.solidjs.com/tutorial/bindings_directives?solved
// need fix
export default function clickOutside(el:Element, accessor: () => false) {
const onClick = (e: Event) =>
!el.contains(e.target as Node) && accessor();
document.body.addEventListener("click", onClick);
onCleanup(() => document.body.removeEventListener("click", onClick));
}
declare module "solid-js" {
namespace JSX {
interface Directives {
clickOutside: () => false;
}
}
}
Theo như tôi hiểu, hàm setToggle() không hoạt động trong fun={}. Tôi không mạnh về Typescript, tôi hy vọng nhận được sự giúp đỡ của bạn.
Tôi sử dụng câu trả lời đầu tiên, nhưng nhận được một lỗi:
const EditorBlockMenu: Component = () => {
return (
<div class='editor-block-menu'>
<div><button>Delete</button></div>
<div><button>Move Up</button></div>
<div><button>Move Down</button></div>
</div>
)
}
const EditorBlockBtn: ParentComponent = () => {
const [toggle, setToggle] = createSignal(false)
const isToggle = () => setToggle(!toggle())
let ref: HTMLDivElement
const handleClick = (event: MouseEvent) => {
if(!ref.contains(event.target as Node)) { // withuot `as Node` highlights in red
setToggle(false)
}
}
onMount(() => {
document.addEventListener('click', handleClick);
})
onCleanup(() => {
document.removeEventListener('click', handleClick);
})
return (
<div classList={{'editor-block-btn': !toggle()}}>
<button onClick={isToggle}>
</button>
<Show
when={toggle()}
fallback={<></>}
>
<EditorBlockMenu ref={ref!}/>
</Show>
</div>
)
}
export default EditorBlockBtn;
Hình ảnh
Câu trả lời #1
Có một hướng dẫn trên trang web của SolidJS minh họa chính xác cùng một trường hợp sử dụng:
https://www.solidjs.com/tutorial/bindings_directives
Các chỉ thị thì tốt khi bạn sao chép một chức năng cho nhiều phần tử, nếu không bạn có thể tự triển khai nó trực tiếp bên trong bất kỳ thành phần nào:
import { createSignal, onCleanup, onMount } from 'solid-js';
export const App = () => {
let ref: HTMLDivElement;
const [show, setShow] = createSignal(true);
const handleClick = (event: MouseEvent) => {
if(!ref.contains(event.target)) {
setShow(false);
};
};
onMount(() => {
document.addEventListener('click', handleClick);
});
onCleanup(() => {
document.removeEventListener('click', handleClick);
});
return (
<div ref={ref!}>
Visible: {show() ? 'true': 'false'}
</div>
);
};
https://playground.solidjs.com/anonymous/c933b32f-67cb-45e9-843c-6555144c33f5
Câu trả lời #2
Nói chung, sau vài lần thử và sai, tôi đã quyết định một phương pháp mới mà có hiệu quả. Tất nhiên, tôi đã đi lệch khỏi con đường đã định ban đầu, nhưng tôi nghĩ không có giải pháp nào tốt hơn cho nhiệm vụ này.
const EditorBlockBtn: ParentComponent = () => {
const [toggle, setToggle] = createSignal(false)
const isToggle = () => setToggle(!toggle())
let divRef: HTMLDivElement
let btnRef: HTMLButtonElement
const handleClick = (event: MouseEvent) => {
if(!divRef.contains(event.target as Node) && !btnRef.contains(event.target as Node)) {
setToggle(false)
}
}
onMount(() => {
document.addEventListener('click', handleClick)
})
onCleanup(() => {
document.removeEventListener('click', handleClick)
})
return (
<div classList={{'editor-block-btn': !toggle()}}>
<button onClick={isToggle} ref={btnRef!}>
</button>
<Show
when={toggle()}
fallback={<></>}
>
<div class='editor-block-menu' ref={divRef!}>
<div><button>Delete</button></div>
<div><button>Move Up</button></div>
<div><button>Move Down</button></div>
</div>
</Show>
</div>
)
}
export default EditorBlockBtn;
