diff --git a/ignite-template.js b/ignite-template.js index 2139600..e547a7f 100644 --- a/ignite-template.js +++ b/ignite-template.js @@ -385,7 +385,7 @@ class IgniteTemplate { /** * Sets the inner html of the element to be constructed by this template. - * @param {String|IgniteProperty} value InnerHTML to set for element. If a property is passed the html will auto update. + * @param {String|IgniteProperty|IgniteProperty[]} value InnerHTML to set for element. If a property is passed the html will auto update. * @param {Function} converter Optional function that can be used to convert the value if needed. * @returns This ignite template so funciton calls can be chained. */ @@ -2528,6 +2528,128 @@ class list extends IgniteTemplate { } } +/** + * A special ignite template that converts a value to HTML or a Template and supports + * dynamic value changes and dynamic converter changes. + */ +class converter extends IgniteTemplate { + constructor(value, converter) { + super(); + + if (converter instanceof IgniteProperty) { + this._callbacks.push(converter.attachOnChange((oldValue, newValue) => this.onConverterChanged(newValue))); + + this.converter = converter.value; + } else if (converter instanceof Function) { + this.converter = converter; + } + + if (value instanceof IgniteProperty) { + this._callbacks.push(value.attachOnChange((oldValue, newValue) => this.onValueChanged(newValue))); + this._callbacks.push(value.attachOnPush((list, items) => this.onValueChanged(list))); + this._callbacks.push(value.attachOnUnshift((list, items) => this.onValueChanged(list))); + this._callbacks.push(value.attachOnPop(list => this.onValueChanged(list))); + this._callbacks.push(value.attachOnShift(list => this.onValueChanged(list))); + this._callbacks.push(value.attachOnSplice((list, start, deleteCount, items) => this.onValueChanged(list))); + + this.value = value.value; + } else if (value) { + this.value = value; + } + + var child = this.value; + + if (this.converter) { + child = this.converter(this.value); + } + + if (child instanceof IgniteTemplate) { + this.child = child; + } else if (child) { + this.child = new html(child); + } else { + this.child = null; + } + + this.tagName = "converter"; + } + + construct(parent, sibling) { + //Don't construct if we have no parent, no sibling and no element. + if (!parent && !sibling && !this.element) { + return; + } + + if (!this.element) { + this.element = window.document.createTextNode(""); //Use a textnode as our placeholder + + if (sibling) { + sibling.parentElement.insertBefore(this.element, sibling); + } else { + parent.appendChild(this.element); + } + } else { + parent = this.element.parentElement; + } + + if (this.child) { + this.child.construct(parent, this.element); + } + } + + onValueChanged(newValue) { + this.value = newValue; + + if (this.child) { + this.child.deconstruct(); + } + + var child = newValue; + + if (this.converter) { + child = this.converter(child); + } + + if (child instanceof IgniteTemplate) { + this.child = child; + + this.child.construct(null, this.element); + } else if (child) { + this.child = new html(child); + + this.child.construct(null, this.element); + } else { + this.child = null; + } + } + + onConverterChanged(newConverter) { + this.converter = newConverter; + + if (this.child) { + this.child.deconstruct(); + } + + var child = this.value; + + if (this.converter) { + child = this.converter(child); + } + + if (child instanceof IgniteTemplate) { + this.child = child; + + this.child.construct(null, this.element); + } else if (child) { + this.child = new html(child); + + this.child.construct(null, this.element); + } else { + this.child = null; + } + } +} + /** * A slot template that mimicks the functionality of a slot element in Web Components. This can * be used to place children of a IgniteElement anywhere in the DOM. Slots don't actually construct an element, @@ -3417,6 +3539,7 @@ export { text, html, list, + converter, a, input, textarea,