Created
November 29, 2023 23:17
-
-
Save dfkaye/428abdeca224c37014d27fc420906d88 to your computer and use it in GitHub Desktop.
onpropertychange signal v.4 -- redone form element sketch that works for arrays and objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 18 september 2023 | |
// onpropertychange signal v.4 | |
// cont'd from https://gist.github.com/dfkaye/4e06eb749f878662e31a6cb019fbd650 | |
// re-done in less than 30 minutes, this implementation makes more sense and | |
// works for arrays and objects... | |
// 19 September 2023 | |
// dispatch propertychange event in deleteProperty handler | |
// add sync logic and reposition Reflect.set() calls in each handler | |
// Fails on element nodes (see end tests). | |
function P(data) { | |
var target = new EventTarget; | |
// These are merged on the data in order to expose them through the proxy. | |
var API = { | |
addEventListener(name, h) { return target.addEventListener(name, h); }, | |
removeEventListener(name, h) { | |
if (name == "propertychange" && h === this.onpropertychange) { | |
return delete this.onpropertychange; | |
} | |
return target.removeEventListener(name, h); | |
}, | |
dispatchEvent(event) { return target.dispatchEvent(event); }, | |
toJSON() { return data; }, | |
toString() { return JSON.stringify(data); }, | |
// onpropertychange: null, | |
api() { return API; } | |
}; | |
// Don't reset the prototype as in v.2, just add the api methods onto the data | |
// and call it good. They are not serialized as JSON anyway. Maybe make that | |
// the representation on any "query". | |
data = Object.assign(Object(data), API); | |
// sync target properties with data properties | |
function sync({ data, target }) { | |
var API = data.api(); | |
Object.getOwnPropertyNames(data).forEach(function (k) { | |
if (!(k in API)) { | |
target[k] = data[k]; | |
} | |
}); | |
Object.getOwnPropertyNames(target).forEach(function (k) { | |
if (!(k in data)) { | |
delete target[k]; | |
} | |
}); | |
} | |
// proxy the data | |
var handler = { | |
defineProperty(data, key, descriptor) { | |
var {value} = descriptor; | |
var previous = Reflect.get(data,key); | |
if (key === 'onpropertychange') { | |
if (value == null) { | |
return handler.deleteProperty(data, key); | |
} | |
target.removeEventListener('propertychange', previous); | |
target.addEventListener('propertychange', value); | |
} | |
Reflect.set(data, key, value); | |
sync({ data, target }); | |
var event = new CustomEvent("propertychange", { | |
detail: { propertyName: key, previous, value } | |
}); | |
return target.dispatchEvent(event); | |
}, | |
deleteProperty(data, key) { | |
var value = Reflect.get(data, key); | |
if (key === 'onpropertychange') { | |
target.removeEventListener('propertychange', value); | |
} | |
Reflect.deleteProperty(data, key); | |
sync({ data, target }); | |
// add onpropertydelete??? | |
var event = new CustomEvent("propertychange", { | |
detail: { propertyName: key, deletedValue: value } | |
}); | |
return target.dispatchEvent(event); | |
} | |
}; | |
return new Proxy(data, handler); | |
} | |
/* test it out */ | |
var a = P([]); | |
function f(e) { | |
console.warn("onpropertychange\n", e.target, e.detail); | |
} | |
a.onpropertychange = f; | |
function h(e) { | |
console.log("h\n", e.target, e.detail) | |
} | |
a.addEventListener("propertychange", h); | |
a[0] = '11'; | |
a.push(99); | |
console.log(a.length, 1 in a); | |
a.length = 1; | |
console.log(a.length, 1 in a); | |
delete a[0]; | |
console.log( JSON.stringify(a), a.toString(), a.toJSON() ); | |
a.name = 'array'; | |
a.length = 0; | |
console.log( JSON.stringify(a), a.toString(), a.toJSON() ); | |
console.dir( a.api() ); | |
var o = P({name: "object"}); | |
o.addEventListener("propertychange", h); | |
o.onpropertychange = f; | |
console.log( o.toString() ); | |
o.name = 'test'; | |
o.onpropertychange = null; | |
console.log( o.toString() ); | |
console.dir( o.api() ); | |
/* fails element nodes */ | |
var n = P(Object.assign(document.createElement("input"), { | |
defaultValue: "show me" | |
})); | |
console.log(n.getAttribute("value")); | |
// TypeError: 'getAttribute' called on an object that does not implement | |
// interface Element. | |
console.log(n.outerHTML); | |
// TypeError: 'get outerHTML' called on an object that does not implement | |
// interface Element. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment