0.9.0 • Published 5 years ago

@lit-any/core v0.9.0

Weekly downloads
2
License
MIT
Repository
-
Last release
5 years ago

lit-any codecov

Building late-bound User Interface with lit-html without actually creating (too many) custom elements

Quick guide

1. Install

yarn add @lit-any/lit-any

2. Set up how to render your content

You want to display an object which represents a person but the avatar comes in two flavors. Of course, it's possible to keep abusing if statements or drop in a <template is="dom-if"> (old syntax, I know).

With lit-any you can deconstruct your HTML by defining partial templates which will be rendered when they are really needed.

import ViewTemplates from '@lit-any/lit-any/views';
import { html } from 'lit-html';

ViewTemplates.default.when
    .value(isPerson)
    .renders((renderFunc, person) => html`
        <person-element .name="${person.name}">
            <span slot="avatar">
                ${renderFunc(person.avatar, 'person-element-avatar')}
            </span>
        </person-element>
    `);

ViewTemplates.default.when
    .scope('person-element-avatar')
    .value(v => v.url)
    .renders((_, image) => html`
        <img src="${image.url}" alt="avatar" />
    `);

ViewTemplates.default.when
    .scope('person-element-avatar')
    .value(v => v.large)
    .renders((_, image) => html`
        <a href="${image.url}">
            <img src="${image.large}" alt="avatar" />
        </a>
    `);

function isPerson(value) {
    return value.type === 'Person';
}

This will set up the rendering so that <person-element> is displayed for a person but the avatar will be rendered based on the presence of the large property.

3a. Render anywhere

To do actual rendering you don't really need a dedicated custom element. Any container will be fine

<div id="personContainer"></div>
import render from '@lit-any/lit-any/render';

const person = {
    type: 'Person',
    image: {
        url: 'https://s.gravatar.com/avatar/1497654c2d1af3cef4987234d1aced57?s=80',
        large: 'https://s.gravatar.com/avatar/1497654c2d1af3cef4987234d1aced57?s=800'
    }
};

const personContainer = document.querySelector('#personContainer');

render({ value: person }, personContainer);

3b. Render element

Alternatively you can use the lit-view instead of rendering directly to any node.

<lit-view id="personContainer"></lit-view>
const person = {
    type: 'Person',
    image: {
        url: 'https://s.gravatar.com/avatar/1497654c2d1af3cef4987234d1aced57?s=80',
        large: 'https://s.gravatar.com/avatar/1497654c2d1af3cef4987234d1aced57?s=800'
    }
};

const personContainer = document.querySelector('#personContainer');

personContainer.value = person;

More features

Scoping

When you want to display same data differently in different context.

import ViewTemplates from '@lit-any/lit-any/views';
import { html } from 'lit-html';
import * as moment from 'moment';

// show nicely formatted date by default
ViewTemplates.default.when
    .value(v => (v instanceOf Date))
    .renders((_, date) => html`<span>${moment(date).format('LL')}</span>`);

// but display calendar in 'event-large' scope
ViewTemplates.default.when
    .value(v => (v instanceOf Date))
    .scope('event-large')
    .renders((_, date) => html`<datetime-picker disabled .datetime="${date}"></datetime-picker>`);

Then set the scope on lit-view element:

<lit-view .value="{{someDate}}" template-scope="event-large"></lit-view>

Or pass to the render function:

import render from '@lit-any/lit-any/render';
import ViewTemplates from '@lit-any/lit-any/views';

const eventDateElement = document.querySelector('#eventDate').

render(ViewTemplates.default, {
    value: new Date(),
    scope: 'event-large',
}, eventDateElement);

Or override from parent template:

ViewTemplates.default.when
    .value(v => v.type === 'Event')
    .renders((renderChild, event) => html`
        <my-event-element>
            <div slot="calendar">
                ${renderChild(event.date, 'event-large')}
            </div>
        </my-event-element>
    `);

3. Rendering forms

Set up how you will render input controls. The set parameter is a function used to set tha value back to the form's model and has to be bound to the input control.

import FieldTemplates from '@lit-any/lit-any/forms';

FieldTemplates.default.when
  .fieldMatches(field => field.type === 'http://www.w3.org/2001/XMLSchema#integer')
  .renders((field, id, value, set) => {
    return html`<input type=number value=${value}
                       @change=${e => set(Number.parseInt(e.target.value, 0))}>`;
  });

Create a form definition:

const contract = {
  fields: [{
    title: 'Age',
    property: 'age',
    type: 'http://www.w3.org/2001/XMLSchema#integer'
  }]
};

Create a form on your page

<lit-form .contract=${contract}
          submit-button-label="Register"
          @submit=${submitModel}></lit-form>

When the Register button is clicked submitModel will be called where the code will retrieve the form's value from event.detail.value.

function submitModel(e) {
  // submit lit-form's value
  fetch(e.detail.target, {
    method: e.detail.method,
    body: e.detail.value
  });
}
0.9.0

5 years ago

0.9.0-a3

5 years ago

0.9.0-a2

5 years ago

0.9.0-a1

5 years ago