Для вложенных компонентов Svlete, где Outer
содержит несколько Inner
, можно ли создать диспетчер с createEventDispatcher
в Inner
, который будет связываться с экземпляром, вызвавшим диспетчер?
Outer.svelte будет выглядеть примерно так
<script lang = "ts">
import Inner from "./Inner.svelte";
/* placeholder array of 0-10 elements, to make it clear the number of Inners
* in this component is different depedning on some outside factors */
let dynamicContent = Array.from({length: Math.random() * 10}, () => "something");
// How is it possible to set this?
let lastSelectedInner: Inner | null = null;
</script>
{#each dynamicContent as item}
<Inner on:select = {(event) => lastSelectedInner = event.detail} />
{/each}
И Inner.svelte будет выглядеть примерно так:
<script lang = "ts>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
</script>
<button on:click = {() => dispatch("select", /* how do I forward the instance here? */)} >
<p>Click me</p>
</button>
Примечание. Мне нужно, чтобы фактический экземпляр находился в переменной, а не в индексе. Кроме того, я не хочу иметь массив всех Inner
, а только одну ссылку на один компонент.
🤔 А знаете ли вы, что...
JavaScript является одним из трех основных языков веб-разработки, вместе с HTML и CSS.
Судя по вашему комментарию, похоже, что экземпляру родительского компонента на самом деле не нужен доступ к экземпляру дочернего компонента, а только (некоторые) к данным в экземпляре дочернего компонента. Таким образом, экземпляр дочернего компонента может передавать эти данные в событии, а не сам экземпляр компонента. А может быть передача данных вообще не нужна?
Внешний.стройный
<script lang = "ts">
import Inner from "./Inner.svelte";
let items = [{
id: 1,
name: `a`,
}, {
id: 2,
name: `b`,
}]
let selectedItem: Object | null = null; // I don't know TS, so unsure about type ^^'
</script>
{#each items as item}
<Inner on:select = {(event) => selectedItem = item} />
{/each}
{#if selectedItem}
<h1>Selected</h1>
<div>Id: {selectedItem.id}</div>
<div>Name: {selectedItem.name}</div>
{/if}
Насколько я знаю, дочерний компонент не может получить доступ к ссылке на себя, но его нужно будет создать через bind:this
в родительском компоненте и передать как реквизит.
Это был бы способ установить lastSelectedInner
без добавления дополнительной логики в компонент Inner
.
<script>
import Inner from "./Inner.svelte";
/* placeholder array of 0-10 elements, to make it clear the number of Inners
* in this component is different depedning on some outside factors */
let dynamicContent = Array.from({length: Math.random() * 10}, () => "something");
let innerRefs = []
// How is it possible to set this?
let lastSelectedInner = null;
$: console.info(lastSelectedInner)
</script>
{#each dynamicContent as item, index}
<Inner bind:this = {innerRefs[index]}
on:select = {() => lastSelectedInner = innerRefs[index]} />
{/each}
После того, как было предложено несколько решений с хорошими обходными путями, я начал искать дополнительные варианты, которые полностью удовлетворяли бы требованию «отсутствия массива внутренних элементов» и «передачи фактической ссылки на компонент».
Я наткнулся на Svelte REPL (не знаю, кто автор), который показывает, как сделать это возможным с помощью специального элемента <svelte:self>. Вот изложенное там решение, адаптированное к этому вопросу. Inner.svelte
:
<script lang = "ts">
export let outer = true;
export let component = null;
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
</script>
{#if outer}
<svelte:self
bind:this = {component}
outer = {false}
on:select = {() => dispatch("select", component)}
/>
{/if}
{#if !outer}
<button on:click = {() => dispatch("select")} >
<p>Click me</p>
</button>
{/if}
Этот подход не самый читаемый, поскольку он требует вложения внутреннего компонента внутрь себя и условного отображения содержимого только во вложенном компоненте. Тем не менее, он достигает именно того результата, который позволяет отправлять событие из компонента, который в деталях передает одну правильно типизированную ссылку.
Обновлено: обновлено в соответствии с комментарием @Corrl.