Search code examples
javascriptcomponentssveltesveltekitsvelte-component

I have a question about the svelte radio component


Radio components are being made and used. However, the input tag cannot be found because the parent page onMount runs earlier than the logic in the component.

Is there any way to disable onMount on the parent page until the logic of the radio common component ends?

// parent page
import { onMount } from 'svelte';
import Radio from '$comp/formObject/RadioCode.svelte';

onMount(()=>{
        let checked = document.getElementsByName('apple')[0].checked;  // ========> error!!!!!
        if(checked == true){
              document.getElementById('apple_txt').disabled = true;
        }
        
})

<Radio id="apple" name="apple" grpCd="fruit_CD"/>
<input id="apple_txt" name="apple_txt" type="text"/>
<!-- RadioCode.svelte -->
<script>
    export let id='';
    export let name='';
    export let grpCd='';
    export let radioList= [];
    
    async function radioData {
        const param = new FormData();
        param.append('grpCd', grpCd);
        
        const response = await Api.post('Controller Url', param);   
        radioList= response.data;
    }
</script>
{#await radioData () then radioList}
    {#each radioList as dtlCd}
            <label for={id}>
                    <input
                        id={id + dtlCd.dtlCd}
                        type="radio"
                        {name}
                        style="width: {width}"
                        value={dtlCd.dtlCd}
                        checked="checked"
                    />{dtlCd.dtlNm} 
                        </label>

    {/each}
{/await}    

I want to control it from the outside as much as possible...


Solution

  • You are not supposed to manipulate the DOM directly like this when using Svelte.

    Either expose a property and bind that or use an event/callback to notify the parent (and send the data along with it).

    For radios and checkboxes, bind:group can be used if all elements belonging together are created in one component. E.g.

    <script>
        import Radio from './Radio.svelte';
        let selected;
    </script>
    
    <Radio name="apple" bind:selected />
    <input name="apple_txt" type="text" disabled={selected == 1} />
    
    <!-- Radio.svelte -->
    <script>
        export let selected;
        export let name;
    
        async function radioData() {
            await new Promise(r => setTimeout(r, 1000));
            // dummy data
            const data = [
                { dtlCd: 1, dtlNm: 'Item 1' },
                { dtlCd: 2, dtlNm: 'Item 2' },
                { dtlCd: 3, dtlNm: 'Item 3' },
            ]
            // either select from data, e.g. via index, or provide with data
            selected = 1;
    
            return data;
        }
    </script>
    
    {#await radioData() then radioList}
        {#each radioList as dtlCd}
            <label>
                <input
                    type="radio"
                    {name}
                    value={dtlCd.dtlCd}
                    bind:group={selected} />
                {dtlCd.dtlNm}
         </label>
        {/each}
    {/await}
    

    REPL

    This will consistently keep the state in sync, if you just want to get the state once using a callback or event might be better.

    <Radio name="apple" onload={s => selected = s} />
    
    // add to Radio.svelte
    export let onload;
    
    async function radioData() {
        // ...
    
        // either select from data, e.g. via index, or provide with data
        selected = 1;
        onload?.(selected);
    
        return data;
    }
    

    REPL

    (Direct callbacks will be the default way to deal with events in Svelte 5.)