Giter Club home page Giter Club logo

laravel-htmx's Introduction

laravel-htmx

Laravel integration for htmx.

Latest Version on Packagist GitHub Tests Action Status Total Downloads

Supported Laravel Versions >= v8.80.0.

Installation

You can install the package via composer:

composer require mauricius/laravel-htmx

You can publish the config file with:

php artisan vendor:publish --tag="laravel-htmx"

This is the contents of the published config file:

return [
];

To install htmx please browse their documentation

Usage

Request

You can resolve an instance of the HtmxRequest from the container which provides shortcuts for reading the htmx-specific request headers.

use Mauricius\LaravelHtmx\Http\HtmxRequest;

Route::get('/', function (HtmxRequest $request)
{
    // always true if the request is performed by Htmx
    $request->isHtmxRequest();
    // indicates that the request is via an element using hx-boost
    $request->isBoosted();
    // the current URL of the browser
    $request->getCurrentUrl();
    // true if the request is for history restoration after a miss in the local history cache
    $request->isHistoryRestoreRequest()
    // the user response to an hx-prompt
    $request->getPromptResponse();
    // 	the id of the target element if it exists
    $request->getTarget();
    // the name of the triggered element if it exists
    $request->getTriggerName();
    // the id of the triggered element if it exists
    $request->getTriggerId();
});

Response

  • HtmxResponseClientRedirect

htmx can trigger a client side redirect when it receives a response with the HX-Redirect header. The HtmxResponseClientRedirect makes it easy to trigger such redirects.

use Mauricius\LaravelHtmx\Http\HtmxResponseClientRedirect;

Route::get('/', function (HtmxRequest $request)
{
    return new HtmxResponseClientRedirect('/somewhere-else');
});
  • HtmxResponseClientRefresh

htmx will trigger a page reload when it receives a response with the HX-Refresh header. HtmxResponseClientRefresh is a custom response class that allows you to send such a response. It takes no arguments, since htmx ignores any content.

use Mauricius\LaravelHtmx\Http\HtmxResponseClientRefresh;

Route::get('/', function (HtmxRequest $request)
{
    return new HtmxResponseClientRefresh();
});
  • HtmxResponseStopPolling

When using a polling trigger, htmx will stop polling when it encounters a response with the special HTTP status code 286. HtmxResponseStopPolling is a custom response class with that status code.

use Mauricius\LaravelHtmx\Http\HtmxResponseStopPolling;

Route::get('/', function (HtmxRequest $request)
{
    return new HtmxResponseStopPolling();
});

For all the remaining available headers you can use the HtmxResponse class.

use Mauricius\LaravelHtmx\Http\HtmxResponse;

Route::get('/', function (HtmxRequest $request)
{
    return with(new HtmxResponse())
        ->location($location) // Allows you to do a client-side redirect that does not do a full page reload
        ->pushUrl($url) // pushes a new url into the history stack
        ->replaceUrl($url) // replaces the current URL in the location bar
        ->reswap($option) // Allows you to specify how the response will be swapped
        ->retarget($selector); // A CSS selector that updates the target of the content update to a different element on the page
});

Additionally, you can trigger client-side events using the addTrigger methods.

use Mauricius\LaravelHtmx\Http\HtmxResponse;

Route::get('/', function (HtmxRequest $request)
{
    return with(new HtmxResponse())
        ->addTrigger("myEvent")
        ->addTriggerAfterSettle("myEventAfterSettle")
        ->addTriggerAfterSwap("myEventAfterSwap");
});

If you want to pass details along with the event you can use the second argument to send a body. It supports strings or arrays.

use Mauricius\LaravelHtmx\Http\HtmxResponse;

Route::get('/', function (HtmxRequest $request)
{
    return with(new HtmxResponse())
        ->addTrigger("showMessage", "Here Is A Message")
        ->addTriggerAfterSettle("showAnotherMessage", [
            "level" => "info",
            "message" => "Here Is A Message"
        ]);
});

You can call those methods multiple times if you want to trigger multiple events.

use Mauricius\LaravelHtmx\Http\HtmxResponse;

Route::get('/', function (HtmxRequest $request)
{
    return with(new HtmxResponse())
        ->addTrigger("event1", "A Message")
        ->addTrigger("event2", "Another message");
});

