Skip to content

Instantly share code, notes, and snippets.

@johnspackman
Last active November 12, 2017 20:42
Show Gist options
  • Save johnspackman/f109bae1756e6f19f30c429c007f9c95 to your computer and use it in GitHub Desktop.
Save johnspackman/f109bae1756e6f19f30c429c007f9c95 to your computer and use it in GitHub Desktop.
/**
* Provides a registry of top level objects
*/
qx.Class.define("qx.core.Id", {
extend: qx.core.Object,
include: [ qx.core.MObjectId ],
type: "singleton",
members: {
/*
* @Override
*/
_createObjectImpl: function(id) {
switch(id) {
case "application":
return qx.core.Init.getApplication() || undefined;
}
return; // undefined
}
},
statics: {
/**
* Returns a top level instance
*
* @param id {String} the ID to look for
* @return {qx.core.Object?} the object
*/
getObject: function(id) {
return this.getInstance().getObject(id);
}
}
});
/**
* A mixin providing objects by ID and owners.
*/
qx.Mixin.define("qx.core.MObjectId",
{
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties: {
/** The ID of the owning object */
owner : {
check : "qx.core.Object",
init : null,
nullable : true,
apply : "_applyOwner"
},
/** The ID of the object. Must be unique if no owner is specified */
objectId :
{
check : "String",
nullable : false,
apply : "_applyObjectId"
},
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members: {
__ownedObjects: null,
/**
* apply owner id
*/
_applyOwner : function(value, old)
{
// todo: check if we have to move to new owner
// for example, if objectId has been assigned before ownerId
// apply to DOM node
if(value && value.getObjectId()) {
this.getContentElement().setAttribute("data-object-id", value.getObjectId());
}
},
/**
* apply object id
*/
_applyObjectId : function(value, old) {
// for the moment, do not allow change since that needs to be handled
if (old ){
this.error("Changing object ID not allowed");
}
// apply to DOM node
if (value && value.getOwner()) {
this.getContentElement().setAttribute("data-object-id", value.getObjectId());
}
},
/**
* Returns the object with the specified ID
*
* @param id {String} ID of the object
* @return {qx.core.Object?} the found object
*/
getObject: function(id) {
if (this.__ownedObjects) {
var obj = this.__ownedObjects[id];
if (obj !== undefined) {
return obj;
}
}
// Handle paths
if (id.indexOf('/') > -1) {
var segs = id.split('/');
var target = this;
var found = segs.every(seg => {
if (!seg.length)
return true;
if (!target)
return false;
var tmp = target.getObject(seg);
if (tmp !== undefined) {
target = tmp;
return true;
}
});
return found ? target : undefined;
}
// No object, creating the object
var obj = this._createObjectImpl(id);
if (obj !== undefined) {
if (!this.__ownedObjects) {
this.__ownedObjects = {};
}
obj.setOwner(this);
obj.setObjectId(id);
this.__ownedObjects[id] = obj;
}
return obj;
},
/**
* Creates the object, intended to be overridden. Null is a valid return value and will be
* cached by `getObject`, however `undefined` is NOT a valid value and so will not be cached
* meaning that `_createObjectImpl` will be called multiple times until a valid value is returned.
*
* @param id {String} ID of the object
* @return {qx.core.Object?} the created object
*/
_createObjectImpl: function(id) {
return; // Return undefined
},
/**
* Discards an object from the list of owned objects; note that this does not dispose of the object,
* simply forgets it if it exists.
*
* @param id {String} the ID of the object to discard
*/
discardObject: function(id) {
if (id.indexOf('/') > -1)
throw new Error("Cannot discard owned objects based on a path");
if (this.__ownedObjects) {
var obj = this.__ownedObjects[id];
if (obj)
obj.setOwner(null);
delete this.__ownedObjects[id];
}
},
/**
* Returns an array of objects that are owned by this object, or an
* empty array if none exists.
*
* @return {Array}
*/
getOwnedObjects : function(){
return this.__ownedObjects ? Object.values(this.__ownedObjects) : [];
}
}
});
@cboulanger
Copy link

Can you explain: say you call myObject.getObject("foo") and it doesn't have this id in its list of owned objects. Why would it then call _createObjectImpl("foo")? I assume this is to allow custom search paths?

@johnspackman
Copy link
Author

No, it's so that objects are created on demand; _createObjectImpl("foo") is guaranteed to be called only once for "foo" and it's result is cached.

The only issue I've had with this pattern is where I have:

construct: function() {
  this.base(argument);
  this.add(this.getObject("lst"));
},
members: {
  _createObjectImpl: function(id) {
    switch(id) {
    case "ctlr":
      return new qx.data.controller.List(null, this.getObject("lst"), "title");

    case "lst":
      return new qx.ui.form.List();

What happens here is that the controller is never instantiated and so lst appears on screen but empty. The solution is to add this.getObject("ctlr") to the constructor, which is a little un-intuitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment