Create a Dynamic Modal using PHP and HTMX #0
Tags: htmx, php, devjournal, webdev
Published: 2024-03-26 17:30:00

Hello Punk! I am again... sorry for that.

The follow up question that come up in my **invisible head** about this journal:

{% embed https://dev.to/darkterminal/1-endpoint-do-5-things-for-htmx-datatable-1k57 %}

![Jd Headless Jd GIF - https://tenor.com/view/jd-headless-jd-john-dorian-bye-funny-gif-14134807](https://media1.tenor.com/m/ifAqVZ3TOlcAAAAd/jd-headless-jd.gif)

is... _**How can I create a modal that display form to create a new supplier in my app and load the content from backend (Yes! PHP) that generate the content, also doing validation and stuff, and removing the modal after the create operation is done!.**_

Hmmm... my question is super interesting. I would not compare with other approach (I mean language or framework), I just want to share what I am doing before (Yes! my [fck](https://github.com/darkterminal/fck) PHP Framework)

---

Technically, I am so proud of myself and I need to prepare for something interesting and wasting my time! 😏 _Oh yeah beibeh_

---

## What's The Plan?!

> ![High Level Plan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mabercrywzf4i5m2imh.png)
> _Screenshot from [htmx modal custom - example](https://htmx.org/examples/modal-custom/)_

## The Meme

![20 Year Old](https://htmx.org/img/memes/20yearold.png)

## The [Hypertext Markup Language](https://en.wikipedia.org/wiki/HTML) of The Modal

This not fully HTML (I see it bro!). Also have a PHP tag inside (Seriously!?) that's part of my [fck](https://github.com/darkterminal/fck) PHP Framework (Fck'it bro!).

### The Create Button

```html
<button class="btn btn-sm btn-neutral" hx-get="<?= base_url('suppliers/create') ?>" hx-target="body" hx-swap="beforeend" id="exclude-indicator"><?= Icons::use('Plus', 'h-4 w-4') ?>Create new</button>
```

![The Create Button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5bvr67gxf62j5z5mhi5g.png)


### The Modal Itself!

```html
<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/views/components/suppliers/modals/modal-create.php

use App\config\helpers\Icons;
use App\config\helpers\Utils;

?>
<div id="modal" _="on closeModal wait 250ms add .closing then wait for animationend then remove me">
    <div class="modal-underlay" _="on click trigger closeModal"></div>
    <div class="w-7/12 min-h-80 max-h-[40em] modal-content">
        <div class="flex justify-between p-2 mb-3 border-b-2">
            <h2 class="text-lg font-bold">Create New Supplier</h2>
            <button class="btn btn-sm btn-ghost btn-circle" _="on click trigger closeModal"><?= Icons::use('XMark') ?></button>
        </div>
        <form class="grid grid-cols-2 gap-3 p-3 w-full" method="post" hx-post="<?= base_url('suppliers/create') ?>" hx-target="#modal" hx-swap="outerHTML" autocomplete="off">
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Supplier Name</span>
                </div>
                <input type="text" name="supplierName" placeholder="Supplier name" class="w-full input input-sm input-bordered <?= !empty(Utils::getValidationError($errors, 'supplierName')) ? 'input-error' : '' ?>" />
                <div class="label">
                    <?= Utils::getValidationError($errors, 'supplierName') ?>
                </div>
            </label>
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Supplier Company Name</span>
                </div>
                <input type="text" name="supplierCompanyName" placeholder="Supplier company name" class="w-full input input-sm input-bordered <?= !empty(Utils::getValidationError($errors, 'supplierCompanyName')) ? 'input-error' : '' ?>" />
                <div class="label">
                    <?= Utils::getValidationError($errors, 'supplierCompanyName') ?>
                </div>
            </label>
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Supplier Phone Number</span>
                </div>
                <input type="text" name="supplierPhoneNumber" placeholder="Supplier phone number" class="w-full input input-sm input-bordered <?= !empty(Utils::getValidationError($errors, 'supplierPhoneNumber')) ? 'input-error' : '' ?>" />
                <div class="label">
                    <?= Utils::getValidationError($errors, 'supplierPhoneNumber') ?>
                </div>
            </label>
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Supplier Email</span>
                </div>
                <input type="text" name="supplierEmail" placeholder="Supplier email address" class="w-full input input-sm input-bordered <?= !empty(Utils::getValidationError($errors, 'supplierEmail')) ? 'input-error' : '' ?>" />
                <div class="label">
                    <?= Utils::getValidationError($errors, 'supplierEmail') ?>
                </div>
            </label>
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Latitude Coordinate</span>
                </div>
                <input type="text" name="latitude" placeholder="Latitude" class="w-full input input-sm input-bordered" />
            </label>
            <label class="w-full form-control">
                <div class="label">
                    <span class="label-text">Longitude Coordinate</span>
                </div>
                <input type="text" name="longitude" placeholder="Longitude" class="w-full input input-sm input-bordered" />
            </label>
            <label class="col-span-2 w-full form-control">
                <div class="label">
                    <span class="label-text">Address</span>
                </div>
                <textarea class="h-24 textarea textarea-bordered <?= !empty(Utils::getValidationError($errors, 'supplierAddress')) ? 'input-error' : '' ?>" name="supplierAddress" placeholder="Supplier Address"></textarea>
                <div class="label">
                    <?= Utils::getValidationError($errors, 'supplierAddress') ?>
                </div>
            </label>
            <div class="col-span-2">
                <button class="btn btn-sm btn-block btn-neutral" type="submit">Save</button>
            </div>
        </form>
    </div>
</div>

```

![The Modal Itself!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6crulvbfah83vkfn7orz.png)

_Wait... what is this?!_

```html
<div id="modal" _="on closeModal wait 250ms add .closing then wait for animationend then remove me">
    <div class="modal-underlay" _="on click trigger closeModal"></div>
```

_and this_

```html
<button class="btn btn-sm btn-ghost btn-circle" _="on click trigger closeModal"><?= Icons::use('XMark') ?></button>
```

Oh sorry, I am not mention it before. If you don't know what is that, that's the [_hyperscript](https://hyperscript.org/).

![_hyperscript little introduction](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q64gq3x5vlvvl6gegoxr.png)

When the `XMark` button is `clicked` then the button will be send event called `closeModal`, where the `closeModal` event is received at:

```html
<div id="modal" _="on closeModal wait 250ms add .closing then wait for animationend then remove me">
```
So whenever this event is (`on`) triggered they will wait `250ms` and `add`-ing a `.closing` class in that element `then wait for animationend then remove me` me mean that element itself.

![Tommy Boy Comedy GIF - https://tenor.com/view/tommy-boy-comedy-chris-farley-stressed-upset-gif-11704610607797404331](https://media1.tenor.com/m/om8hVYgIgqsAAAAC/tommy-boy-comedy.gif)

**Huuuuuh... how good I am explaining!? Dang bro... that sound weird for me!**

## First Milestone

![First Milestone](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn583t02ourykraksg1c.gif)

The first milestone is to create this and display it. In this moment is not doing anything. But after we create a `routing` for that modal request and adding some CSS (that I copy and paste in the htmx docs example)

---
## The CSS (copy and paste)

Source: [https://htmx.org/examples/modal-custom/](https://htmx.org/examples/modal-custom/)
```css
/***** MODAL DIALOG ****/
#modal {
	/* Underlay covers entire screen. */
	position: fixed;
	top:0px;
	bottom: 0px;
	left:0px;
	right:0px;
	background-color:rgba(0,0,0,0.5);
	z-index:1000;

	/* Flexbox centers the .modal-content vertically and horizontally */
	display:flex;
	flex-direction:column;
	align-items:center;

	/* Animate when opening */
	animation-name: fadeIn;
	animation-duration:150ms;
	animation-timing-function: ease;
}

#modal > .modal-underlay {
	/* underlay takes up the entire viewport. This is only
	required if you want to click to dismiss the popup */
	position: absolute;
	z-index: -1;
	top:0px;
	bottom:0px;
	left: 0px;
	right: 0px;
}

#modal > .modal-content {
	/* Position visible dialog near the top of the window */
	margin-top:10vh;

	/* Sizing for visible dialog */
	width:80%;
	max-width:600px;

	/* Display properties for visible dialog*/
	border:solid 1px #999;
	border-radius:8px;
	box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3);
	background-color:white;
	padding:20px;

	/* Animate when opening */
	animation-name:zoomIn;
	animation-duration:150ms;
	animation-timing-function: ease;
}

#modal.closing {
	/* Animate when closing */
	animation-name: fadeOut;
	animation-duration:150ms;
	animation-timing-function: ease;
}

#modal.closing > .modal-content {
	/* Animate when closing */
	animation-name: zoomOut;
	animation-duration:150ms;
	animation-timing-function: ease;
}

@keyframes fadeIn {
	0% {opacity: 0;}
	100% {opacity: 1;}
} 

@keyframes fadeOut {
	0% {opacity: 1;}
	100% {opacity: 0;}
} 

@keyframes zoomIn {
	0% {transform: scale(0.9);}
	100% {transform: scale(1);}
} 

@keyframes zoomOut {
	0% {transform: scale(1);}
	100% {transform: scale(0.9);}
}
```

## The Routing #1 GET

```php
<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/routes/web.php
use Fckin\core\Application;

/** @var Application $app  */

$app->router->get('suppliers/create', 'Suppliers@create');
```

This route is corresponding to get the modal content from the backend (Yes! PHP). then display in the body of the page as mentioned in this button and `swap` it `beforeend`:

```html
<button class="btn btn-sm btn-neutral" hx-get="<?= base_url('suppliers/create') ?>" hx-target="body" hx-swap="beforeend" id="exclude-indicator"><?= Icons::use('Plus', 'h-4 w-4') ?>Create new</button>
```

## The Controller #1 GET

```php
<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/controllers/Suppliers.php

namespace App\controllers;

use App\config\helpers\Utils;
use App\models\Suppliers as ModelsSuppliers;
use Fckin\core\Controller;
use Fckin\core\Request;
use Fckin\core\Response;

class Suppliers extends Controller
{
    protected $suppliers;

    public function __construct()
    {
        $response = new Response();
        if (!isAuthenticate()) {
            $response->setStatusCode(401);
            exit();
        }
        $this->suppliers = new ModelsSuppliers();
    }

public function create(Request $request)
    {
        $params = []; // <-- I will do it something here when make POST request

        return Utils::addComponent('suppliers/modals/modal-create', $params);
    }
}
```

That's it! Here I am...

![First Milestone](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn583t02ourykraksg1c.gif)

If anything goes wrong or not working! It's my fault! not me. (_Are you confuse? Yes, me too..._)

---

See ya in the next stone! 👋 I am so tired!!! Hope you mad of me... thank you very much, I appreciate that...