Render Blade Fragments

This library also provides a basic Blade extension to render template fragments.

The library provides two new Blade directives: @fragment and @endfragment. You can use these directives to specify a block of content within a template and render just that bit of content. For instance:

{{-- /contacts/detail.blade.php  --}}
<html>
    <body>
        <div hx-target="this">
            @fragment("archive-ui")
                @if($contact->archived)
                    <button hx-patch="/contacts/{{ $contact->id }}/unarchive">Unarchive</button>
                @else
                    <button hx-delete="/contacts/{{ $contact->id }}">Archive</button>
                @endif
            @endfragment
        </div>
        <h3>Contact</h3>
        <p>{{ $contact->email }}</p>
    </body>
</html>

With this fragment defined in our template, we can now render either the entire template:

Route::get('/', function ($id) {
    $contact = Contact::find($id);

    return View::make('contacts.detail', compact('contact'));
});

Or we can render only the archive-ui fragment of the template by using the renderFragment macro defined in the \Illuminate\View\View class:

Route::patch('/contacts/{id}/unarchive', function ($id) {
    $contact = Contact::find($id);

    // The following approaches are equivalent

    // Using the View Facade
    return \Illuminate\Support\Facades\View::renderFragment('contacts.detail', 'archive-ui', compact('contact'));

    // Using the view() helper
    return view()->renderFragment('contacts.detail', 'archive-ui', compact('contact'));

    // Using the HtmxResponse Facade
    return \Mauricius\LaravelHtmx\Facades\HtmxResponse::renderFragment('contacts.detail', 'archive-ui', compact('contact'));

    // Using the HtmxResponse class
    return with(new \Mauricius\LaravelHtmx\Http\HtmxResponse())
        ->renderFragment('contacts.detail', 'archive-ui', compact('contact'));
});

OOB Swap support

htmx supports updating multiple targets by returning multiple partial responses with hx-swap-oop. With this library you can return multiple fragments by using the HtmxResponse as a return type.

For instance, let's say that we want to mark a todo as completed using a PATCH request to /todos/{id}. With the same request, we also want to update in the footer how many todos are left:

{{-- /todos.blade.php  --}}
<html>
    <body>
        <main hx-target="this">
            <section>
                <ul class="todo-list">
                    @fragment("todo")
                        <li id="todo-{{ $todo->id }}" @class(['completed' => $todo->done])>
                            <input
                                type="checkbox"
                                class="toggle"
                                hx-patch="/todos/{{ $todo->id }}"
                                @checked($todo->done)
                                hx-target="#todo-{{ $todo->id }}"
                                hx-swap="outerHTML"
                            />
                            {{ $todo->name }}
                        </li>
                    @endfragment
                </ul>
            </section>
            <footer>
                @fragment("todo-count")
                    <span id="todo-count" hx-swap-oob="true">
                        <strong>{{ $left }} items left</strong>
                    </span>
                @endfragment
            </footer>
        </main>
    </body>
</html>

We can use the HtmxResponse to return multiple fragments:

Route::patch('/todos/{id}', function ($id) {
    $todo = Todo::find($id);
    $todo->done = !$todo->done;
    $todo->save();

    $left = Todo::where('done', 0)->count();

    return HtmxResponse::addFragment('todomvc', 'todo', compact('todo'))
        ->addFragment('todomvc', 'todo-count', compact('left'));
});

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

laravel-htmx's People

Contributors

asbiin avatar mauricius avatar robertogallea avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-htmx's Issues

Session Support

Hello,
Thanks for the great package!
Does it also handles the flash sessions?
For example I want to handle the following:

session()->flash('status', 'success');
return back();

composer require not working

composer require mauricius/laravel-htmx
Using version ^0.4.0 for mauricius/laravel-htmx
Root package 'mauricius/laravel-htmx' cannot require itself in its composer.json

Laravel 11 support

