MVVM based alternative to LitElement
npm install @kdsoft/lit-mvvmBased on lit-element. An alternative that replaces observable properties with an observable
and shareable view model. It also separates out render scheduling to be pluggable.
Package available from https://www.npmjs.com/package/@kdsoft/lit-mvvm.
There are two demo projects in the GitHub repository, they work best from VS Code (for instructions, see their README files).
An example for a simple check list that would be used like
where myModel needs to be an instance of MyChecklistModel (see the controls-demo for details):
``javascript
${item}
import { Queue, priorities } from '@nx-js/queue-util/dist/es.es6.js';
import { html, nothing } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { LitMvvmElement, css } from '@kdsoft/lit-mvvm';
class MyCheckList extends LitMvvmElement {
constructor() {
super();
// One can also set the global window._kd_soft.scheduler and leave this.scheduler unassigned,
// a shared scheduler allows us to have more control over render scheduling compared to
// each control having an independent scheduler.
this.scheduler = new Queue(priorities.HIGH);
this.getItemTemplate = item => html;
item-chk-${model.getItemId(item)}
}
get showCheckboxes() { return this.hasAttribute('show-checkboxes'); }
set showCheckboxes(val) {
if (val) this.setAttribute('show-checkboxes', '');
else this.removeAttribute('show-checkboxes');
}
// Observed attributes will trigger an attributeChangedCallback, which in turn
// will cause a re-render to be scheduled!
static get observedAttributes() {
return [...super.observedAttributes, 'show-checkboxes'];
}
_checkboxClicked(e) {
e.preventDefault();
// want to keep dropped list open for multiple selections
if (this.model.multiSelect) {
e.stopPropagation();
const itemDiv = e.currentTarget.closest('.list-item');
this.model.selectIndex(itemDiv.dataset.itemIndex, e.currentTarget.checked);
}
}
_itemClicked(e) {
const itemDiv = e.currentTarget.closest('.list-item');
if (this.model.multiSelect) {
this.model.toggleSelectedIndex(itemDiv.dataset.itemIndex);
} else { // on single select we don't toggle a clicked item
this.model.selectIndex(itemDiv.dataset.itemIndex, true);
}
}
_checkBoxTemplate(model, item) {
const chkid = ;
return html
tabindex="-1"
class="my-checkbox"
@click=${this._checkboxClicked}
.checked=${model.isItemSelected(item)}
?disabled=${item.disabled} />
;
}
_itemTemplate(item, indx, showCheckboxes) {
const disabledString = item.disabled ? 'disabled' : '';
const tabindex = indx === 0 ? '0' : '-1';
return html
;
}
// model may still be undefined
connectedCallback() {
super.connectedCallback();
}
disconnectedCallback() {
super.disconnectedCallback();
}
shouldRender() {
return !!this.model;
}
// only called when model is defined, due to the shouldRender() override
beforeFirstRender() {
//
}
firstRendered() {
//
}
rendered() {
//
}
static get styles() {
return [
css
,
];
}
render() {
return html
;
}
}
window.customElements.define('my-checklist', MyCheckList);
`
Styling component externally
There are several ways of styling a component from the outside
1. Use elements.
* The slotted components are already styled when assigned to the slot.
* That styling is based on where these components are placed in the light DOM.
2. Use ::part() selectors.
* A Shadow Dom node can be identified with a "part" attribute and referenced from the outside using a ::part() selector.
* There are limitations: children of the part cannot be selected/styled that way, only the part itself.
* Nested parts need to be exported explicitly using the "exportedparts" attribute.
3. Inject a stylesheet into the component
* A stylesheet can be added like this, in the beforeFirstRender() override:
this.renderRoot.adoptedStyleSheets = [...this.renderRoot.adoptedStyleSheets, newStyleSheet];
4. Note: vs callbacks
* In a hierarchical structure like a tree view it is hard to syntactically add slotted components especially when the tree view is dynamically constructed based on a recursive hierarchical model.
* One can use inheritance and specify a "renderNode(nodeModel)" method to override in a derived class. See demo-tree-view-menu.js in the demo/controls` directory.