Using Alpine.js x-on events on Astro.js component - javascript

I'm trying to build some simple stuff with Astro.js for HTML templates and Alpine.js for interactivity when needed. I stumbled upon problem of trying to use Alpine x-on events on Astro component
Here is sample usage of the Button component:
<div x-data>
<Button #click="console.log('Clicked astro')">Click me</Button>
<button #click="console.log('Clicked normal button')">Click me</button>
</div>
The first one doesn't work while the native button works as normal.
Button component is just a button element with some tailwind classes, nothing fancy:
<button class="border-[1px] rounded-lg transition px-6 py-2 hover:text-inherit hover:border-transparent">
<slot />
</button>
How can I get it to work? I really love astro with alpine so far but this little thing is stopping me from using it for my project.
I tried various of different things but none of them worked really. I went through docs and couldn't find anything related to my issue - it feels like there must a simple solution but I'm missing something.

I have reached out to Astro support on discord and it was answered by user happydev there:
Anything passed to an astro component like this is taken as a prop, so you need to pass it to the element you want to bind the event on
Something like this
//Button.astro
---
const {"#click": click} = Astro.props
---
<button #click={click}>
<slot/>
</button>

Related

How to access a property or a method of alpine.js parent component from a child component?

This is kind of an example scenario what the problem looks like,
<div x-data="{ count : 0 }">
<div x-data>
<span x-text="count"></span>
<button x-on:click="count++">Increment</button>
<button x-on:click="count--">Decrement</button>
</div>
</div>
It would be able to increase/decrease the data count from the child component.
I thought of handling it through dispatching custom events using $dispatch() but then again in terms of design, I might need to write listeners on both parent and child component which make the logic more complex since it should be reactive as well.
There was a Github issue, and none of the proposed solutions was working.
I thought of handling it through dispatching custom events using $dispatch() but then again in terms of design, I might need to write listeners on both parent and child component which make the logic more complex since it should be reactive as well.
This is the crux of the issue, in order to do parent-child and child-parent communication you'll need to use events. In the case of child -> parent, you'll trigger increment and decrement events (which will be listened to in the parent component using x-on:increment and x-on:decrement). In the case of parent -> child, you'll need to use $watch to trigger updates whenever count updates (I'll used the new-count event name), this will be listened to on the window from the child component using x-on:new-count.window.
Here's the full working solution (see it as a CodePen):
<div
x-data="{ count : 0 }"
x-init="$watch('count', val => $dispatch('new-count', val))"
x-on:increment="count++"
x-on:decrement="count--"
>
<div>In root component: <span x-text="count"></span></div>
<div
x-data="{ count: 0 }"
x-on:new-count.window="count = $event.detail"
>
<div>In nested component <span x-text="count"></span></div>
<button x-on:click="$dispatch('increment')">Increment</button>
<button x-on:click="$dispatch('decrement')">Decrement</button>
</div>
</div>
In the case you've presented, the count state might be better served by using a global store that integrates with Alpine.js such as Spruce, in which case we'll read and update a shared global store to which both the parent and child components are subscribed (see the Spruce docs). You can find the working example in the following CodePen.
<div x-data x-subscribe="count">
<div>In root component: <span x-text="$store.count"></span></div>
<div x-data x-subscribe="count">
<div>In nested component <span x-text="$store.count"></span></div>
<button x-on:click="$store.count ++">Increment</button>
<button x-on:click="$store.count--">Decrement</button>
</div>
</div>
<script>
Spruce.store('count', 0);
</script>
The final solution that should be mentioned is that removing the nested component would mean that the count increment and decrement would work as expected. Obviously this example was probably simplified & meant to be illustrative, so this solution might not work in a lot of cases. Note: the only difference is removing the second x-data.
<div x-data="{ count : 0 }">
<div>
<span x-text="count"></span>
<button x-on:click="count++">Increment</button>
<button x-on:click="count--">Decrement</button>
</div>
</div>
Another way is to use the magic $store 👉as a Global State:
<div x-data x-init={Alpine.store('count', 0)}>
<div x-data>
<span x-text="$store.count"></span>
<button x-on:click="$store.count++">Increment</button>
<button x-on:click="$store.count--">Decrement</button>
</div>
</div>

Setting raised = true to an ember button when on current page/template