Currently when trying to upgrade to Laravel 11 there is a conflict error

Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires mauricius/laravel-htmx ^0.5.0 -> satisfiable by mauricius/laravel-htmx[v0.5.0].
    - Conclusion: don't install laravel/framework v11.0.1 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.2 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.3 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.4 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.5 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.6 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.7 (conflict analysis result)
    - Conclusion: don't install laravel/framework v11.0.0 (conflict analysis result)
    - mauricius/laravel-htmx v0.5.0 requires illuminate/contracts ^8.80|^9.0|^10.0 -> satisfiable by illuminate/contracts[v8.80.0, ..., 8.x-dev, v9.0.0-beta.1, ..., 9.x-dev, v10.0.0, ..., 10.x-dev].
    - Only one of these can be installed: illuminate/contracts[v6.0.0, ..., 6.x-dev, v7.0.0, ..., 7.x-dev, v8.0.0, ..., 8.x-dev, v9.28.0, ..., 9.x-dev, v10.0.0, ..., 10.x-dev, v11.0.0, ..., 11.x-dev], laravel/framework[v11.0.0, ..., 11.x-dev]. laravel/framework replaces illuminate/contracts and thus cannot coexist with it.
    - Root composer.json requires laravel/framework ^11.0 -> satisfiable by laravel/framework[v11.0.0, ..., 11.x-dev].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.```

Request Validation Support

Hello, I was wondering if you plan on overriding or providing any kind of support for default Laravel request validation, ether direct or via FormRequests so we can respond with a view with validation errors rather than redirecting.

Usage in middleware

I'm trying to establish if a request was made by HTMX in some middleware.

I have installed the package and published the config.

My middleware is receiving a regular Request and not a HtmxRequest so trying to call the helper methods causes an exception to be thrown.

Have I done something wrong here or is this expected?

composer - File name too long

It seems the filenames in the tests/snapshots folder is creating a "file name too long" error, which seems to be coming from the unzip package that I guess composer is using.

I am running composer on an Ubuntu VM on Windows 11.

Here is some output:

- Installing mauricius/laravel-htmx (v0.4.0): Extracting archive

Failed to extract mauricius/laravel-htmx: (50) '/usr/bin/unzip' -qq '<redacted>/src/vendor/composer/tmp-8789489caefce2fe6d56ee016c02f2c9' -d '<redacted>/src/vendor/composer/f9e20eae'

error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_defined_inline__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_even_if_it_is_enclosed_in_other_fragments__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_even_if_it_is_nested_in_other_fragments__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_even_if_it_is_not_aligned_with_the_closing_fragment__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_even_if_it_it_contains_multibyte_characters__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_whose_name_is_enclosed_in_double_quotes__1.txt
        File name too long
error:  cannot create <redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_whose_name_is_enclosed_in_single_quotes__1.txt
        File name too long


The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)
    Unzip with unzip command failed, falling back to ZipArchive class
    Install of mauricius/laravel-htmx failed

In ZipDownloader.php line 223:                                                                                                                                     
  The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems): ZipArchi  
  ve::extractTo(<redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_defined_inline__1.txt): Failed to open stream: File name too long 

In ZipDownloader.php line 210:                                                                                       
  ZipArchive::extractTo(<redacted>/src/vendor/composer/f9e20eae/mauricius-laravel-htmx-9c111be/tests/__snapshots__/FragmentBladeDirectiveTest__the_render_fragment_view_macro_can_render_a_single_fragment_defined_inline__1.txt): Failed to open stream: File name too long 

Hope this can be resolved.
Looking forward to using the package!

Support object events for `addTrigger` in addition to strings

The htmx docs for HX-Trigger state that events can be strings, but can also be any JSON serializable value.

Would you be open to a PR in this regard?

Example


Input:

with(new HtmxResponse())->addTrigger('showMessage', ['level' => 'info', 'message', 'Here Is A Message'])

Output:

HX-Trigger: {"showMessage":{"level" : "info", "message" : "Here Is A Message"}}

What would happen if you call addTrigger with the same event name twice is up for debate.

Reinventing fragments

Why did you choose to re-invent fragments istead of using the ones added inside laravel directly?

Unexpected end of file with nested fragments

Spatie\LaravelIgnition\Exceptions\ViewException: syntax error, unexpected end of file in file /var/www/html/storage/framework/views/6a26dcf6e7e98f66352021016e0241e2.blade.php on line 19

I just put one @Fragment block inside other and try to render outside fragment. I thing outside @Fragment block connected to inside @endfragment

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.