0
votes

I am just starting with Svelte. I have a searchbar component and an unique instance of that component: main_searchbar. I want to focus that component from a different component.

So I export input in searchbar.svelte:

<script>
    export let value = '';
    export let input;
</script>

<span class="searchbar">
    <input
        class="search"
        type="text"
        name="search"
        bind:value
        bind:this={input}
    >
</span>

Then I import it in main_searchbar.svelte:

<script context="module">
    import Searchbar from './searchbar.svelte';
    export let obj;
</script>

<Searchbar bind:this={obj}/>

Then I create a button to focus:

<script>
        import {obj} from './main_searchbar.svelte';
        
        function focus() {
            obj.input.focus();
        }
</script>

<button on:click={focus}>Focus</button>

And last the App.svelte:

<script>
    import Main_searchbar from './main_searchbar.svelte'
    import Focus from './focus.svelte'
</script>

<Main_searchbar/>
<Focus/>

Of course this doesnt work: https://svelte.dev/repl/c4eb67950c6240e593173431edb18e1a?version=3.38.2 What is the correct way of doing this in Svelte?

2
How would svelte know what obj points to? If you have more than one instance of searchbar.svelte, which one gets the focus? I think you should either opt for a store or dispatch an event.Connor Low

2 Answers

1
votes

Svelte ships with stores, a pretty neat way to manage global state; with these, you don't have to pass references between components. Here's a simplified solution using a writable store:

store.js

import { writable } from 'svelte/store';
// An observable string value.
export const focused = writable('');

searchbar.svelte

<script>
    import { focused } from './store'
    export let target;
    let input;
    
    // When the value of the "focused" store is equal to target, 
    //    call focus() on the input.
    focused.subscribe((v) => {
        if(v === target) {
            input.focus()
            // Because subscribe only notifies us of changes,
            //   we have to reset the store or the focus button 
            //   will stop working.
            focused.set('');
        }
    })
</script>

<span class="searchbar">
    <input bind:this={input} />
</span>

focus.svelte

<script>
    import { focused } from './store';
    export let targeting;
        
    // Use the .set() method to set the value of our `focused` store,
    //   triggering all our subscribers (like the one in searchbar.svelte).     
    const focus = () => focused.set(targeting)
</script>

<button on:click={focus}>Focus</button>

App.svelte

<script>
    import Searchbar from './searchbar.svelte'
    import Focus from './focus.svelte'

    // target and targeting must match. 
    // This allows you to reuse the focus component!
</script>

<Searchbar target="search" />
<Focus targeting="search" />

REPL

0
votes

To fix your problem you can add the following line to Searchbar.svelte

<svelte:options accessors/>

But something feels very "un-svelte" about your setup, if I were you I would reconsider the architecture.