Last active
May 30, 2026 10:02
-
-
Save wtnabe/a1fffb4926a6e7b47a92a40c4598f61c to your computer and use it in GitHub Desktop.
Alien Signals x LitElement x Custom Model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="ja"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>alien-signals x LitElement x custom model</title> | |
| </head> | |
| <body> | |
| <app-selector></app-selector> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "lit": "https://esm.sh/lit", | |
| "alien-signals": "https://esm.sh/alien-signals" | |
| } | |
| } | |
| </script> | |
| <script type="module"> | |
| import { LitElement, html } from 'lit' | |
| import { signal, effect } from 'alien-signals' | |
| /** | |
| * @typedef {Object} Item | |
| * @property {string} value | |
| * @property {string} label | |
| * @property {string} description | |
| */ | |
| /** | |
| * @typedef {Object} Model | |
| * @property {Item[]} items | |
| * @property {Signal} selection | |
| * @property {Function} pickAndSetItem | |
| */ | |
| class Model { | |
| items = [ | |
| { value: 'apple', label: 'Apple 🍎', description: '赤い果物' }, | |
| { value: 'banana', label: 'Banana 🍌', description: '黄色い果物' }, | |
| { value: 'cherry', label: 'Cherry 🍒', description: '小さな赤い果物' }, | |
| ] | |
| selection = signal(null) | |
| pickAndSetItem = (e) => { | |
| this.selection(e.target.value || null) | |
| } | |
| } | |
| class AppSelector extends LitElement { | |
| static properties = { | |
| currentItem: { attribute: false } | |
| } | |
| connectedCallback() { | |
| super.connectedCallback(); | |
| this.model = new Model() | |
| this.stop ||= effect(() => { | |
| this.currentItem = this.model.items.find((i) => i.value === this.model.selection()) ?? null | |
| }) | |
| } | |
| disconnectedCallback() { | |
| super.disconnectedCallback(); | |
| this.stop?.() | |
| this.stop = null | |
| } | |
| render() { | |
| return html` | |
| <item-selector | |
| .items=${this.model.items} | |
| .changeHandler=${this.model.pickAndSetItem} | |
| ></item-selector> | |
| <display-component .item=${this.currentItem}></display-component> | |
| ` | |
| } | |
| } | |
| customElements.define('app-selector', AppSelector) | |
| class ItemSelector extends LitElement { | |
| static properties = { | |
| /** @type {Item} */ | |
| items: { type: Array, attribute: false }, | |
| /** @type {Function} */ | |
| changeHandler: { attribute: false } | |
| } | |
| render() { | |
| return html` | |
| <label> | |
| アイテムを選択: | |
| <select @change=${this.changeHandler}> | |
| <option value="">-- 選択してください --</option> | |
| ${this.items.map((item) => html`<option value=${item.value}>${item.label}</option>`)} | |
| </select> | |
| </label> | |
| ` | |
| } | |
| } | |
| customElements.define('item-selector', ItemSelector) | |
| class DisplayComponent extends LitElement { | |
| static properties = { | |
| /** @type {Item | null} item */ | |
| item: { attribute: false } | |
| } | |
| render() { | |
| return html` | |
| <div> | |
| <p>Selected: <strong>${this.item?.label ?? '(none)'}</strong></p> | |
| ${this.item ? html`<p>${this.item.description}</p>` : ''} | |
| </div> | |
| ` | |
| } | |
| } | |
| customElements.define('display-component', DisplayComponent) | |
| </script> | |
| </body> | |
| </html> |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
メモ
設計について
alien-signalsについて
set(),get()ではなく、引数を与えるか与えないかで挙動が変わるNotes
Design
About alian-signals
set()orget(); its behavior changes depending on whether an argument is provided or not.