Skip to content

Commit

Permalink
Enables synchronous render on construction of Template() by passing d…
Browse files Browse the repository at this point in the history
…ata object in
  • Loading branch information
stephband committed Jul 14, 2024
1 parent f2d617e commit 54a9dde
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 15 deletions.
2 changes: 1 addition & 1 deletion modules/define-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default function defineElement(tag, src, lifecycle = {}, props, scope = {
return element(tag, {
construct: function(shadow) {
const internals = getInternals(this);
const renderer = internals.renderer = new Template(template, this, assign({}, scope, {
const renderer = internals.renderer = new Template(template, this, assign({}, scope, undefined, {
host: this,
shadow: shadow
}));
Expand Down
12 changes: 12 additions & 0 deletions modules/renderer/renderer-boolean.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

import Signal from '../../../fn/modules/signal.js';
import composeBoolean from './compose-boolean.js';
import AttributeRenderer from './renderer-attribute.js';
import { stats } from './renderer.js';
Expand Down Expand Up @@ -31,6 +33,16 @@ function setBooleanAttribute(node, name, value) {
export default class BooleanRenderer extends AttributeRenderer {
static parameterNames = AttributeRenderer.parameterNames;

/* Only needed to evaluate */
constructor(signal, fn, parameters, element, name) {
super(signal, fn, parameters, element, name);

// A synchronous evaluation while data signal value is undefined binds
// this renderer to changes to that signal. If signal value is an `data`
// object it renders the renderer immediately.
Signal.evaluate(this, this.evaluate);
}

render(strings) {
const value = composeBoolean(arguments);

Expand Down
6 changes: 6 additions & 0 deletions modules/renderer/renderer-checked.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import id from '../../../fn/modules/id.js';
import isDefined from '../../../fn/modules/is-defined.js';
import Signal from '../../../fn/modules/signal.js';
import trigger from '../../../dom/modules/trigger.js';
import config from '../config.js';
import bindChecked from '../scope/bind-checked.js';
Expand Down Expand Up @@ -58,6 +59,11 @@ export default class CheckedRenderer extends AttributeRenderer {

// Flag whether element has a value attribute
this.hasValue = isDefined(element.getAttribute('value'));

// A synchronous evaluation while data signal value is undefined binds
// this renderer to changes to that signal. If signal value is an `data`
// object it renders the renderer immediately.
Signal.evaluate(this, this.evaluate);
}

/*
Expand Down
5 changes: 5 additions & 0 deletions modules/renderer/renderer-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ export default class TextRenderer extends Renderer {
// https://developer.mozilla.org/en-US/docs/Web/API/Range
this.first = node;
this.last = node.nextSibling;

// A synchronous evaluation while data signal value is undefined binds
// this renderer to changes to that signal. If signal value is an `data`
// object it renders the renderer immediately.
Signal.evaluate(this, this.evaluate);
}

update() {
Expand Down
6 changes: 6 additions & 0 deletions modules/renderer/renderer-tokens.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import id from '../../../fn/modules/id.js';
import overload from '../../../fn/modules/overload.js';
import Signal from '../../../fn/modules/signal.js';
import toText from './to-text.js';
import AttributeRenderer from './renderer-attribute.js';
import { stats } from './renderer.js';
Expand Down Expand Up @@ -50,6 +51,11 @@ export default class TokensRenderer extends AttributeRenderer {
// Renderer properties
this.list = element[this.property];
this.tokens = nothing;

// A synchronous evaluation while data signal value is undefined binds
// this renderer to changes to that signal. If signal value is an `data`
// object it renders the renderer immediately.
Signal.evaluate(this, this.evaluate);
}

render(strings) {
Expand Down
6 changes: 6 additions & 0 deletions modules/renderer/renderer-value.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import id from '../../../fn/modules/id.js';
import overload from '../../../fn/modules/overload.js';
import Signal from '../../../fn/modules/signal.js';
import config from '../config.js';
import bindValue from '../scope/bind-value.js';
import AttributeRenderer from './renderer-attribute.js';
Expand Down Expand Up @@ -40,6 +41,11 @@ export default class ValueRenderer extends AttributeRenderer {

constructor(signal, fn, parameters, element) {
super(signal, fn, parameters, element, 'value');

// A synchronous evaluation while data signal value is undefined binds
// this renderer to changes to that signal. If signal value is an `data`
// object it renders the renderer immediately.
Signal.evaluate(this, this.evaluate);
}

render(strings) {
Expand Down
13 changes: 3 additions & 10 deletions modules/renderer/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,9 @@ export default class Renderer {

// Track the number of renderers created
if (window.DEBUG) { ++Renderer.count; }

// A synchronous evaluation while signal value is undefined binds this
// renderer to changes to the data object. The signal must be empty
// during construction, and be given value later. (Otherwise, this will
// have to be moved to the bottom of each constructor so the renderers
// are fully ready.)
Signal.evaluate(this, this.#evaluate);
}

#evaluate() {
evaluate() {
// Bind this renderer to current data
const data = this.#data.value;

Expand Down Expand Up @@ -187,7 +180,7 @@ export default class Renderer {
try {
++this.renderCount;
// Evaluation causes DOM render
Signal.evaluate(this, this.#evaluate);
Signal.evaluate(this, this.evaluate);
}
catch(e) {
// TODO: add template id to error message
Expand All @@ -197,7 +190,7 @@ export default class Renderer {
}
else {
++this.renderCount;
Signal.evaluate(this, this.#evaluate);
Signal.evaluate(this, this.evaluate);
}

this.status = this.status === 'rendering' ? 'idle' : this.status ;
Expand Down
4 changes: 2 additions & 2 deletions modules/scope/include.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import requestTemplate from '../request-template.js';
import requestData from '../request-data.js';

function push(template, data, element, parameters, options) {
const renderer = new Template(template, element, parameters, options);
renderer.push(data);
const renderer = new Template(template, element, parameters, data, options);
//renderer.push(data);
return renderer;
}

Expand Down
4 changes: 2 additions & 2 deletions modules/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default class Template {
// Data signal
#data;

constructor(template, parent = template.parentElement, parameters = {}, options = defaults) {
constructor(template, parent = template.parentElement, parameters = {}, data, options = defaults) {
// A literal template may be created from inside a TextRenderer via
// ${ include(...) } and for this reason we must avoid accessing any
// signals outside of a Signal.evaluate(), or they are registered as
Expand All @@ -132,7 +132,7 @@ export default class Template {
this.parameters = parameters;
this.first = content.childNodes[0];
this.last = content.childNodes[content.childNodes.length - 1];
this.#data = Signal.of();
this.#data = Signal.of(data);
this.contents = compiled.targets
// We must find targets in cloned content
.map(this.#toRendererParams, this)
Expand Down

0 comments on commit 54a9dde

Please sign in to comment.