CWD Subs
CWD Subs

Reputation: 350

Refreshing data after-the-fact in AlpineJS

I'm using Alpine to display a list of items that will change. But I can't figure out how to tell Alpine to refresh the list of items once a new one comes back from the server:

<div x-data=" items() ">
    <template x-for=" item in items " :key=" item ">
        <div x-text=" item.name "></div>
    </template>
</div>

The first "batch" of items is fine, because they're hard-coded in the items() function:

    function items(){
        return {
            items: [
                { name: 'aaron'  },
                { name: 'becky'  },
                { name: 'claude' },
                { name: 'david'  }
            ]
        };
    }

Some code outside of Alpine fetches and receives a completely new list of items, that I want to display instead of the original set. I can't figure out how, or if it's even currently possible. Thanks for any pointer.

Upvotes: 19

Views: 26336

Answers (2)

oehmaddin
oehmaddin

Reputation: 180

Expanding on Hugo's great answer I've implemented a simple patch method that lets you update your app's state from the outside while keeping it reactive:

<div x-data="app()" x-on:patch.window="patch">
  <h1 x-text="headline"></h1>
</div>
function app(){
  window.model = {
    headline: "some initial value",
    patch(payloadOrEvent){
      if(payloadOrEvent instanceof CustomEvent){
        for(const key in payloadOrEvent.detail){
          this[key] = payloadOrEvent.detail[key];
        }
      }else{
        window.dispatchEvent(new CustomEvent("patch", {
          detail: payloadOrEvent
        }));
      }
    }
  };
  return window.model;
}

In your other, non-related script you can then call

window.model.patch({headline : 'a new value!'});

or, if you don't want assign alpine's data model to the window, you can simply fire the event, as in Hugo's answer above:

window.dispatchEvent(new CustomEvent("patch", {
  detail: {headline : 'headline directly set by event!'}
}));

Upvotes: 6

Hugo
Hugo

Reputation: 3888

There are 3 ways to solve this.

  1. Move the fetch into the Alpine.js context so that it can update this.items
  function items(){
        return {
            items: [
                { name: 'aaron'  },
                { name: 'becky'  },
                { name: 'claude' },
                { name: 'david'  }
            ],
            updateItems() {
              // something, likely using fetch('/your-data-url').then((res) => )
              this.items = newItems;
            }
        };
    }
  1. (Not recommended) From your JavaScript code, access rootElement.__x.$data and set __x.$data.items = someValue
<script>
  // some other script on the page
  // using querySelector assumes there's only 1 Alpine component
  document.querySelector('[x-data]').__x.$data.items = [];
</script>
  1. Trigger an event from your JavaScript and listen to it from your Alpine.js component.

Update to the Alpine.js component, note x-on:items-load.window="items = $event.detail.items":

<div x-data=" items() " x-on:items-load.window="items = $event.detail.items">
    <template x-for=" item in items " :key=" item ">
        <div x-text=" item.name "></div>
    </template>
</div>

Code to trigger a custom event, you'll need to fill in the payload.

<script>
let event = new CustomEvent("items-load", {
  detail: {
    items: []
  }
});
window.dispatchEvent(event);
</script>

Upvotes: 44

Related Questions