javascriptphplaravellaravel-livewire

Can selected Livewire properties be just sent from a component to its template (i.e. without two-way binding)?


I have a form in a Blade template that utilises Livewire:

{{-- resources/views/page-generation.blade.php --}}
<div class="py-12">
    <livewire:titlegen.form />
</div>

The form looks like this

<?php
// app/Livewire/Titlegen/Form.php

namespace App\Livewire\Titlegen;

use Livewire\Component;

class Form extends Component
{
    // Two way (entered by user, but need to be reflected on screen when loading)
    public string $productName;
    public string $targetAudience;
    public string $usps;
    public string $cta;
    public string $keyPhrases;

    // Outputs (generated on server, shown to user)
    public ?array $titles;
    public ?string $ctaOut;
    public ?string $error;

    // Outputs (stored on server, shown to user)
    public array $menus = [
        // Long menu to render
    ];

    public function boot(ExampleDependency $dependency): void
    {
        // Set dependencies...
    }

    // Some Livewire action methods here...
}

The first two categories do require binding. The first group are two way, and although the second group are only one way, they are small, and it is not a great bother they are send in the wrong direction.

But the $menus is an interesting one. I want to set it in the Component class, and it is already exposed in the template as I want, but this means that it becomes treated as a bound property, so it will be send from the client back to the server. (I don't want this to happen, as it is a waste of bandwidth, but moreover it unnecessarily complicates my server input filtering).

I suppose I can just set this in the template, but that feels a bit hacky. Is there a dynamic way I can pass PHP variables to the rendered template without them becoming bound in the two way process?

I've been reading about Wireables, and I suppose I could use that to collapse private data into a harmless null value. But that feels rather like a laborious hack, rather than a solution. Is there anything better?


Solution

  • Ah, having posted a question on this, I was inspired to try something, and I have a solution. The problem I had was that I did not have a render() method in my Component class, since by default it does not need one. Laravel uses some magic to derive a default Blade template from the name and folder of the class.

    In reality it is doing this under the hood:

    public function render(): View
    {
        return view('livewire.titlegen.form');
    }
    

    I think this Component was generated by an artisan function, and so I just used it as it came.

    With the above discovery made, I could pass a variable to the template:

    public function render(): View
    {
        return view(
            'livewire.titlegen.form',
            ['menus' => $this->menus, ]
        );
    }
    

    And then finally in the class properties, change it from public, which hides it from the two-way binding mechanism:

    protected array $menus = [
        // Long menu to render
    ];