diff --git a/README.md b/README.md index c72bfda..fae582c 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,25 @@ There a quite a few more, for the full list see the [sprae README](https://githu ## Sending events -To send events to a LiveState backend, the `sendEvent()` function is provided and able to be called from event handlers in the template. It takes the name of the event to send to the channel, and will convert DOM events as follows: +Sending events to the LiveState channel can be done declaratively or programmatically. + +### Declarative event sending + +Sprae directives have been added for several events: + +* :sendclick +* :sendsubmit +* :sendinput + +The value of the attribute for each will specify the event name to send to the LiveState channel, similar to LiveView `phx-*` attributes. Example: + +```html + +``` + +### Programmatic event sending + +To programmatically send events to a LiveState channel, the `sendEvent()` function is provided and able to be called from event handlers in the template. It takes the name of the event to send to the channel, and will convert DOM events as follows: * submit and input events will send the FormData and prevent the default event behaviour. * click events will send the dataset of the element (any `data-` attributes). @@ -131,9 +149,13 @@ Example: ``` -## Status +## Demo + +The `silly_crm.html` shows a working CRUD example. It is a front end to the [silly_crm](http://github.com/superchris/silly_crm) project. To run it: -live-templates should be considered alpha quality. +1. Run the sillycrm project on localhost:4000 (instructions are in README) +2. Run `npm start` in this project +2. Go to http://localhost:8080/silly_crm.html ## Future plans diff --git a/example.html b/example.html index 9d41e35..aab30d8 100644 --- a/example.html +++ b/example.html @@ -31,6 +31,6 @@ - + \ No newline at end of file diff --git a/package.json b/package.json index da6e0c6..dd59db5 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "", "license": "MIT", "author": "live-template", - "version": "0.3.0", + "version": "0.4.0", "type": "module", "main": "index.js", "module": "index.js", diff --git a/silly_crm.html b/silly_crm.html new file mode 100644 index 0000000..e4945fa --- /dev/null +++ b/silly_crm.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + +
Last nameFirst name
+ + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/live-template.js b/src/live-template.js index 3b34438..505165e 100644 --- a/src/live-template.js +++ b/src/live-template.js @@ -1,6 +1,6 @@ import LiveState from 'phx-live-state'; import { registerContext, observeContext } from 'wc-context'; -import sprae from 'sprae'; +import sprae, { directive } from 'sprae'; export class LiveTemplateElement extends HTMLElement { connectedCallback() { @@ -28,7 +28,10 @@ export class LiveTemplateElement extends HTMLElement { this.liveState.connect(); this.liveState.addEventListener('livestate-change', ({ detail: { state } }) => { this.buildTemplate(); - sprae(this, {...state, sendEvent: (n) => (e) => this.sendEvent(n, e)}); + directive.sendclick = this.sendEventDirective('click'); + directive.sendsubmit = this.sendEventDirective('submit'); + directive.sendinput = this.sendEventDirective('input'); + sprae(this, { ...state, sendEvent: (n) => (e) => this.sendEvent(n, e) }); }); } @@ -38,6 +41,20 @@ export class LiveTemplateElement extends HTMLElement { this.replaceChildren(template.content); } } + + sendEventDirective(eventName) { + return (el, evaluate, state) => { + let removeOldListener; + return () => { + removeOldListener?.(); + const handler = (e) => { + this.sendEvent(evaluate, e); + }; + removeOldListener = () => el.removeEventListener(eventName, handler); + el.addEventListener(eventName, handler); + } + } + } sendEvent(eventName, e) { if (e instanceof SubmitEvent) { diff --git a/test/live-template-test.js b/test/live-template-test.js index 8992fa6..af0a7e6 100644 --- a/test/live-template-test.js +++ b/test/live-template-test.js @@ -63,6 +63,26 @@ describe('render template', () => { expect(pushCall.args[1].foo).to.equal('bar'); expect(pushCall.args[1].bar).to.equal('wuzzle'); }); + it('sends events from directivs', async () => { + const el = await fixture(` + +
+ + + +
+
+ `); + setupLiveState(el); + const pushStub = sinon.stub(); + el.liveState.pushEvent = pushStub; + const button = el.querySelector('button'); + button.click(); + const pushCall = pushStub.getCall(0); + expect(pushCall.args[0]).to.equal('it'); + expect(pushCall.args[1].foo).to.equal('bar'); + expect(pushCall.args[1].bar).to.equal('wuzzle'); + }); it('allows for nested templates and fallback content', async () => { const el = await fixture(`