<div class="flex">
<div class="layout-row layout-align-end-center">
{{#paper-button href="overview" raised=true primary=true}}Overview{{/paper-button}}
{{#paper-button href="incomes"}}Incomes{{/paper-button}}
{{#paper-button href="expenses"}}Expenses{{/paper-button}}
{{#paper-button href="settings"}}Settings{{/paper-button}}
</div>
</div>
So I am trying to set the raised=true whenever I am on certain page/template. The above code is from my application template. Is there any way of doing this using JS/Ember or should I just do it manually in each of my templates?
I think the best way to go is to write a custom component wrapping paper-button. ember-route-helpers gives you nice helpers to do that:
{{#paper-button
onclick={{transition-to #route)
href=(url-for #route)
raised=(is-active #route)
primary=(is-active #route)
}}
{{yield}}
{{/paper-button}}
Then you can use that component with {{#your-component route="settings"}}Settings{{/your-component}}.
It's important to understand that you should pass both the action and the href. Because then when people click on the button it will make a transition and dont reload the entire page, but rightlick->open and screenreaders are are not broken.

How can I configure facebook plugin for vuejs with slots

I need to integrate facebook plugin into my vuejs application. I wanted to use
vue-facebook-login-component plugin but I am confused at some points. I can not change text, it doesnt have a text prop, instead it has a text slot. And their slot example doesnt have a api-id which needed for facebook login.
They have to slot example but I could not understand how to use it, never used slot before. I searched google but still can not make it.
When I used in my code, slot-scope="scope" gives scope is defined but never used error, so my first question is how will I use it ?
Another issues is, in slot example it doesnt have app-id, I confused where to put id when I use slot, and how to get login-logout information.
this one works but I can not change login/logoutworking texts.
<v-facebook-login app-id="966242223397117"></v-facebook-login>
this is the example with slot given on the plugin page
<template>
<v-facebook-login-scope>
<button slot-scope="scope">
<!-- Compose HTML/CSS here, otherwise nothing will be rendered! -->
</button>
</v-facebook-login-scope>
</template>
and this is my original button code where I want to integare facebook login. How will I use his code in above code-with-slot ? and where will I put app-id ? Here it is a more detailed code with slot from the plugin and it doesnt have api-id either.
https://github.com/adi518/vue-facebook-login-component/blob/master/src/components/FBLogin.vue
<v-button
class="gray fw shadow-none login-box__submit"
title="FACEBOOK Ä°LE GÄ°RÄ°Åž YAP"
/>
so what I tried is directly putting my button inside of it (just giving class doesnt work)
This brings button with my button class but stays connecting and never bring login
<template>
<v-facebook-login-scope>
<button slot-scope="scope">
<!-- Compose HTML/CSS here, otherwise nothing will be rendered! -->
<v-button
class="gray fw shadow-none login-box__submit"
title="FACEBOOK Ä°LE GÄ°RÄ°Åž YAP"
/>
</button>
</v-facebook-login-scope>
</template>
Try this
<v-facebook-login app-id="966242223397117">
<span slot="login">FACEBOOK Ä°LE GÄ°RÄ°Åž YAP</span>
</v-facebook-login>

Create a base component to be reused when creating new components

I'm putting together an application in which there are many modals. As I do not want to repeat the code of the modal, I want to assemble a base component that has the minimum structure and then with that structure to be able to assemble the different modals and to carry what I need inside (form, text, images)
An example of what I am looking to do
<app-modal-base>
<app-form></app-form>
<app-modal-base>
I hope you understand what I'm looking for. In case you can not, someone found an alternative solution?
Thanks
In your base modal template, include the <ng-content></ng-content> tag. When you display your modal, you can use it as follows:
<Modal>
<div id="mydiv">
<p> Simple paragraph </p>
<form>...</form>
</div>
</Modal>
The modal will include what you have included between the <Modal></Modal> tags at the place where the <ng-content></ng-content> tags are in the template for the base modal component. It would look like:
template: `
<div id="closeButton"></div>
<ng-content></ng-content>
`
This information is gathered from this source, and I can't seem to find official docs about this. You might have to try it out.

AngularJS 1.2 ngInclude inside ngIf

I'm running into some odd behavior when putting an ngInclude inside an ngIf or ngSwitch.
For example, take the following:
<button ng-click="showIncTemplate = !showIncTemplate">Toggle Included Template</button>
<button ng-click="showInlineTemplate = !showInlineTemplate">Toggle Inline Template</button>
<div ng-if="showIncTemplate">
<p>Included template:</p>
<div ng-include="'template.html'"></div>
</div>
<div ng-if="showInlineTemplate">
<h1>Inline Template</h1>
</div>
(http://plnkr.co/edit/gULbwnKb0gQS8DWz0V6U)
The buttons toggle an options to render the divs that follow. The inline example behaves as expected, with the content appearing or disappearing on click.
The div with the child include seems not to include the template when first drawn, but then includes it repeatedly on every subsequent redraw.
What's going on here? I do see some breaking changes around ngInclude, is there some other way I should be doing this? Or is this a bug in Angular?
Edit:
It looks like this is already in the angularjs github issue tracker:
https://github.com/angular/angular.js/issues/3627
They've fixed it in this snapshot:
http://code.angularjs.org/snapshot/

Categories

Resources