Live toggle switch component in Drupal with Wire

Live toggle input

In this tutorial we'll create a stylish toggle switch button that saves its state on the server using Wire and Tailwind for stunning aesthetics.

We'll specifically focus on toggling the Nodes' published status value. If you haven't installed Wire in your Drupal project yet, no worries! Just follow the steps on the Installation page.

Once Wire is enabled, we'll dive right into creating a game-changing Wire Component.

Let's get started!

 

Create a Wire component with the following Drush command

drush generate wire

We'll assume you entered the Wire Label as Node Status Toggle in a module with wiredemo machine name 

Two new files were created in your chosen module

../wiredemo/src/Plugin/WireComponent/NodeStatusToggle.php 
../wiredemo/templates/templates/wire/node_status_toggle.html.twig

Let's add the toggle input in our node_status_toggle.html.twig

<div wire:key="node_status_{{ nodeId }}">
  
  <label class="mx-2 relative inline-flex items-center cursor-pointer">
    
    <input
      wire:model="nodeStatus"
      wire:loading.attr="disabled"
      type="checkbox"
      class="sr-only peer"
    >
    
    <div class="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-4 peer-focus:ring-blue-300 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600 peer-focus:outline-none"></div>
    
  </label>

</div>

Above we binded the input to nodeStatus wire model so let's get to the PHP part.

namespace Drupal\wiredemo\Plugin\WireComponent;

use Drupal\wire\View;
use Drupal\wire\WireComponent;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Implementation for Node Status Toggle Wire Component.
 *
 * @WireComponent(
 *   id = "node_status_toggle",
 *   label = @Translation("Node Status Toggle"),
 * )
 */
class NodeStatusToggle extends WireComponent {

  protected ?EntityTypeManagerInterface $entityTypeManager;

  public int $nodeId;

  public bool $nodeStatus;

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = new static($configuration, $plugin_id, $plugin_definition);
    $instance->entityTypeManager = $container->get('entity_type.manager');
    return $instance;
  }

  public function mount($nodeId): void {
    $this->nodeStatus = $this->entityTypeManager
      ->getStorage('node')
      ->load($nodeId)
      ->isPublished();
  }

  public function updatingNodeStatus(): void {
    $newStatus = !$this->nodeStatus;

    $this->entityTypeManager->getStorage('node')
      ->load($this->nodeId)
      ->set('status', $newStatus)
      ->save();

    $this->nodeStatus = $newStatus;
  }

  public function render(): ?View {
    return View::fromTpl('node_status_toggle');
  }

}

Above we laid the logic to handle live status change of a node with a toggle switch.

We declared two public properties $nodeId and $nodeStatus.

$nodeId is needed so we know with what Node we're dealing and $nodeStatus to toggle the status itself via our model declaration.

Initially the status is retrieved via the mount() method which only runs on the very first component render so we can show the existing status accordingly.

When the checkbox input state changes(automatically tracked as of wire:model directive btw) the  updatingNodeStatus method is invoked as it's part of available Lifecycle Hooks meaning any public property update can be intercepted with a method following the form: updating[camel-case-PropertyName].

Now, in order to plug-in our component in any of your templates where you have a Node ID available you can include the wire component as following:

{{ wire('node_status_toggle', {'nodeId': node.id}) }}

See Rendering Components for more info about how to render the component as well as how to include it via render element.

Personally, I'm using this implementation to allow users on jobsdrupal.com  to quickly hide or activate their job adverts without a page refresh.

Live status toggle for a list of Nodes
********************************** ************************* ************************ **************** ****************** *********** ************** ************* ************ *************