@nent/core
Version:
313 lines (312 loc) • 9.97 kB
JavaScript
/*!
* NENT 2022
*/
import { Component, Element, h, Host, Prop, State, } from '@stencil/core';
import { commonState, CommonStateSubscriber, debugIf, valueToArray, warnIf, } from '../../services/common';
import { resolveChildElementXAttributes } from '../../services/data/elements';
import { DATA_EVENTS } from '../../services/data/interfaces';
import { filterData } from '../../services/data/jsonata.worker';
import { hasToken, resolveTokens } from '../../services/data/tokens';
import { ROUTE_EVENTS } from '../n-views/services/interfaces';
import { routingState } from '../n-views/services/state';
/**
* This tag renders a template for each item in the configured array.
* The item template uses value expressions to insert data from any
* data provider as well as the item in the array.
*
* @system content
* @extension data
* @extension elements
*/
export class ContentDataRepeat {
constructor() {
this.dynamicContent = null;
/**
* If set, disables auto-rendering of this instance.
* To fetch the contents change to false or remove
* attribute.
*/
this.deferLoad = false;
/**
* Turn on debug statements for load, update and render events.
*/
this.debug = false;
/**
* Force render with data & route changes.
*/
this.noCache = false;
}
get childTemplate() {
return this.el.querySelector('template');
}
get childScript() {
return this.el.querySelector('script');
}
componentWillLoad() {
debugIf(this.debug, 'n-content-repeat: loading');
this.dataSubscription = new CommonStateSubscriber(this, 'dataEnabled', DATA_EVENTS.DataChanged);
this.routeSubscription = new CommonStateSubscriber(this, 'routingEnabled', ROUTE_EVENTS.RouteChanged);
if (this.childTemplate === null) {
warnIf(this.debug, 'n-content-repeat: missing child <template> tag');
}
else {
this.innerTemplate = this.childTemplate.innerHTML;
}
this.contentKey = `data-content`;
}
async componentWillRender() {
var _a;
if (!this.innerTemplate)
return;
if (this.dynamicContent && !this.noCache) {
if (commonState.elementsEnabled) {
resolveChildElementXAttributes(this.el);
}
return;
}
const remoteContent = this.el.querySelector(`.${this.contentKey}`);
remoteContent === null || remoteContent === void 0 ? void 0 : remoteContent.remove();
const items = await this.resolveItems();
const innerContent = await this.resolveHtml(items);
if (innerContent) {
this.dynamicContent = this.el.ownerDocument.createElement('div');
this.dynamicContent.className = this.contentKey;
this.dynamicContent.innerHTML = innerContent;
if (commonState.elementsEnabled) {
resolveChildElementXAttributes(this.dynamicContent);
}
this.dynamicContent.innerHTML = innerContent;
if (routingState === null || routingState === void 0 ? void 0 : routingState.router) {
(_a = routingState.router) === null || _a === void 0 ? void 0 : _a.captureInnerLinks(this.dynamicContent);
}
this.el.append(this.dynamicContent);
}
}
async resolveHtml(items) {
debugIf(this.debug, 'n-content-repeat: resolving html');
let shouldRender = !this.deferLoad;
if (shouldRender && this.when && commonState.dataEnabled) {
const { evaluatePredicate } = await import('../../services/data/expressions');
shouldRender = await evaluatePredicate(this.when);
}
if (!shouldRender) {
return null;
}
// DebugIf(this.debug, `n-content-repeat: innerItems ${JSON.stringify(this.resolvedItems || [])}`);
if (this.innerTemplate) {
let resolvedTemplate = '';
return await items.reduce((previousPromise, item) => previousPromise.then(async () => resolveTokens(this.innerTemplate.slice(), false, item).then(html => {
resolvedTemplate += html;
return resolvedTemplate;
})), Promise.resolve());
}
return null;
}
async resolveItems() {
var _a;
let items = [];
if (this.items) {
items = await this.resolveItemsExpression();
}
else if (this.childScript) {
try {
let text = ((_a = this.childScript.textContent) === null || _a === void 0 ? void 0 : _a.replace('\n', '')) || '';
text =
commonState.dataEnabled && hasToken(text)
? await resolveTokens(text, true)
: text;
items = valueToArray(JSON.parse(text || '[]'));
}
catch (error) {
warnIf(this.debug, `n-content-repeat: unable to deserialize JSON: ${error}`);
}
}
else if (this.itemsSrc) {
items = await this.fetchJson();
}
else {
warnIf(this.debug, 'n-content-repeat: you must include at least one of the following: items, json-src or a <script> element with a JSON array.');
}
if (this.filter) {
let filterString = this.filter.slice();
if (hasToken(filterString)) {
filterString = await resolveTokens(filterString);
}
debugIf(this.debug, `n-content-repeat: filtering: ${filterString}`);
items = valueToArray(await filterData(filterString, items));
}
return items;
}
async fetchJson() {
try {
debugIf(this.debug, `n-content-repeat: fetching items from ${this.itemsSrc}`);
const response = await window.fetch(this.itemsSrc);
if (response.status === 200) {
const data = await response.json();
return valueToArray(data);
}
warnIf(this.debug, `n-content-repeat: Unable to parse response from ${this.itemsSrc}`);
}
catch (err) {
warnIf(this.debug, `n-content-repeat: Unable to parse response from ${this.itemsSrc}: ${err}`);
}
return [];
}
async resolveItemsExpression() {
let items = [];
try {
let itemsString = this.items;
if (itemsString && hasToken(itemsString)) {
itemsString = await resolveTokens(itemsString);
debugIf(this.debug, `n-content-repeat: items resolved to ${itemsString}`);
}
items = itemsString ? JSON.parse(itemsString) : [];
}
catch (error) {
warnIf(this.debug, `n-content-repeat: unable to deserialize JSON: ${error}`);
}
return items;
}
render() {
return (h(Host, null,
h("slot", null)));
}
disconnectedCallback() {
this.dataSubscription.destroy();
this.routeSubscription.destroy();
}
static get is() { return "n-content-repeat"; }
static get properties() { return {
"items": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The array-string or data expression to obtain a collection for rendering the template.\n{{session:cart.items}}"
},
"attribute": "items",
"reflect": false
},
"itemsSrc": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "example",
"text": "/data.json"
}],
"text": "The URL to remote JSON collection to use for the items."
},
"attribute": "items-src",
"reflect": false
},
"filter": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The JSONata query to filter the json items\nsee <https://try.jsonata.org> for more info."
},
"attribute": "filter",
"reflect": false
},
"deferLoad": {
"type": "boolean",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "If set, disables auto-rendering of this instance.\nTo fetch the contents change to false or remove\nattribute."
},
"attribute": "defer-load",
"reflect": false,
"defaultValue": "false"
},
"debug": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Turn on debug statements for load, update and render events."
},
"attribute": "debug",
"reflect": false,
"defaultValue": "false"
},
"noCache": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Force render with data & route changes."
},
"attribute": "no-cache",
"reflect": false,
"defaultValue": "false"
},
"when": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "A data-token predicate to advise this element when\nto render (useful if used in a dynamic route or if\ntokens are used in the 'src' attribute)"
},
"attribute": "when",
"reflect": false
}
}; }
static get states() { return {
"innerTemplate": {},
"resolvedTemplate": {},
"dynamicContent": {}
}; }
static get elementRef() { return "el"; }
}