mojahed/spa 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

mojahed/spa

Composer 安装命令:

composer require mojahed/spa

包简介

A Laravel SPA package using Alpine.js, Axios and Bootstrap — blade components, directives and JS utilities to build dynamic single-page applications without a build step.

README 文档

README

A Laravel SPA package powered by Alpine.js, Axios, and Bootstrap — no build step, no npm, no Vite. Load blade fragments dynamically, manage modals, offcanvases, and independent page sections with clean declarative components.

Requirements

  • PHP >= 7.4
  • Laravel >= 7.x (components & directives)
  • Laravel >= 9.36 (for the SpaRequest::fragmentIf() controller helper)
  • Bootstrap 5
  • Alpine.js 3.x
  • Axios
  • SweetAlert2 (for confirm dialogs and toasts)

Installation

composer require mojahed/spa

Publish and copy assets to your public directory:

php artisan spa:setup

Optionally publish config and views for customization:

php artisan spa:publish

Layout Setup

Add directives to your main layout file:

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ config('app.name') }}</title>

    @spacss
    @stack('styles')
</head>

<body @spadata>

    @include('partials.sidebar')

    <div class="main-content">
        @yield('content')
    </div>

    @spajs
    @stack('scripts')

</body>
</html>

Use a custom Alpine component name:

<body @spadata('myAppData')>

Then define it in your scripts:

function myAppData() {
    return {
        ...spa(),
        // your custom properties and methods
    };
}

Directives

Directive Output
@spacss CSS link tags (Bootstrap + spa.css)
@spajs JS script tags (Alpine, Axios, Bootstrap, SweetAlert2, spa.js) + CSRF headers
@spadata x-data="spa()" on body
@spadata('name') x-data="name()" on body

Components

<x-spa-target> — Inline Fragment Zone

Loads a blade fragment into an inline div. Optionally auto-loads on page ready.

<x-spa-target
    id="table-wrapper"
    url="{{ route('employees.index') }}"
    auto-load="true"
    loader-type="table"
    loader-rows="6"
    loader-cols="5"
/>

Props

Prop Default Description
id required Unique ID
url '' URL to fetch
auto-load false Load on page ready
loader-type config default spinner | skeleton | table | card
loader-rows 5 Skeleton/table rows
loader-cols 4 Table columns
method get HTTP method
params [] Query params array

<x-spa-modal> — Bootstrap Modal Container

<x-spa-modal id="form-modal" size="xl" />

Props

Prop Default Description
id required Unique ID
size lg sm | md | lg | xl | fullscreen
title '' Modal title
loader-type config default Loader type while loading
static-backdrop true Prevent close on outside click
scrollable false Scrollable modal body

<x-spa-offcanvas> — Bootstrap Offcanvas Container

<x-spa-offcanvas id="details-offcanvas" title="Details" width="500px" />

Props

Prop Default Description
id required Unique ID
title '' Header title
placement end start | end | top | bottom
width 450px Width (end/start only)
loader-type config default Loader type while loading

<x-spa-btn> — Button Trigger

{{-- Load into modal --}}
<x-spa-btn modal="form-modal" url="{{ route('employees.create') }}" class="btn btn-success btn-sm">
    New Employee
</x-spa-btn>

{{-- Delete with confirm --}}
<x-spa-btn
    url="{{ route('employees.destroy', $emp->id) }}"
    method="DELETE"
    confirm="true"
    confirm-title="Delete this employee?"
    confirm-text="This cannot be undone."
    on-success-reload="#table-wrapper"
    on-success-toast="Employee deleted."
    class="btn btn-danger btn-xs">
    Delete
</x-spa-btn>

Props

Prop Default Description
url '' Target URL
method get HTTP method
target '' Load into #spa-target id
modal '' Load into modal id
offcanvas '' Load into offcanvas id
confirm false Show confirm dialog first
confirm-title config Confirm dialog title
confirm-text config Confirm dialog body text
confirm-type warning SweetAlert icon type
confirm-ok config Confirm button label
confirm-cancel config Cancel button label
on-success-reload '' Reload target by #id
on-success-close '' Close modal/offcanvas by #id
on-success-toast '' Show success toast message
on-success-redirect '' Redirect to URL
on-success-emit '' Dispatch custom Alpine event

