Mat70x7
Mat70x7

Reputation: 132

Svelte component created with JavaScript not receiving reactive updates

I dynamically create a component on button click with the following JS:

<script>
    export let order;

    function toggleModal(an_order){
        modal_open.set(!$modal_open);
        const modal = new OrderModal({
            target: document.querySelector('.modal_container-' + order.id),
            props: {
                order: an_order
            },
        });
    }
</script>

However whenever I update the order object it does not reactively reflect in the created component.

If I put the component directly in the html instead of making it dynamic like this:

<div class="modal_container-{order.id} fixed">
    <OrderModal {order} />
</div>

<div class="max-w-xs w-full overflow-hidden rounded-lg shadow-md bg-white cursor-pointer" on:click="{() => toggleModal(order)}">

Then it works correctly and the elements are reactively updated.

Is there a way to make components created by JavaScript update reactively?

*** Updating to show how it is inserted ***

The function that does the update:

function deleteItem(){
  order.order_items.splice(index, 1);
  $order_list = $order_list;
}

As you can see I explicitly do an assignment to trigger the update which as specified works when the component is not created through javascript.

Upvotes: 1

Views: 642

Answers (2)

dummdidumm
dummdidumm

Reputation: 5426

I don't know what's the context of your update function and where it's located/called from, but when creating a component programmatically/imperatively, triggering a rerender by assignment doesn't work. Instead you need to use the $set method on the component instance (docs: https://svelte.dev/docs#$set):

<script>
    export let order;

    let modal; // <- top level variable so it can be used in the update function
    function toggleModal(an_order){
        modal_open.set(!$modal_open);
        modal = new OrderModal({
            target: document.querySelector('.modal_container-' + order.id),
            props: {
                order: an_order
            },
        });
    }

    // ...
    function deleteItem(){
        order.order_items.splice(index, 1);
        // imperatively update the order
        modal.$set({order: $order_list});
    }

    // if you want updates of the `order` prop to propagate to the modal once it's set, you can do
    $: modal && modal.$set({order: order});
</script>

Upvotes: 1

Stephane Vanraes
Stephane Vanraes

Reputation: 16411

The reason this doesn't work is because the compiler will create all the links between reactive elements during compilation. Your Modal component does not exist at that time so it is not possible to do so. This also explains why adding the Modal directly works.

One way to work around this is by using a store and pass this store into the modal.

In general in Svelte, you will not use the code as you have it. A more Svelte way to do it would be to use a bool to track if the Modal has to be shown or not:

<script>
  let showModal = false
  function toggleModal() {
    showModal = !showModal
  }
</script>

{#if showModal}
  <Modal />
{/if}

Upvotes: 0

Related Questions