dna-poster

DNA

Progressive Web Components.

Events

Since Web Components extend the native HTMLElement, events handling is completly delegated to the DOM implementation, so you can use addEventListener and removeEventListener to properly setup a callback. DNA add some extra features like declarative event listeners and delegations.

Declarative event listeners

You can declare event listeners on a component using the listeners accessor:

import { Component, customElements } from '@chialab/dna';

class Button extends Component {
    static get listeners() {
        return {
            'click': Button.prototype.onClick,
            'input [name="age"]': Button.prototype.onInputAge,
        };
    };

    onClick(event) {
        event.preventDefault();
    }

    onInputAge(event, target) {
        console.log(target.value);
    }
}

customElements.define('x-button', Button, { extends: 'button' });

Event declaration accepts a function (in the example above, a prototype method has been referenced) or an object for listener configuration:

import { Component, customElements } from '@chialab/dna';

class Tracker extends Component {
    static get listeners(){
        return {
            touchmove: {
                callback: function(event) {
                    // ...
                },
                passive: true,
            },
        };
    };
}

customElements.define('x-tracker', Tracker);

Template listeners

Listeners can be added via a template attribute named as the event with the on prefix:

import { Component, customElements, html } from '@chialab/dna';

class Header extends Component {
    render() {
        return html`
            <h2>${this.title}</h2>
            <nav>
                <button onclick=${this.onNavClick}>Close</button>
            </nav>
        `;
    }
}

customElements.define('x-header', Header, { extends: 'header' });

Delegation

DNA supports event delegation for both imperatively and declaratively declarations. The listener callback will receive the original fired event as first argument and the matched target as second argument.

Using the listeners getter, you can specify the delegated child selector after the event name in the declaration key:

import { Component, customElements } from '@chialab/dna';

class Dialog extends Component {
    static get listeners() {
        return {
            // event name + selector
            'click nav button': {
                callback: function(event, target) {
                    ...
                },
                passive: false,
            },
        };
    };
}

customElements.define('x-dialog', Dialog, { extends: 'dialog' });

Otherwise, you can use addEventListener, removeEventListener, delegateEventListener and undelegateEventListener methods:

import { Component, customElements, DOM } from '@chialab/idom';

class Dialog extends Component { ... }
customElements.define('x-dialog', Dialog, { extends: 'dialog' });

const element = new Dialog();
const closeDialog = (event, target) => { ... };
DOM.appendChild(document.body, element);
// delegate listener
element.delegateEventListener('click', 'nav button', closeDialog, { passive: false });
// undelegate listener
element.undelegateEventListener('click', 'nav button', closeDialog);
Dispatching events

DNA components overrides the dispatchEvent method in order to support an alternative signature for easier CustomEvents creation:

import { Component, customElements, DOM } from '@chialab/dna';

class Button extends Component { ... }
customElements.define('x-button', Button, { extends: 'button' });

const button = new Button();
DOM.appendChild(document.body, element);
// native dispatch
const event = new CustomEvent('sendEmail', {
    detail: { from: '...', to: '...', body: '...' },
    bubbles: true,
    cancelable: true,
    composed: false,
});
button.dispatchEvent(event);

// DNA alternative signature
button.dispatchEvent('sendEmail',
    // detail
    { from: '...', to: '...', body: '...' },
    // bubbles
    true,
    // cancelable
    true,
    // composed
    false
);

Async dispatch

With DNA, you can also dispatch events and await a Promise which resolves when all async listeners are completed:

import { Component, customElements, DOM } from '@chialab/dna';

class Paginator extends Component {
    static get listeners() {
        return {
            'click button.next': async function() {
                this.data = await this.dispatchAsyncEvent('fetch');
            },
        };
    };
}

customElements.define('x-paginator', Paginator);

const paginator = new Paginator();
paginator.addEventListener('fetch', (event) => {
    event.respondWith(async () => {
        const response = await fetch('/posts');
        return await response.json();
    });
});