Grid Tutorials with htmx - what is the proposed solution?

112 views
Skip to first unread message

donal...@gmail.com

unread,
Jun 1, 2025, 12:07:44 PM6/1/25
to py4web
It seems the 'upgraded' grid now doesn't support htmx ie 

from py4web.utils.grid import AttributesPluginHtmx

is not an option anymore.  Most of Jim's grid tutorials were using this feature quite a bit - so keen to undersand what the proposed way forward is for this?

Donald

Jorge Salvat

unread,
Jun 20, 2025, 10:34:29 PM6/20/25
to py4web
True, new  Grid.py  needs to copy this code at the end:


class AttributesPlugin:
    def __init__(self, target_element=None):
        self.target_element = target_element
        self.default_attrs = {}

    def form(self, url):
        attrs = copy.copy(self.default_attrs)
        return attrs

    def link(self, url):
        attrs = copy.copy(self.default_attrs)
        attrs["_href"] = url
        return attrs

    def confirm(self, message):
        attrs = copy.copy(self.default_attrs)
        attrs["_onclick"] = "if(!confirm('%s')) return false;" % message
        return attrs


class AttributesPluginHtmx(AttributesPlugin):
    def __init__(self, target_element):
        super().__init__(target_element)
        self.default_attrs = {
            "_hx-target": self.target_element,
            "_hx-swap": "innerHTML",
        }

    def form(self, url):
        attrs = copy.copy(self.default_attrs)
        attrs["_hx-post"] = url
        attrs["_hx-encoding"] = "multipart/form-data"
        return attrs

    def link(self, url):
        attrs = copy.copy(self.default_attrs)
        attrs["_hx-get"] = url
        return attrs

    def confirm(self, message):
        attrs = copy.copy(self.default_attrs)
        attrs["_hx-confirm"] = message
        return attrs

Massimo

unread,
Jun 21, 2025, 3:14:03 AM6/21/25
to py4web
I am not very skilled in html but I fixed  Jim's HTMX examples and I believe they work without the need for those classes.
Is there any example that is not working as intended?
If you tell me what you are trying to accomplish with htmx, I can probably provide an example, or perhaps I am wrong and I am missing something important.

Massimo

unread,
Jun 21, 2025, 8:13:36 PM6/21/25
to py4web
GridActionButton has been replaced with a single dict, for example:

     GridActionButton(
         url=URL("somewhere", row.id),
         text=f"click me",
         icon="fa-redo",
         message=f"are you sure?",
         append_id=True,
     )

with

     {
         "text": f"click me",
         "url": URL("somewhere", row.id),
         "icon": "fa-redo",
         "_tooltop": "will do something",
         "_onclick": "confirm('are you sure?')",
     )

Only text and url are required.
Using _ you can add any attribute you want to the generated button.

You can add htmx attributes as well.

     {
         "text": f"click me",
         "url": URL("somewhere", row.id),
         "_hx-confirm": "are you sure?",
     }

Also notice that I recommend adding hx-boost="true" to the div wrapping the grid load

        <div id="htmx-target" hx-boost="true" hx-target="#htmx-target">
            <div hx-get="[[=URL('my-grid-page')) ]]" hx-trigger="load">
               Loading ....
            </div>
         </div>

I have not tried building much with htmx but what I tried worked. 

My belief is that py4web should be transparent to HTMX and that is what I tried to achieve.
We do not want to break things because py4web or htmx change and are coupled.

I think as long as you have a way to pass any attribute you want using dicts you should be fine.

Jorge Salvat

unread,
Jun 23, 2025, 11:31:36 AM6/23/25
to py4web
Hi Massimo,

If I have several  hx-target  in the same parent.html  page, how do I tell  Grid  which target applies to each child.html ?
Before I used this:

    attrs = {
        "_hx-get": URL("enlaces_child", vars=dict(parent_id=actor_id)),
        "_hx-target": "#enlaces-target",
        "_class": "button is-default",
    }
    grid.attributes_plugin = AttributesPluginHtmx("#enlaces-target")


Thanks,
Jorge

Massimo

unread,
Jun 24, 2025, 1:19:42 AM6/24/25
to py4web
Something like this?

controllers.py

@action("parent")
@action.uses(auth, "parent.html")
def _():
return locals()

@action("child1")
@action.uses(auth)
def _():
return Grid(db.thing).xml()

@action("child2")
@action.uses(auth)
def _():
return Grid(db.thing).xml()

parent.html

[[extend "layout.html"]]

<h1>Hello To Parent</h1>

<div id="enlaces-target1" hx-boost="true" hx-target="#enlaces-target1">
<div hx-get="[[=URL('child1') ]]" hx-trigger="load">
Loading ....
</div>
</div>

<div id="enlaces-target2" hx-boost="true" hx-target="#enlaces-target2">
<div hx-get="[[=URL('child2') ]]" hx-trigger="load">
Loading ....
</div>
</div>

[[block page_scripts]]<script src="https://unpkg.com/htmx...@1.9.6"></script>[[end]]

You can then add buttons which have their own targets in two different ways:

@action("child1")
@action.uses(auth)
def _():
buttons = [
# make grid build the button
lambda row: {"text": "clickme1", "url": f"http://example.com/{row.id}", "hx-boost": "false"},
# make the button yourself
lambda row: A("click me2", _href=f"http://example.com/{row.id}", **{"hx-boost": "false"})
]
return Grid(db.thing, post_action_buttons=buttons).xml()

Notice that there is a bug in grid.py (*btn) -> (**btn) which is fixed now in master.


Reply all
Reply to author
Forward
Message has been deleted
0 new messages