I need to update the price on the page when a user changes the value of either the width, height and/or length. The code below works, but it seems it could be cleaner instead of having three separate updated
lifecycle hooks with duplicate code. When the view is mounted, I set the default values of width, height and length and calculate the price.
show.php
...
class Show extends Component
{
public $product;
public $sku;
public $name;
public $description;
public $width;
public $height;
public $length;
public $price;
public function mount(Product $product){
$this->product = $product;
$this->sku = $product->sku;
$this->name = $product->name;
$this->description = $product->description;
$this->width = 4;
$this->height = 4;
$this->length = 10;
$this->price = (((($this->width + $this->height + $this->height)*12)/144)*25)*$this->length;
}
public function updatedWidth() {
$width = $this->width;
$height = $this->height;
$length = $this->length;
$this->price = (((($width + $height + $height)*12)/144)*25)*$length;
}
public function updatedHeight() {
$width = $this->width;
$height = $this->height;
$length = $this->length;
$this->price = (((($width + $height + $height)*12)/144)*25)*$length;
}
public function updatedLength() {
$width = $this->width;
$height = $this->height;
$length = $this->length;
$this->price = (((($width + $height + $height)*12)/144)*25)*$length;
}
public function render()
{
return view('livewire.shop.show')->layout('layouts.frontend');
}
}
show.blade.php
<!-- ... -->
<div class="mt-10 px-4 sm:px-0 sm:mt-16 lg:mt-0">
<h1 class="text-3xl font-extrabold tracking-tight text-gray-900">{{$product->name}}</h1>
<div class="mt-3">
<h2 class="sr-only">Product information</h2>
<p wire:model="price" class="text-3xl text-gray-900">${{number_format($price, 2, '.')}}</p>
</div>
<div class="mt-6">
<h3 class="sr-only">Description</h3>
<div class="text-base text-gray-700 space-y-6">
<p>
{{$product->description}}
</p>
</div>
</div>
<form wire:submit.prevent="createOrderItem" method="POST" class="mt-6">
<div>
<h3 class="text-sm text-gray-600">Size</h3>
<div class="grid grid-cols-4 gap-4">
<div class="mt-4 sm:mt-0 sm:pr-9">
<label for="width" class="block text-sm font-medium text-gray-700">Outer Width:</label>
<select wire:model="width" id="width" name="width" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option value="3">3"</option>
<option value="4">4"</option>
<option value="5">5"</option>
</select>
</div>
<div class="mt-4 sm:mt-0 sm:pr-9">
<label for="height" class="block text-sm font-medium text-gray-700">Outer Height:</label>
<select wire:model="height" id="height" name="height" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option value="3">3"</option>
<option value="4">4"</option>
<option value="5">5"</option>
</select>
</div>
<div class="mt-4 sm:mt-0 sm:pr-9">
<label for="length" class="block text-sm font-medium text-gray-700">Length:</label>
<select wire:model="length" id="length" name="length" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option value="10">10'</option>
<option value="11">11'</option>
<option value="12">12'</option>
</select>
</div>
</div>
</div>
<!-- ... -->
Since you are doing the same calculation multiple times, it would make sense to extract that into its own method. Then, you can use the "global" updated()
lifecycle-hook and conditionally set the price based on which fields were updated.
class Show extends Component
{
public $product;
public $sku;
public $name;
public $description;
public $width;
public $height;
public $length;
public $price;
public function mount(Product $product)
{
$this->product = $product;
$this->sku = $product->sku;
$this->name = $product->name;
$this->description = $product->description;
$this->width = 4;
$this->height = 4;
$this->length = 10;
$this->price = $this->calculatePrice();
}
/**
* Calculates the price based on height, width and length
* @return int $calculatedPrice
*/
private function calculatePrice()
{
return (((($this->width + (2 * $this->height)) * 12) / 144) * 25) * $this->length;
}
/**
* Life-cycle hook that will fire on each updated value from the view
* @param string $field The fieldname that is updated
*/
public function updated($field)
{
if (in_array($field, ['width', 'height', 'length'])) {
$this->price = $this->calculatePrice();
}
}
public function render()
{
return view('livewire.shop.show')
->layout('layouts.frontend');
}
}