zunnu/cake-htmx

CakePHP 的 Htmx 插件

安装: 134

依赖项: 0

建议者: 0

安全: 0

星星: 7

关注者: 2

分支: 2

开放问题: 1

类型:cakephp-plugin

1.0.2 2024-07-25 19:40 UTC

This package is auto-updated.

Last update: 2024-09-21 13:43:56 UTC


README

CakePHP 集成 htmx

支持的 CakePHP 版本 >= 4.x 和 5.x。

使用 Composer 安装

进入应用程序文件夹根目录(其中包含 composer.json 文件)并运行以下命令

composer require zunnu/cake-htmx

然后使用 CakePHP 控制台加载插件

./bin/cake plugin load CakeHtmx

要安装 htmx,请浏览 他们的文档

用法

主功能目前封装在 Htmx 组件中。要加载该组件,您需要修改 src/Controller/AppController.php 并在 initialize() 函数中加载 Htmx 组件

$this->loadComponent('CakeHtmx.Htmx');

请求

您可以使用检测器来检查请求是否为 Htmx。

$this->getRequest()->is('htmx')  // Always true if the request is performed by Htmx
$this->getRequest()->is('boosted') // Indicates that the request is via an element using hx-boost
$this->getRequest()->is('historyRestoreRequest') // True if the request is for history restoration after a miss in the local history cache

使用组件,您可以检查更多关于请求的详细信息。

$this->Htmx->getCurrentUrl();  // The current URL of the browser
$this->Htmx->getPromptResponse(); // The user response to an hx-prompt
$this->Htmx->getTarget(); // The id of the target element if it exists
$this->Htmx->getTriggerName(); // The name of the triggered element if it exists
$this->Htmx->getTriggerId(); // The id of the triggered element if it exists

响应

  • 重定向

当 Htmx 收到带有 HX-Redirect 的响应时,可以触发客户端重定向。

$this->Htmx->redirect('/somewhere-else');
  • clientRefresh

当 Htmx 收到带有 HX-Refresh 的响应时,将触发页面刷新。clientRefresh 是一个自定义响应,允许您发送此类响应。它不接受任何参数,因为 Htmx 忽略任何内容。

$this->Htmx->clientRefresh();
  • stopPolling

当使用 轮询触发器 时,如果遇到特殊的 HTTP 状态码 286 的响应,Htmx 将停止轮询。stopPolling 是具有该状态码的自定义响应。

$this->Htmx->stopPolling();

有关所有可用头部的详细信息,请参阅文档。

$this->Htmx->location($location) // Allows you to do a client-side redirect that does not do a full page reload
$this->Htmx->pushUrl($url) // pushes a new url into the history stack
$this->Htmx->replaceUrl($url) // replaces the current URL in the location bar
$this->Htmx->reswap($option) // Allows you to specify how the response will be swapped
$this->Htmx->retarget($selector); // A CSS selector that updates the target of the content update to a different element on the page

此外,您还可以使用 addTrigger 方法触发客户端事件。

$this->Htmx
    ->addTrigger('myEvent')
    ->addTriggerAfterSettle('myEventAfterSettle')
    ->addTriggerAfterSwap('myEventAfterSwap');

如果您想随事件传递详细信息,可以使用第二个参数发送正文。它支持字符串或数组。

$this->Htmx->addTrigger('myEvent', 'Hello from myEvent')
->addTriggerAfterSettle('showMessage', [
    'level' => 'info',
    'message' => 'Here is a Message'
]);

如果您想触发多个事件,可以多次调用这些方法。

$this->Htmx
    ->addTrigger('trigger1', 'A Message')
    ->addTrigger('trigger2', 'Another Message')

CSRF token

要将 CSRF token 添加到所有请求中,请将以下代码添加到您的布局中。

document.body.addEventListener('htmx:configRequest', (event) => {
    event.detail.headers['X-CSRF-Token'] = "<?= $this->getRequest()->getAttribute('csrfToken') ?>";
})

渲染块和 OOB 交换

setBlock() 函数允许您渲染特定块,同时删除可能渲染的其他块。这在您只需要更新视图的一部分时非常有用。

$this->Htmx->setBlock('userTable');

addBlock() 函数允许您将特定块添加到应渲染的块列表中。

$this->Htmx->addBlock('userTable');

addBlocks() 函数允许您将多个块添加到应渲染的块列表中

