Properties

Wire components store and track data as public properties on the component class.

class HelloWorld extends WireComponent {

  public string $message = 'Hello World!';

  public function render(): ?View {

    $twig = <<<'TWIG'
      <div>
        <p>{{ message }}</p>
      </div>
    TWIG;

    return View::fromString($twig);
  }

}

No need to explicitly pass them into the view although you can if you want or maybe you need to to modify it, in which case it will be:

 ...
 
 return View::fromString($twig, ['message' => strtolower($this->message)]);

Essential things to note about public properties:

  • Property names can't conflict with property names reserved for Wire (e.g. id)
  • Data stored in public properties is made visible to the front-end JavaScript. Therefore, you SHOULD NOT store sensitive data in them.
  • Properties can ONLY be either JavaScript-friendly data types (string, int, array, boolean), OR one of the following PHP types: Stringable, Collection, DateTime.

`protected` and `private` properties DO NOT persist between Wire updates. Generally, you should avoid using them for storing state.
Also, note that while `null` data type is JavaScript-friendly, public properties set to `null` DO NOT persist between Wire updates. 

Initializing Properties

Properties can be initialized by using the mount method of the component.

class HelloWorld extends WireComponent {

  public string $message;

  public function mount() {

    $this->message = 'Hello World!';

  }

}

Additionally, a $this->fill() method is available for cases where you have to set lots of properties.

public function mount() {

  $this->fill(['message' => 'Hello World!']);

}

Also, Wire offers $this->reset() and $this->resetExcept() to programmatically reset public property values to their initial state. This is useful for cleaning input fields after performing an action.

public function resetFilters() {

  // Reset the search property only.
  $this->reset('search');
  
  // Reset both, search AND tags properties.
  $this->reset(['search', 'tags']);
  
  // Reset everything but the search property.
  $this->resetExcept('search');
  
}

Wire can "bind" (or "synchronize") the current value of some HTML element with a specific property in your component.

class HelloWorld extends WireComponent {

  public string $message;

}
<div>

  <input wire:model="message" type="text">

  <h1>{{ $message }}</h1>

</div>

When a user types something into the text field, the value of the $message property will automatically update.

Internally, Wire will listen for an input event on the element, and when triggered, it will send an AJAX request to re-render the component with the new data.

You can add wire:model to any element that dispatches an input event. Even custom elements, or third-party JavaScript libraries.

Common elements to use wire:model on include:
  • <input type="text">
  • <input type="radio">
  • <input type="checkbox">
  • <select>
  • <textarea>
 

Wire supports binding to nested data inside arrays using dot notation:

public array $formValues = [
  'name' => '',
  'email' => '',
];
<div>
  <input type="text" wire:model="formValues.name">
  <input type="text" wire:model="formValues.email">
</div>

By default, Wire applies a 150ms debounce to text inputs. This avoids too many network requests being sent as a user types into a text field.

If you wish to override this default (or add it to a non-text input), Wire offers a "debounce" modifier. If you want to apply a half-second debounce to an input, you would include the modifier like so:

<input type="text" wire:model.debounce.500ms="name">

By default, Wire sends a request to the server after every input event (or change in some cases). This is usually fine for things like <select> elements that don't typically fire rapid updates, however, this is often unnecessary for text fields that update as the user types.

In those cases, use the lazy directive modifier to listen for the native change event.

<input type="text" wire:model.lazy="name">

Now, the $name property will only be updated when the user clicks away from the input field. This is useful for validating user inputs.

In cases where you don't need data updates to happen live, Wire has a .defer modifier that batches data updates with the next network request.

Given the following component HTML

<input type="text" wire:model.defer="name">
<button wire:click="search">Search</button>

As the user types into the <input> field, no network requests will be sent. Even if the user clicks away from the input field and onto other fields on the page, no requests will be sent.

When the user presses "Search", only one network request that contains both the new "name" state, and the "search" action to perform.

This can drastically cut down on network usage when it's not needed. In fact, most of the times you might want to use it this way.

Sometimes you may want to have a component property which is not a data type supported by Wire.

For example, let’s say we have a custom object in our app called Settings. Rather than just store settings data as a plain array in the component, we can attach associated behavior to this data with a convenient wrapper object or DTO like Settings:

class Settings implements \Drupal\wire\Wireable {
  public array $items = [];
  
  public function __construct($items) {
    $this->items = $items;
  }
  
  public function toWire() {
    return $this->items;
  }
  
  public static function fromWire($value) {
    return new static($value);
  }
  
}

Now you can use this object as a public property of your component as long as that object implements the \Drupal\wire\Wireable interface AND the property is type-hinted like so:

class HelloWorld extends WireComponent {

  public Settings $settings;
  
  public function mount() {
    $this->settings = new Settings([
      'foo' => 'bar',
    ]);
  }
  
  public function changeSetting() {
    $this->settings->foo = 'baz';
  }
  
}

With this approach, changes to the component are persisted between requests.

********************************** ************************* ************************ **************** ****************** *********** ************** ************* ************ *************