我正在使用 Livewire 3 和 Alpine.js 3.4.2 在 Laravel 项目中构建一个向导。主向导和每个步骤都是 Livewire 组件。一切正常,直到我单击第一步中的“保存”按钮进入下一步,此时我在浏览器控制台中遇到以下错误:
Alpine 表达式错误:意外的标记“}” 表达式:“$wire。” 未捕获的语法错误:意外的标记“}”
这是我的实现:
主要向导:
class TenantCreate extends Component {
use AuthorizesActions;
public $tenant;
public $steps;
public $currentStep;
protected $listeners = ['stepCompleted'];
public function mount()
{
$this->steps = [
1 => ['status' => false, 'data' => []],
2 => ['status' => false, 'data' => []],
3 => ['status' => false, 'data' => []]
];
$this->currentStep = 1;
}
public function stepCompleted($step, $stepStatus = false, $stepData = [])
{
if ($step === $this->currentStep && $stepStatus) {
if($step == 1) {
$this->tenant = $stepData['tenant'];
}
$this->steps[$this->currentStep]['data'] = $stepData;
$this->steps[$this->currentStep]['status'] = $stepStatus;
$this->currentStep++;
}
}
}
查看:
<div class="modal fade" id="createModal" tabindex="-1" aria-hidden="true" wire:ignore.self>
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCenterTitle">Create Tenant</h5>
<button type="button" wire:click='closeModal()' class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form wire:submit.prevent>
<div class="modal-body">
<div class="row">
<div>
<div x-data="app()" x-cloak>
<div class="container mx-auto">
<div x-show.transition="step === 'complete'">
<div class="w-100 d-flex flex-column align-items-center justify-content-center">
<img src="{{ asset('admin-assets/img/general/check-icon.svg') }}" alt="Check Icon" class="mb-3">
<h2 class="fs-4 mb-3 text-dark fw-bold">Tenant Created Successfully</h2>
</div>
</div>
<div x-show.transition="step != 'complete'">
<!--- Top Navigation --->
<div class="border-bottom py-3">
<div class="progress w-75 mx-auto mb-4">
<div class="progress-bar" role="progressbar"
x-bind:style="'width: ' + (parseInt(step / 5 * 100)) + '%'"
aria-valuenow="step"
x-text="parseInt(step / 6 * 100) + '%'"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<div class="text-uppercase small text-muted mb-1 lh-tight" x-text="`Step : ${step} of 6`"></div>
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between">
<div>
<div x-show="step === 1">
<div class="h5 fw-bold text-muted">Basic Information</div>
</div>
<div x-show="step === 2">
<div class="h5 fw-bold text-muted">System Parameters</div>
</div>
<div x-show="step === 3">
<div class="h5 fw-bold text-muted">Employees import</div>
</div>
<div x-show="step === 4">
<div class="h5 fw-bold text-muted">Survey Installer(Engagement)</div>
</div>
<div x-show="step === 5">
<div class="h5 fw-bold text-muted">Survey Installer(Experience)</div>
</div>
<div x-show="step === 6">
<div class="h5 fw-bold text-muted">Survey Installer(On-Demand)</div>
</div>
</div>
</div>
</div>
<!--- /Top Navigation --->
<!--- Step Content --->
<div class="py-10">
<div x-show="step === 1">
@livewire('admin.tenants.partials.basic-information',
['stepData' => $steps[1]['data'] ?? [],
'scope' => 'wizard'])
</div>
<div x-show="step === 2">
@livewire('admin.tenants.partials.system-parameters',
[
'stepData' => $steps[2]['data'] ?? [],
'scope' => 'wizard',
'tenant' => $tenant
],
key('sys-param-' . ($tenant['id'] ?? 'default'))
)
</div>
<div x-show="step === 3">
@livewire('admin.tenants.partials.employees-import',
[
'stepData' => $steps[3]['data'] ?? [],
'tenant' => $tenant,
'scope' => 'wizard'
],
key('emp-data-key-' . ($tenant['id'] ?? 'default'))
)
</div>
</div>
<!--- / Step Content --->
</div>
</div>
<div class="bottom-0 start-0 end-0 py-4 bg-white" x-show="step != 'complete'">
<div class="container mx-auto p-3">
<div class="d-flex justify-content-between">
<div class="col-6">
<button
x-show="step > 1"
wire:click="updateStep(step - 1)"
class="mx-auto py-2 p-3 rounded-lg shadow text-center text-secondary bg-white border fw-medium"
style="width: 8rem;">
Previous
</button>
</div>
<div>
<button
x-show="step < 3 && isStepCompleted(step)"
wire:click="updateStep(step + 1)"
class="w-32 border-0 py-2 px-3 rounded shadow text-center text-white bg-primary fw-medium"
style="width: 8rem;">
Next
</button>
<button
x-show="step === 3"
wire:click="updateStep('complete')"
class="w-32 border-0 py-2 px-3 rounded shadow text-center text-white bg-primary fw-medium"
style="width: 8rem;">
Complete
</button>
</div>
</div>
</div>
</div>
</div>
<script>
function app() {
return {
step: @entangle('currentStep'),
stepsData: @entangle('steps'),
isStepCompleted(step) {
return this.stepsData[parseInt(step)]?.status === true;
}
}
}
</script>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
第一步:
class BasicInformation extends Component
{
public $tenant, $name, $clean_external_id, $region_id, $regions = [];
public $scope, $stepStatus = false;
public function submitBasicInfo()
{
$this->validate((new StoreTenant())->rules(1), $this->messages(), $this->attributes());
DB::beginTransaction();
try {
$this->tenant = Tenant::create([
'name' => $this->name,
'region_id' => $this->region_id,
]);
$this->tenant->update([
'external_id' => strtolower($this->clean_external_id) . '-' . $this->tenant->id
]);
$this->tenant->dataItem()->create([]);
$this->tenant->tenant_id = $this->tenant->external_id;
DB::commit();
//Agent Request
$response = (new AgentService($this->tenant->id))->createVhAndSslCeritificate($this->name, $this->tenant->tenant_id);
if($response['status'] == 200){
$this->stepStatus = true;
//only if the component in wizard
if($this->scope == 'wizard'){
$this->dispatch('stepCompleted', 1, $this->stepStatus, $this->getStepData());
}
return true;
}else{
$this->addError('stepErrors', 'Something went wrong While creating tenant on the server, please contact adminstartor');
return $this;
}
return true;
}catch (\Exception $e) {
DB::rollBack();
throw new \Exception('Error Creating Tenant : ' . $e->getMessage());
}
}
}
第一步查看:
<div>
@error('stepErrors')
<div class="container mt-0">
<div class="alert alert-danger">
<h5 class="alert-heading mb-3">Errors:</h5>
<ul class="list-group list-group-flush">
@foreach ($errors->get('stepErrors') as $error)
<li class="list-group-item list-group-item-danger d-flex justify-content-between align-items-center">
<span>{{ $error }}</span>
<span class="badge bg-danger rounded-pill">!</span>
</li>
@endforeach
</ul>
</div>
</div>
@enderror
{{-- Form Fields --}}
<div class="row mt-3">
<div class="col-6">
<div class="d-flex align-items-center">
<label class="form-label mb-0">Domain</label>
<x-right-popover title="Tenant Domain"
content="<p>This field will be used for Tenant URLS<br><small>Must be valid URL, no spaces or symbols e.g. engagesoft</small></p>"/>
</div>
<x-text-input wire:model='name' placeholder="Tenant Domain" :disabled="$scope == 'wizard' && $stepStatus"/>
<x-input-error :messages="$errors->get('name')" />
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<label class="form-label mb-0">Region</label>
<x-right-popover title="Tenant Region (server)"
content="<p>Choose the region that tenant will be on.</p>"/>
</div>
<x-select wire:model='region_id' placeholder="Select Region" :data="$regions" :disabled="$scope == 'wizard' && $stepStatus"
wire:key="region-select-{{ now() }}"
/>
<x-input-error :messages="$errors->get('region_id')" />
</div>
</div>
<div class="row mt-3 mb-3">
<div class="col-6 mb-0">
<div class="d-flex align-items-center">
<label class="form-label mb-0">Tenant ID</label>
<x-right-popover title="Tenant ID"
content="<p>Unique id, no symbols or spaces.<small>e.g. engage</small></p>"/>
</div>
<x-text-input wire:model='clean_external_id' placeholder="Tenant ID" :disabled="$scope == 'wizard' && $stepStatus"/>
<x-input-error :messages="$errors->get('clean_external_id')" />
</div>
</div>
@if (!($scope == 'wizard' && $stepStatus))
<x-button-loading
wire:click="submitBasicInfo"
buttonName="Save"
target="submit"
class="w-32 border-0 py-2 px-3 rounded shadow text-center text-white bg-primary fw-medium"
/>
@endif
</div>
Livewire v3.5
Laravel v11.9
PHP v8.3
我尝试过的:
确保正确定义 Alpine.js 初始化(x-data)。
使用不同的方式调用stepCompleted方法直接调用livewire和内部apline:
$wire.call(), @this.call()
观察结果:
该错误特别指向 Alpine.js 上下文中的 $wire 调用。 通过wire:click指令与Livewire组件交互时。
问题: 是什么导致了意外令牌“}”错误,如何修复它以允许向导中 Livewire 3 和 Alpine.js 之间的无缝集成?
我遇到了一个问题,单击表单向导步骤内的提交按钮(每个步骤都是一个 Livewire 组件)会触发传统的表单提交,这不是预期的行为。
问题: 我的表单向导的每个步骤都是一个 Livewire 组件,每个步骤都有一个提交按钮。最初,我像这样绑定提交操作:
<x-button-loading x-on:click="$wire.submitBasicInfo" buttonName="Save" target="submitBasicInfo"/>
如果没有 .prevent 修饰符,单击按钮会触发默认的表单提交行为。这导致表单以传统方式提交,干扰 Livewire 处理。
解决方案: 为了解决这个问题,我使用了 .prevent 修饰符,它可以阻止默认表单提交并允许 Alpine.js 和 Livewire 正确处理表单逻辑:
<x-button-loading @click.prevent="$wire.submitBasicInfo" buttonName="Save" target="submitBasicInfo" />
因此,通过使用 @click.prevent,可以停止默认行为,确保表单提交由 Livewire 控制,而不会发生传统的表单发布。