Reputation: 11
I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up
.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>
's should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up
css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
is-signed-up
css-class to the corresponding <button>
to show its status to the user.I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2'
, but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! 😊
Upvotes: 0
Views: 531
Reputation: 10502
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter
component using Alpine.data()
creates the data (subs
array), provides the toggling method (toggle_subscription(which)
) and the checking method (is_subscribed(which)
) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init()
method that executes automatically after the component is initialized. I have also created a save
method that we can use to send the subscription list back to the backend.
The second component, subButton
with Alpine.bind()
is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription
with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red
CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed
method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/[email protected]/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button @click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'@click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>
Upvotes: 2