Stimulus Outlet API

เราเคยพูดถึงเรื่องการสื่อสารกันระหว่าง controller ผ่านการใช้ Custom Event 🔗 เมื่อ Stimulus 3.2 ได้ปล่อย Outlet API ออกมาเพื่อใช้ในการสื่อสารระหว่าง controller ผ่าน outlet ซึ่งเป็นอีกทางเลือกหนึ่ง

นิยาม outlet attribute เพื่อใช้สื่อสารข้ามระหว่าง controller ดังนั้นเราต้องกำหนด outlet ที่จะคุยด้วย และอ้างอิงถึงคอมโพเนนต์ผ่าน CSS Selector

// Syntax
data-[identifier]-[outlet]-outlet="[selector]"

// Example
<ul id="sidebar" data-controller="sidebar" data-sidebar-content-outlet="#area">

ตัวอย่างข้างต้น จะเห็นว่า sidebar controller ต้องการสื่อสารกับ content controller (outlet) ผ่าน #area (id="area")

สำหรับโค้ดที่สมบูรณ์จะอยู่ด้านล่าง โดยเราจะทำตัวอย่างให้ใกล้เคียงกับตัวอย่างของบทความที่แล้ว

<div class="flex flex-row">
  <div class="flex flex-row gap-4 w-full">
    <ul id="sidebar" data-controller="sidebar" data-sidebar-content-outlet="#area">
      <li>
        <button class="w-48 border rounded-lg bg-gray-100 cursor-pointer mb-2 p-4 text-left" data-action="click->sidebar#load" data-url="/about">About</button>
      </li>
      <li>
        <button class="w-48 border rounded-lg bg-gray-100 cursor-pointer mb-2 p-4 text-left" data-action="click->sidebar#load" data-url="/contact">Contact</button>
      </li>
    </ul>
    <div id="area" class="w-full border rounded-lg bg-gray-100 p-4" data-controller="content">
    </div>
  </div>
</div>
// sidebar_controller.js
import { Controller } from "@hotwired/stimulus"
import { get } from "@rails/request.js";

export default class extends Controller {
  static outlets = [ "content" ]
  async connect() {
    const element = this.element.querySelector("[data-url]")
    if (element) {
      await this.loadContent(element)
    }
  }

  async load(event) {
    await this.loadContent(event.target)
  }

  async loadContent(element) {
    const url = element.getAttribute("data-url")
    const response = await get(url)
    if (response.ok) {
      this.contentOutletElement.innerHTML = await response.html
    }
  }
}

ตัวอย่าง