$this->Htmx->addBlocks(['userTable', 'pagination']);

OOB 交换

Htmx 支持通过返回多个部分响应来更新多个目标,这些响应使用 hx-swap-oop。请参阅示例 Users index search functionality with pagination update。注意,如果您正在处理示例中的表格,您可能需要添加以下内容

<script type="text/javascript">
    htmx.config.useTemplateFragments = true;
</script>

到您的模板或布局中。

示例

用户索引搜索功能

在这个示例中,我们将使用 Htmx 实现用户索引的搜索功能,并动态过滤结果。我们将把表格主体包裹在一个名为 usersTableviewBlock 中。页面加载时,我们将渲染 usersTable viewBlock

// Template/Users/index.php

<?= $this->Form->control('search', [
    'label' => false, 
    'placeholder' => __('Search'),
    'type' => 'text', 
    'required' => false, 
    'class' => 'form-control input-text search',
    'value' => !empty($search) ? $search : '',
    'hx-get' => $this->Url->build(['controller' => 'Users', 'action' => 'index']),
    'hx-trigger' => "keyup changed delay:200ms",
    'hx-target' => "#search-results",
    'templates' => [
        'inputContainer' => '<div class="col-10 col-md-6 col-lg-5">{{content}}</div>'
    ]
]); ?>

<table id="usersTable" class="table table-hover table-white-bordered">
    <thead>
        <tr>
            <th scope="col"><?= 'id' ?></th>
            <th scope="col"><?= 'Name' ?></th>
            <th scope="col"><?= 'Email' ?></th>
            <th scope="col"><?= 'Modified' ?></th>
            <th scope="col"><?= 'Created' ?></th>
            <th scope="col" class="actions"><?= 'Actions' ?></th>
        </tr>
    </thead>
    
    <tbody id="search-results">
        <?php $this->start('usersTable'); ?>
            <?php foreach ($users as $user): ?>
                <tr>
                    <td><?= $user->id ?></td>
                    <td><?= h($user->name) ?></td>
                    <td><?= h($user->email) ?></td>
                    <td><?= $user->modified ?></td>
                    <td><?= $user->created ?></td>
                    <td class="actions">
                        <?= $this->Html->link('Edit',
                            [
                                'action' => 'edit',
                                $user->id
                            ],
                            [
                                'escape' => false
                            ]
                        ); ?>
                        <?= $this->Form->postLink('Delete',
                            [
                                'action' => 'delete',
                                $user->id
                            ],
                            [
                                'confirm' => __('Are you sure you want to delete user {0}?', $user->email),
                                'escape' => false
                            ]
                        ); ?>
                    </td>
                </tr>
            <?php endforeach; ?>
        <?php $this->end(); ?>

        <?php echo $this->fetch('usersTable'); ?>
    </tbody>
</table>

在我们的控制器中,我们将检查请求是否为 Htmx,如果是,则仅渲染 usersTable viewBlock

// src/Controller/UsersController.php

public function index()
{
    $search = null;
    $query = $this->Users->find('all');

    if ($this->request->is('get')) {
        if(!empty($this->request->getQueryParams())) {
            $data = $this->request->getQueryParams();

            if(isset($data['search'])) {
                $data = $data['search'];
                $conditions = [
                    'OR' => [
                        'Users.id' => (int)$data,
                        'Users.name LIKE' => '%' . $data . '%',
                        'Users.email LIKE' => '%' . $data . '%',
                    ],
                ];
                $query = $query->where([$conditions]);
                $search = $data;
            }
        }
    }

    $users = $query->toArray();
    $this->set(compact('users', 'search'));

    if($this->getRequest()->is('htmx')) {
        $this->viewBuilder()->disableAutoLayout();

        // we will only render the usersTable viewblock
        $this->Htmx->setBlock('usersTable');
    }
}

用户索引搜索功能及分页更新

在这个示例中,我们将使用 Htmx 实现用户索引的动态搜索功能。这将允许我们实时过滤结果并相应地更新分页。我们将把表格主体包裹在名为 usersTableviewBlock 中,并添加到 pagination 块。页面加载时,我们将渲染 usersTablepagination viewBlock

// Template/Users/index.php

