diff --git a/src/ignite-html.js b/src/ignite-html.js index 064ab8d..483d0f8 100644 --- a/src/ignite-html.js +++ b/src/ignite-html.js @@ -7,10 +7,11 @@ class IgniteProperty { this.onChangeCallbacks = []; this.onPushCallbacks = []; this.onPopCallbacks = []; + this.callbacks = []; this._value = val; //Attempt to patch the value if it's a list. - this.patchListValue(); + this.patchList(); } get value() { @@ -21,28 +22,59 @@ class IgniteProperty { var old = this._value; this._value = val; + //Disconnect any existing callbacks + this.callbacks.forEach(callback => callback.disconnect()); + this.callbacks = []; + //Attempt to patch the value if it's a list. - this.patchListValue(); + this.patchList(); //Invoke any callbacks letting them know the value changed. this.invokeOnChange(old, val); } - patchListValue() { - if (Array.isArray(this._value)) { - let prop = this; + patchList() { + if (Array.isArray(this._value) && this._value.onPushCallbacks == undefined) { + this._value.onPushCallbacks = []; + this._value.onPopCallbacks = []; this._value.push = function () { var len = Array.prototype.push.apply(this, arguments); - prop.invokeOnPush(); + this.onPushCallbacks.forEach((callback) => callback.invoke(arguments[0])); return len; }; this._value.pop = function () { var len = Array.prototype.pop.apply(this, arguments); - prop.invokeOnPop(); + this.onPopCallbacks.forEach((callback) => callback.invoke()); return len; } + + this._value.attachOnPush = function (func) { + var callback = new IgniteCallback(func, (detach) => this.detachOnPush(detach)); + this.onPushCallbacks.push(callback); + return callback; + } + + this._value.attachOnPop = function (func) { + var callback = new IgniteCallback(func, (detach) => this.detachOnPop(detach)); + this.onPopCallbacks.push(callback); + return callback; + } + + this._value.detachOnPush = function (callback) { + this.onPushCallbacks = this.onPushCallbacks.filter(push => push != callback); + } + + this._value.detachOnPop = function (callback) { + this.onPopCallbacks = this.onPopCallbacks.filter(pop => pop != callback); + } + + this.callbacks.push(this._value.attachOnPush(() => this.invokeOnPush())); + this.callbacks.push(this._value.attachOnPop(() => this.invokeOnPop())); + } else if (Array.isArray(this._value) && this._value.onPushCallbacks) { + this.callbacks.push(this._value.attachOnPush(() => this.invokeOnPush())); + this.callbacks.push(this._value.attachOnPop(() => this.invokeOnPop())); } } @@ -65,22 +97,34 @@ class IgniteProperty { } attachOnChange(onChange) { - var callback = new IgnitePropertyOnChangeCallback(this, onChange); + var callback = new IgniteCallback(onChange, (detach) => this.detachOnChange(detach)); this.onChangeCallbacks.push(callback); return callback; } attachOnPush(onPush) { - var callback = new IgnitePropertyOnPushCallback(this, onPush); + var callback = new IgniteCallback(onPush, (detach) => this.detachOnPush(detach)); this.onPushCallbacks.push(callback); return callback; } attachOnPop(onPop) { - var callback = new IgnitePropertyOnPopCallback(this, onPop); + var callback = new IgniteCallback(onPop, (detach) => this.detachOnPop(detach)); this.onPopCallbacks.push(callback); return callback; } + + detachOnChange(callback) { + this.onChangeCallbacks = this.onChangeCallbacks.filter(change => change != callback); + } + + detachOnPush(callback) { + this.onPushCallbacks = this.onPushCallbacks.filter(push => push != callback); + } + + detachOnPop(callback) { + this.onPopCallbacks = this.onPopCallbacks.filter(pop => pop != callback); + } } /** @@ -92,79 +136,28 @@ IgniteProperty.prototype.toString = function () { } /** - * The outline of a ignite property onchange callback that allows - * disconnecting of callbacks on demand when they are no longer needed. + * The outline of a ignite callback that can be invoked and disconnected + * to help maintain and cleanup callbacks. */ -class IgnitePropertyOnChangeCallback { - constructor(property, callback) { +class IgniteCallback { + constructor(callback, detach) { this.callback = callback; - this.property = property; + this.detach = detach; } - invoke(oldValue, newValue) { + invoke(...params) { + console.log("Invoke with params:", params); + if (this.callback) { - this.callback(oldValue, newValue); + this.callback(...params); } } disconnect() { this.callback = null; - if (this.property) { - this.property.onChangeCallbacks = this.property.onChangeCallbacks.filter(callback => callback != this); - this.property = null; - } - } -} - -/** - * The outline of a ignite property onpush callback that allows - * disconnecting of callbacks on demand when they are no longer needed. - */ -class IgnitePropertyOnPushCallback { - constructor(property, callback) { - this.callback = callback; - this.property = property; - } - - invoke(oldValue, newValue) { - if (this.callback) { - this.callback(oldValue, newValue); - } - } - - disconnect() { - this.callback = null; - - if (this.property) { - this.property.onPushCallbacks = this.property.onPushCallbacks.filter(callback => callback != this); - this.property = null; - } - } -} - -/** - * The outline of a ignite property onpop callback that allows - * disconnecting of callbacks on demand when they are no longer needed. - */ -class IgnitePropertyOnPopCallback { - constructor(property, callback) { - this.callback = callback; - this.property = property; - } - - invoke(oldValue, newValue) { - if (this.callback) { - this.callback(oldValue, newValue); - } - } - - disconnect() { - this.callback = null; - - if (this.property) { - this.property.onPopCallbacks = this.property.onPopCallbacks.filter(callback => callback != this); - this.property = null; + if (this.detach) { + this.detach(this); } } } diff --git a/src/ignite-template.js b/src/ignite-template.js index 32e6451..e644151 100644 --- a/src/ignite-template.js +++ b/src/ignite-template.js @@ -600,13 +600,15 @@ class list extends IgniteTemplate { } onListChanged(oldValue, newValue) { + console.log("On List changed:", newValue); + this.list = newValue; IgniteRenderingContext.enter(); try { this.construct(null); //The list changed, reconstruct this template. - } catch { } + } catch (error) { console.error(error); } IgniteRenderingContext.leave(); } @@ -616,7 +618,12 @@ class list extends IgniteTemplate { try { var template = this.forEach(this.list[this.list.length - 1]); - template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling); + + if (this.elements.length > 0) { + template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling); + } else { + template.construct(this.element.parentElement, null); + } this.children.push(template); this.elements.push(template.element); diff --git a/src/main-app.js b/src/main-app.js index d8768ef..0b629e5 100644 --- a/src/main-app.js +++ b/src/main-app.js @@ -26,7 +26,7 @@ class MainApp extends IgniteElement { .class(this.sheetClass) .child(new html(`