imacrayon / alpine-ajax Goto Github PK
View Code? Open in Web Editor NEWAn Alpine.js plugin for building server-powered frontends.
Home Page: https://alpine-ajax.js.org
License: MIT License
An Alpine.js plugin for building server-powered frontends.
Home Page: https://alpine-ajax.js.org
License: MIT License
Hi, I was playing with this today, and I like it so far, so thank you :)
If I enhance links (a la wire:navigate
or htmx-boost
) or use alpine ajax for filtering (involving get queries), is it on me to manually manage history / push/replace URLs, or do I have an error in the code? I haven't found anything about the history in the docs
<form method=GET @submit='
$event.submitter.disabled = true
$ajax("/user")
.finally(() => $event.submitter.disabled = false)
'>
<input type=text name=username value=myinput>
<button>Submit</button>
</form>
It still send GET but it send FormData instead of /user?username=myinput
(with empty body).
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method
I brought up headers whilst comparing alpine-ajax versus htmx with my team, but found nothing in the docs. However checking the source code reveals the x-alpine-request
header:
Line 123 in e84170c
It would be good to document it.
Curious about this, also because x-arrange will never be used without x-target would it be x-target.prepend.focus:foo
Hey, first of all I want to say that this is a great project. HTMX is great but this feels so easier to use especially if one is already using alpinejs.
I was wondering if there is a recommended way to handle sse and websockets just like there are extensions in HTMX ?
Another nice thing would be something like hx-boost so that no extra library is needed to create an spa like feel when navigating to different urls.
for reference:
It seems that the current version of alpine-ajax doesn't work if the element to be replaced is a tr.
I believe this is because of this line:
Line 283 in f0d0bbb
Specifically, creating the fragment doesn't work:
document.createRange().createContextualFragment('<tr id="someid"></tr>').getElementById("someid")
return null.
So when we try to update a tr
, we get "Target #someid not found in AJAX response."
Hi @imacrayon
Just wondering whether you can provide me a suggestion, if I am using alpine, and I need web component for it, which framework or library I can use to build with?
Like web component as template, and I can use it together, something like that.
Thanks
Eric Xin
Infinite scroll is tricky because it requires that we append new server content rather than replace it. In the interest of keeping things simple I intended to allow only replacing DOM content in Apline AJAX, but it looks like we might need to support additional ways to add content to the page.
I'd like for this project to support a clean way to perform inline validation. I've got a prototype working but it requires some gnarly code in x-data
. I think it will probably need to be handled with a new directive.
Adding this might be a good idea, it would prevent accidental double clicks.
Hi there, and thank you very much for all your effort on this great plugin!
Unfortunately i'm running into the following error on the latest version when using x-target
, seems to be related to a recent feature.
TypeError: can't access property "hasAttribute", event.submitter is null
if (event.submitter.hasAttribute("formnoajax")) {
return;
}
I'm a JavaScript package noob and I've been manually building and committing the /dist
directory with each release. I'd like some help with a GitHub Action to take care of the builds so we can add /dist
to the .gitignore
.
Currently it is not possible to call the $ajax
functions outside of Alpine.js
Would it technically be possible to have exported theese functions so that they will be usable outside of Alpine.js?
Hi @imacrayon
Do you think you would have x-ref available to replace x-target in next version, since I may not have id for target element, but I would have x-ref for target element.
Thanks
Eric Xin
Links added to the DOM after an Ajax component is initialized are not progressively enhanced.
The work around for now is to add thex-ajax
directive to new links. The directive will trigger an Alpine component to re-initialize.
Hi, is this something that could be used with Dropzone.js?
I have a Dropzone form that opens a modal when Dropzone fires the success event. Rather than returning JSON from my backend, parsing it, and updating the Modal info (which is my current approach, and rather tedious), I wanted to use this, to return an html snippet that would fill the modal.
Being a JS novice, I wasn't able to figure out a way to do this. Maybe it's a Dropzone limitation?
Thanks!
Hey @imacrayon,
thank you for all the work you put into this plugin! Really appreciate it!
I found a small bug – I have a form that looks like this:
<form x-init x-target="customer-table" x-target.replace></form>
Everything works as expected, but I get this error in the console:
Uncaught Missing ID: <form x-init="" x-target="customer-table" x-target.replace=""> is missing an ID to target.
When I add an id
to the form, the error disappears. If the ID is required, the docs should probably tell this – but I don't think it's required, because everything works? 🤔
Maybe you can have a look.
TIA & Greetings from Germany!
Not having this option is making it awkward to handle server side html generation for full page refreshes. Is this on the roadmap or is there a reason why this intentionally is left out? This can be an x-merge option.
Hi @imacrayon
Sorry for another question, I am too new to Alpine.
I used to use vue3, and I am tired of doing spc and webpack/vite, I have to use vue3 since SPA.
Alpine seems very similar to vue, but I am not sure how to use Alpine for spa way.
Would you please give me some instructions?
Thanks
Eric Xin
I want to try out Alpine-ajax, but I can't get it to work. I have installed it by including it in the head of my HTML file. All seems to work until I interact with the page, and I get an error:
Uncaught (in promise) Missing Target: #[object HTMLButtonElement] was not found in the current document.
E https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js:1
j https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js:1
j https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js:1
r https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js:1
[cdn.min.js:1:8310](https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js)
r https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js:1
I tried to reproduce the toggle example but this error has stumped me.
I am using Bun and Elysia for serving my code and i made a repository to reproduce this error:
https://github.com/hansaskov1/alpine-ajax-button
The entire source code is also here.
import { Elysia } from "elysia";
import { html } from "@elysiajs/html";
const Head = ({ children }: { children: undefined | {} }) => (
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
{/* Alpine and Alpine Ajax */}
<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
</head>
<body>
{children}
</body>
</html>
);
const LikeButton = ({ id }: { id: string }) => (
<form id="like" x-init x-target method="post" action={`/comments/${id}/like`}>
<button name="id" value={id}>Like</button>
</form>
)
const UnlikeButton = ({ id }: { id: string }) => (
<form id="like" x-init x-target method="delete" action={`/comments/${id}/like`}>
<button name="id" value={id} x-autofocus>Unlike</button>
</form>
)
const app = new Elysia()
.use(html())
.get("/", () => (
<Head>
<LikeButton id={"1"} />
</Head>
))
.delete("/comments/:id/like", ({ params }) => (
<LikeButton id={params.id} />
))
.post("comments/:id/like", ({ params }) => (
<UnlikeButton id={params.id} />
))
.listen(3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
Here is the rendered html from the browser
<html lang="en" class=" mrcjhi idc0_350">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script defer="" src="https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js"></script><script defer="" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
</head>
<body>
<form id="like" x-init="" x-target="button" method="post" action="/comments/1/like">
<button name="id" value="1">Like</button>
</form>
</body>
</html>
Could someone please help me understand and resolve this issue?
Could not find a declaration file for module '@imacrayon/alpine-ajax'. 'c:/Users/Michaylen/Desktop/laravel-app/node_modules/@imacrayon/alpine-ajax/dist/module.cjs.js' implicitly has an 'any' type.
Try npm i --save-dev @types/imacrayon__alpine-ajax
if it exists or add a new declaration (.d.ts) file containing declare module '@imacrayon/alpine-ajax';
ts(7016)
VS Code
A form issuing a GET
request will change the query string in the current URL. It would be good if AJAX-enabled forms would also update the URL so pages work the same way with or without JavaScript.
Maybe allow for a modifier only on GET like x-target.sse
that setup an event source. I'm using SSE in htmx and would like to experiment with a similar approach in "pure"Alpine
listenForSubmit(window)
&& listenForNavigate(window)
both return cleanup functions that are never run.
Based on a conversation with @delaneyj renaming x-arrange
to x-merge
might make it more clear that the attribute is meant to be added to the target, not the initiator.
Hi, how can i make html head updated for each page when i use ajax request in links
If the response is ok, the event ajax:success
get's fired. I use this event to search in the markup for missing Alpine JS Plugins. As the plugins must have been loaded before the markup appears on the page, it would be nice to have a preventDefault
option with a function in details who can be called to continue the work.
Pseudo-Code
window.addEventListener('ajax:success', (event) => {
event.preventDefault();
const html = event.detail.response.html
// do the needed stuff
event.detail.render();
})
Like that It would be even possible the alter the markup:
window.addEventListener('ajax:success', (event) => {
event.preventDefault();
const html = event.detail.response.html
// do the needed stuff
event.detail.render(html);
})
Hey,
is it possible to change the url location on click?
Thanks
Hi,
First, thank you for your work.
I am having trouble with infinite scroll implementation.
As it is, I get a Target #pagination not found in AJAX response.
error logged, and the same page tow loaded again and again as I scroll…
<section id="articles"
class="grid items-start sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6"
x-data x-masonry.wait.100 x-arrange="append">
{{ collection from="journal" sort="date:desc" paginate="25" as="billet" }}
{{ billet }}
{{ partial src="brick" preset="{view:glide_presets | shuffle | first}" }}
{{ /billet }}
{{ paginate }}
<div class="" id="pagination" x-init x-intersect="$ajax('{{ next_page }}', {events: 'true'})"
x-target="articles pagination" @ajax:success="$dispatch('reload:masonry')">
</div>
{{ /paginate }}
{{ /collection }}
</section>
I am using Statamic to display entries, and the way the pagination works, the pagination loop has to be inside the collection entries loop. Or, I have noticed that in your example https://alpine-ajax.js.org/examples/infinite-scroll/, the pagination is outside the target table body where the new elements are loaded and added.
I suppose this explains that, perhaps? But I do not understand why the plugin could not find the #pagination, wherever it is in the markup?
If I duplicate the {{ collection }}
loop, once with the entries, and once with the pagination, outside of the target, it kinda works, but feels "messy".
Any inputs/ideas?
Thanks in advance for your thoughts.
Hi,
I am testing out the lib and stumble upon an unexpected behavior trying to follow the doc.
Setting x-merge=append
or prepend
... does not seem to work.
I added a codepen and loom below. Apologize ahead of time if I miss something obvious
🙏
https://codepen.io/dephiros/pen/dyrGPQN
https://www.loom.com/share/b0d4eecace2447d5a61e73caee930d50?sid=970f5462-e73b-4d05-804b-53fea9e65bc5
The Alpine UI components required the Focus Plugin to be included separately. This project could follow the same pattern the Morph Plugin dependency:
<script defer src="https://unpkg.com/@imacrayon/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://unpkg.com/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
I feel like the Morph Plugin isn't as commonly used as the Focus Plugin and that was my initial reasoning for bundling it in Alpine AJAX, but I'm happy to decouple it if there's enough interest in that.
Based off discussions in #28 it would be nice if Alpine AJAX warned developers when they are targeting two elements where one is nested within the other. Right now you'll see a confusing warning about the child target missing from the response.
Before logging a warning here we can check if the current target is a child of another target.
Hi @imacrayon
First picture is htmx + hyperscript.
Second picture is alpine + alpine ajax
I saw they have difference, hyperscript seems not use the openmodel function, and add class directly, and you have openmodal this function involved, just wondering whether you need to add modal library as well, just not sure where it comes from, and just wondering if I want to use add class that way, is it possible, if that works, how to do?
Thanks
Eric Xin
If a POST send a 302 it should support changing the window.location. This can happen for example with a validated form instead of returning an updated form with errors it sends the user to the newly created resource.
Is there a reason the x-target is found via getElementById
, rather than querySelector
, this allowing use of body
as the target (or even a class like js-whatever-target
?
Hi,
I'm testing the Alpine AJAX in a WordPress project, and would like to know the best way to filter content based on the select box.
Same as here (https://alpine-ajax.js.org/examples/filterable-content/) but with select box instead of buttons.
Thanks.
Hi there, first thanks for this library it is really nice to use and well made!
One of my use case is that, a response from the API would have multiple targets which this allows.
However one might be to replace the form with an empty one, and the other one to add an element to a list let's say.
I am currently returning the full list again then I don't have the issue but was wondering if there's a way with this library to say id X will merge and id Y will replace (default)
Thanks
I found out x-target
cannot be blank (self-target) on <form>
when there is x-merge
. For example:
<form id=user x-init x-target x-merge=after method=post action=/create/users>
<button>Create</button> <!-- Uncaught (in promise) Missing Target: #[object HTMLInputElement] was not found in the current document. -->
</form>
Is this intentional?
(I've not yet test for other x-merge
pattern)
There's a need to add loading indicators when network requests are slow. I think this will require a new directive. My initial thoughts are that it would work like this:
<template x-busy>
<img src="loading.svg" alt="Loading">
</template>
This template content would show only when AJAX is processing a network request. I'm open to bikeshedding other attribute names other than x-busy
.
Hi @imacrayon
If there is multiple targets defined, not sure how the responses look like, would you please give me a picture?
Thanks
Eric Xin
Since v0.1 keyboard focus automatically moves to the first focusable element in a target when that target changes on the page. I think this default may be too heavy-handed.
You can disable focus behavior using initial-focus=“false”
and I’ve found myself using that a lot so that I can have finer focus control.
Maybe the focus behavior should default to “false” and then when you want to focus something you can opt-in by setting initial-focus
?
I am using the Alpine morph plugin to merge new content onto the page. Maybe I'm doing something wrong, but If that content contains new Alpine components, these don't get initialized.
Focus the first autofocus
element in a target when content changes.
People like tabs.
I have been trying to get ChartJS integration working to display host metrics using AJAX requests. However, I have not been able to find examples with this use case. Assume we make an API call that returns JSON data displaying CPU metrics in the following format when making a get request to /nodecpustats:
"KernelSet":true,"Kernel":501435,"UserSet":true,"User":443484,"IdleSet":true,"Idle":112996834,"IowaitSet":true,"Iowait":5197,"IntrSet":true,"Intr":0,"UtilizationSet":true,"Utilization":95011600}
How can we display the Utilization metric as time series data using Alpine+ChartJS? How can we make the chart reactive and smooth scroll? Any help would be appreciated.
I'm trying to consume a wordpress json with alpine js + Swiper Js. But there is an error that I can not identify.
https://codepen.io/johnquimera/pen/mdjPvMO
Can someone help me?
I have a simple form with one input value:
<form x-data="{use_cache: $persist(false)}" x-effect="console.log(use_cache)" x-target="notion" method="post" action="/notion">
<label>
<input type="checkbox" name="use_cache" x-model="use_cache" />
Use cache
</label>
<button>Fetch</button>
</form>
The form seems to use multipart/form-data
instead of the default application/x-www-form-urlencoded
enctype
Would it be possible to set enctype
?
Great project! Django creates a CSRF token that needs to be passed when sending POST requests from the client.
An example of how you might handle this with HTMX would something like the following:
<div class="article-page" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
Is there a way to do something similar with alpine-ajax?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.