<?= $this->Form->control('search', [
    'label' => false, 
    'placeholder' => __('Search'),
    'type' => 'text', 
    'required' => false, 
    'class' => 'form-control input-text search',
    'value' => !empty($search) ? $search : '',
    'hx-get' => $this->Url->build(['controller' => 'Users', 'action' => 'index']),
    'hx-trigger' => 'keyup changed delay:200ms',
    'hx-target' => '#search-results',
    'hx-push-url' => 'true',
    'templates' => [
        'inputContainer' => '<div class="col-10 col-md-6 col-lg-5">{{content}}</div>'
    ]
]); ?>

<table id="usersTable" class="table table-hover table-white-bordered">
    <thead>
        <tr>
            <th scope="col"><?= 'id' ?></th>
            <th scope="col"><?= 'Name' ?></th>
            <th scope="col"><?= 'Email' ?></th>
            <th scope="col"><?= 'Modified' ?></th>
            <th scope="col"><?= 'Created' ?></th>
            <th scope="col" class="actions"><?= 'Actions' ?></th>
        </tr>
    </thead>
    
    <tbody id="search-results">
        <?php $this->start('usersTable'); ?>
            <?php foreach ($users as $user): ?>
                <tr>
                    <td><?= $user->id ?></td>
                    <td><?= h($user->name) ?></td>
                    <td><?= h($user->email) ?></td>
                    <td><?= $user->modified ?></td>
                    <td><?= $user->created ?></td>
                    <td class="actions">
                        <?= $this->Html->link('Edit',
                            [
                                'action' => 'edit',
                                $user->id
                            ],
                            [
                                'escape' => false
                            ]
                        ); ?>
                        <?= $this->Form->postLink('Delete',
                            [
                                'action' => 'delete',
                                $user->id
                            ],
                            [
                                'confirm' => __('Are you sure you want to delete user {0}?', $user->email),
                                'escape' => false
                            ]
                        ); ?>
                    </td>
                </tr>
            <?php endforeach; ?>
        <?php $this->end(); ?>

        <?php echo $this->fetch('usersTable'); ?>
    </tbody>
</table>

// pagination
<?php $this->start('pagination'); ?>
    <nav aria-label="Page navigation" id="pagination">
        <ul class="pagination justify-content-center">
            <?php $this->Paginator->setTemplates([
                'prevActive' => '<li class="page-item pagination-previous"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'prevDisabled' => '<li class="page-item disabled pagination-previous"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'number' => '<li class="page-item"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'current' => '<li class="page-item active"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'nextActive' => '<li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'nextDisabled' => '<li class="page-item disabled pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'first' => '<li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
                'last' => '<li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li>',
            ]); ?>
            <?= $this->Paginator->first('<i class="fas fa-angles-left"></i>', ['escape' => false]) ?>
            <?= $this->Paginator->prev('<i class="fas fa-chevron-left"></i>', ['escape' => false]) ?>
            <?= $this->Paginator->numbers(['first' => 1, 'last' => 1, 'modulus' => 3]) ?>
            <?= $this->Paginator->next('<i class="fas fa-chevron-right"></i>', ['escape' => false]) ?>
            <?= $this->Paginator->last('<i class="fas fa-angles-right"></i>', ['escape' => false]) ?>
        </ul>
    </nav>
<?php $this->end(); ?>

<?= $this->fetch('pagination'); ?>

在我们的控制器中,我们将检查请求是否为 Htmx,如果是,则仅渲染 usersTable viewBlock

// src/Controller/UsersController.php

public function index()
{
    $search = null;
    $query = $this->Users->find('all');

    if ($this->request->is('get')) {
        if(!empty($this->request->getQueryParams())) {
            $data = $this->request->getQueryParams();

            if(isset($data['search'])) {
                $data = $data['search'];
                $conditions = [
                    'OR' => [
                        'Users.id' => (int)$data,
                        'Users.name LIKE' => '%' . $data . '%',
                        'Users.email LIKE' => '%' . $data . '%',
                    ],
                ];
                $query = $query->where([$conditions]);
                $search = $data;
            }
        }
    }

    $this->paginate['limit'] = 200;
    $users = $this->paginate($query);
    $this->set(compact('users', 'search'));

    if($this->getRequest()->is('htmx')) {
        $this->viewBuilder()->disableAutoLayout();

        // render users table and pagination blocks
        $this->Htmx->addBlock('usersTable')->addBlock('pagination');
    }
}

许可证

许可协议为 MIT 许可证