Search code examples
javascriptsveltesvelte-3svelte-component

Svelte3: A modal at docroot with dynamic content


I am trying to implement a modal dialog box somewhat typical, but with a few prerequisites that I don't see how to implement the svelte way:

  • It needs to sit at docroot (and be free of parents components with overflow hidden for ex)
  • I need to be able to call it from any child component.
  • The content needs to be dynamic; HTML, another component with props, binds... whatever svelte usually allows you to do...
  • Some of the components I'll render in the modal use context from parents (that are not in the docroot...)

Now I've tried a component with a slot, but I can't create them dynamically from anywhere and have the modal sit at docroot, I've also tried creating it at docroot with an exported openDialog() function that takes components as argument, but I don't see how to pass a component (with props, binds ect...) to a function ?

How would you approach this ?


Solution

  • The content needs to be dynamic; HTML, another component with props, binds... whatever svelte usually allows you to do...

    Have you seen https://svelte.dev/examples/modal? This might be most of what you're looking for. You can also search the web for "svelte modal repl" and you will find a lot of people who have solved the problem in similar ways.

    It needs to sit at docroot (and be free of parents components with overflow hidden for ex)

    You can have your modal sit at document body with some "portal" code that looks like this:

    (Note that you won't get normal JS event bubbling)

    <script>
      import { onMount } from 'svelte'
    
      let modal;
    
      onMount(() => {
        if (typeof document !== 'undefined') document.body.appendChild(modal);
      })
    </script>
    
    <div class="modal" bind:this={modal}>
      <slot/>
    </div>
    

    I need to be able to call it from any child component.

    Not sure what you exactly mean by "child component", but if you export a show() function, then you can pass that as a prop to any child components. For example:

    <!-- Modal.svelte -->
    <script>
      export function show() { /** ... */ }
    </script>
    
    <div class="modal">
      <slot/>
    </div>
    
    <!-- Child.svelte -->
    <script>
      export let showModal;
    <script/>
    
    
    <!-- Parent.svelte -->
    <script>
      import Modal from './Modal.svelte'
      import Child from './Child.svelte'
    
      let modal;
    </script>
    
    <Modal bind:this={modal}>
      <Child showModal={() => modal.show()}/>
    </Modal>
    

    Some of the components I'll render in the modal use context from parents (that are not in the docroot...)

    The Svelte context API should work fine with the rest of this code.