<x-spa-link> — Anchor Trigger

Same props as <x-spa-btn>, renders as an <a> tag.

<x-spa-link
    offcanvas="details-offcanvas"
    url="{{ route('employees.show', $emp->id) }}"
    class="text-info">
    View Details
</x-spa-link>

<x-spa-form> — Axios Form Submit

Handles form submission with error binding and submitting state. Pass initial field values through :data and bind inputs with x-model="{model}.{field}".

<x-spa-form
    url="{{ route('items.store') }}"
    method="POST"
    model="form"
    :data="['name' => '', 'status' => 'active']"
    on-success-reload="#table-wrapper"
    on-success-close="#form-modal"
    on-success-toast="Saved!"
>
    <input type="text" x-model="form.name" :class="{ 'is-invalid': errors.name }">
    <div class="invalid-feedback" x-text="errors.name"></div>

    <button type="button" @click="submit()" :disabled="submitting">
        <span x-show="submitting" class="spinner-border spinner-border-sm"></span>
        Save
    </button>
</x-spa-form>

Props

Prop Default Description
url required Submit URL
method post HTTP method
model form Name of the Alpine data object holding the fields
:data [] Initial field values, e.g. :data="['name' => $item->name]"

<x-spa-loader> — Standalone Loader

<x-spa-loader type="spinner" />
<x-spa-loader type="skeleton" rows="5" />
<x-spa-loader type="table" rows="6" cols="4" />
<x-spa-loader type="card" rows="3" />

Dashboard — Independent Sections

Each section loads independently with its own loader, just like the AWS dashboard:

<div class="row g-3">
    <div class="col-md-4">
        <x-spa-target id="widget-revenue"    url="{{ route('dashboard.revenue') }}"    auto-load="true" loader-type="card" />
    </div>
    <div class="col-md-4">
        <x-spa-target id="widget-attendance" url="{{ route('dashboard.attendance') }}" auto-load="true" loader-type="skeleton" />
    </div>
    <div class="col-md-4">
        <x-spa-target id="widget-notices"    url="{{ route('dashboard.notices') }}"    auto-load="true" loader-type="spinner" />
    </div>
</div>

Controller — Fragment Detection

Use Laravel's built-in fragment system with the X-SPA-Request header automatically sent by the package:

use Mojahed\Spa\SpaRequest;

public function index(Request $request)
{
    $employees = Employee::all();

    return SpaRequest::fragmentIf($request,
        view('employees.index', compact('employees')),
        'list'
    );
}

Or directly with Laravel's fragmentIf:

return view('employees.index', compact('employees'))
    ->fragmentIf($request->hasHeader('X-SPA-Request'), 'list');

JS Utilities

All utilities are globally available:

// Toasts
spaToast({ type: 'success', title: 'Saved!', seconds: 3 });
spaToast({ type: 'error',   title: 'Something went wrong.' });

// URL params
spaSetParam('page', 2);
spaGetParam('page', 1);
spaRemoveParam('page');
spaGetAllParams();

// Script execution from fragment HTML
spaRunScriptCode(html);

// Trigger an action programmatically
spaAction({
    url:      '/employees/1',
    method:   'delete',
    confirm:  { enabled: true, title: 'Delete?' },
    onSuccess: { toast: 'Deleted!', reload: 'table-wrapper' }
});

Configuration

Publish and edit config/spa.php:

return [
    'asset_path'       => 'assets/spa',
    'asset_version'    => null,         // e.g. '1.0' for cache busting
    'default_loader'   => 'spinner',    // spinner | skeleton | table | card
    'auto_run_scripts' => true,
    'confirm' => [
        'title'  => 'Are you sure?',
        'text'   => "You won't be able to revert this!",
        'type'   => 'warning',
        'ok'     => 'Yes, proceed!',
        'cancel' => 'Cancel',
    ],
];

Legacy Compatibility

If you are migrating from existing code that uses toast(), setParam(), getParam(), runScriptCode() — these are aliased to the new spa* functions so your existing code continues to work without changes.

License

MIT — Md. Mojahedul Islam

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-05

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固