I'm building a wizard using Livewire 3, alpine 3.4.2, the main wizard is a Livewire component and each wizard's step is a Livewire component too, the issue is after I click on the first step save button(to go to the next step) there's an error being shown in the console, I trace the code and I try to use dispatch, $wire.call, all options that we have the call component method but still face the same issue.
Error :
Alpine Expression Error: Unexpected token '}'
Expression: "$wire."
Uncaught SyntaxError: Unexpected token '}'
Main wizard :
<?php
class TenantCreate extends Component
{
use AuthorizesActions;
public $tenant;
public $steps;
public $currentStep;
protected $listeners = ['stepCompleted'];
public function mount()
{
$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++;
}
}
public function render()
{
return view('admin.tenants.tenant-create');
}
}
View :
<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>
<!--- Completed Step --->
<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="" 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>
</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>
<!--- Bottom Navigation --->
<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>
<!--- / Bottom Navigation --->
</div>
<script>
function app() {
return {
step: @entangle('currentStep'),
steps: @entangle('steps'),
isStepCompleted(step) {
const currentStep = parseInt(step);
if (!this.steps || !this.steps[currentStep]) return false;
return this.steps[currentStep]?.status === true;
}
}
}
</script>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
step one :
<?php
class BasicInformation extends Component
{
public $tenant, $name, $clean_external_id, $region_id, $regions = [];
public $scope, $stepStatus = false;
public function mount($stepData = [], $scope = "")
{
$this->regions = Region::pluck('name', 'id');
$this->scope = $scope;
$this->tenant = $stepData['tenant'] ?? [];
$this->name = $this->tenant['name'] ?? '';
$this->clean_external_id = $stepData['clean_external_id'] ?? '';
$this->region_id = $this->tenant['region_id'] ?? null;
}
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());
}
}
private function getStepData()
{
return [
'tenant' => $this->tenant,
'clean_external_id' => $this->clean_external_id,
];
}
public function render()
{
return view('admin.tenants.partials.basic-information');
}
}
Step one View :
<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 8.3