Closed p9malino26 closed 4 months ago
I think this is an excellent idea. The qxObject system is a hidden gem of qooxdoo which is worth being made a first-class citizen. Your proposal makes it much more idiomatic to work with.
However, the key shold something different than "qxObjects". In most cases, the objects defined are widget components. Maybe "components" would be the best term.
I think you're right. The system exists to make defining widget composition easier. Things like controllers should be defined as members.
If we were to use 'components' as the name, would the widgets defined under 'components' be referenced using getQxObject or would we use 'getComponent' for that? Or could we make getComponent a synonym of qxObject?
I like this idea too; although the "QxObject" name isn't as perfectly elegant as we might like for the QxObject API, we had to avoid conflicts and so the name has stuck.
As the backing for this code is the getQxObject
/ _createQxObjectImpl
APIs, IMHO I think the qxObjects
top level member is the right name
Widget/children/widgetChildren.
Forced use of the old 'var' instead of let or const because you cannot re-declare variables with the same name in the same scope.
I declare via let before switch. For me it is not problem.
The results of a little discussion we've had on this in the office;
Having a more declarative notation added to the qxObject system would be quite beneficial. Currently, it would be a little complex, however by setting it up correctly now, the new properties system proposal (#10410) would automatically promote it to the desired behavior.
The end result once all is said and done would be that the following two lines of code are equivalent;
this.qxObjects.myObject;
this.getQxObject("myObject");
The first one is the ideal access to qxObjects; completely declarative in nature. While in the second (current) syntax it's quite easy to make typos, in the first syntax this would be much more obvious, especially after the type checking system (#10639) is detailed and implemented, in which static compile-time type checking can be quite easily applied to the object. Applying the same to function paramaters is less simple and may require a more complicated solution to #10639.
Under the hood the implementation would be very simple. A property defined under the new system named qxObjects
, with it's mutability set to readonly and it's value initialised to a get-trap Proxy instance which redirects all qxObjects[key]
and qxObjects.key
to getQxObject(key)
.
Below is a proof of concept demonstrating that this works with any keys through either index access or by dot notation.
class MyQxThingOrWhatever {
// this would exist inside of `qx.core.MObjectId`, utilizing the new property system
get qxObjects() {
return this.#__cached_qxObjects;
}
#__cached_qxObjects = new Proxy({}, { get: (_, key) => this.getQxObject(key) });
_createQxObjectImpl(id) {
// defined by the class author
switch (id) {
case "foo":
return "created a foo";
default:
return "dunno what a " + id + " is!";
}
}
getQxObject(id) {
// exists within `qx.core.MObjectId` (see `qx.core.Object|include`)
return this._createQxObjectImpl(id);
}
}
const thing = new MyQxThingOrWhatever();
console.log(thing.qxObjects.foo);
console.log(thing.qxObjects["foo"]);
console.log(thing.qxObjects.bar);
console.log(thing.qxObjects["0-weird_´´å^ß_text"]);
# output:
created a foo
created a foo
dunno what a bar is!
dunno what a 0-weird_´´å^ß_text is!
The actual cost of this syntax is incredibly low - near zero. Proxy instances are weightless compared to many other classes, and the code cost is defining one single property.
I like this idea too; although the "QxObject" name isn't as perfectly elegant as we might like for the QxObject API, we had to avoid conflicts and so the name has stuck.
As the backing for this code is the
getQxObject
/_createQxObjectImpl
APIs, IMHO I think theqxObjects
top level member is the right name
Couldn't we choose a more "elegant" name for the top-level key and preserve BC - there is no need to rename any of the existing infrastructure but this would allow us to develop the implementation in other ways if we so choose in the future, adding more functionalities that are independent of the qxObject architecture. I am not saying that there is a need for this (yet) but this would - in addition to look a bit nicer - preserve the freedom to do so.
Couldn't we choose a more "elegant" name for the top-level key and preserve BC
How about just objects: {}
? I guess the bit Im not keen on is it being something completely different, eg components:{}
declared but .getQxObject()
elsewhere
How about just
objects: {}
? I guess the bit Im not keen on is it being something completely different, egcomponents:{}
declared but.getQxObject()
elsewhere
I like that idea: it is very generic so that one has a generative cache for any object, regardless of whether it is a widget component or anything else. The widget component architecture can then be built on top of this by describing it in the documentation.
Context
This proposes a new system in which cached objects for Qooxdoo classes are defined (getQxObject, getChildControl, etc). It is 100% BC and solves the major problems present in the current system, which include:
No checking for return statement after each case block. This can lead to recursion errors, which are a pain to debug, like in the example below:
Forced use of the old 'var' instead of let or const because you cannot re-declare variables with the same name in the same scope. The compiler will complain in this case:
Having to remember to call
super._createQxObjectImpl(id)
(or similar to createWidget or createChildControl) at the end, which we may forget to do or accidentally delete.Design
Example:
Some points
As well as solving the above-mentioned problems, this solution offers many additional benefits, including:
Things to consider