Skip to content

Instantly share code, notes, and snippets.

@guilhermeaiolfi
Created April 28, 2010 18:35
Show Gist options
  • Save guilhermeaiolfi/382513 to your computer and use it in GitHub Desktop.
Save guilhermeaiolfi/382513 to your computer and use it in GitHub Desktop.
qx.Class.define("playground.Form",
{
extend : qx.ui.container.Composite,
include: [playground.MBuilder],
implement : [playground.IForm],
construct : function()
{
this.base(arguments);
this.__fields = {};
this.__buttons = [];
this.__validationManager = new qx.ui.form.validation.Manager();
this.__resetter = new qx.ui.form.Resetter();
this._setLayout(new qx.ui.layout.VBox());
this._fieldsContainer = new qx.ui.container.Composite();
this._buttonsContainer = new qx.ui.container.Composite();
this._buttonsContainer.setMaxHeight(30);
this._buttonsContainer.setLayout(new qx.ui.layout.HBox(10, 'right'));
this._buttonsContainer.setHeight(30);
this._add(this._fieldsContainer, {flex: 1});
this._add(this._buttonsContainer, {flex: 1});
},
members :
{
__model : null,
__fields : null,
__validationManager : null,
__buttons : null,
__resetter : null,
_fieldsContainer: null,
_buttonsContainer: null,
add : function(item, position) {
this._fieldsContainer.add(item, position);
},
addItem: function(id, item, entry)
{
var clazz = item.constructor;
this.__fields[id] = item;
if (qx.Class.hasInterface(clazz, qx.ui.form.IForm) && id)// || (id && clazz.classname == "qx.ui.tree.Tree"))
{
this.__resetter.add(item);
this.__validationManager.add(item, entry.validator);
}
},
addButton : function(button) {
this.__buttons.push(button);
button.setAllowStretchY(false);
button.setAlignY('middle');
button.setAlignX('right');
this._buttonsContainer.add(button);
},
setLayout: function(layout)
{
this._fieldsContainer.setLayout(layout);
},
reset : function() {
this.__resetter.reset();
this.__validationManager.reset();
},
redefineResetter : function()
{
this.__resetter.redefine();
},
validate : function() {
return this.__validationManager.validate();
},
getValidationManager : function() {
return this.__validationManager;
},
getButtons : function()
{
return this.__buttons;
},
getItems : function() {
return this.__fields;
}
}
});
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2009 1&1 Internet AG, Germany, http://www.1und1.de
License:
LGPL: http://www.gnu.org/licenses/lgpl.html
EPL: http://www.eclipse.org/org/documents/epl-v10.php
See the LICENSE file in the project's top-level directory for details.
Authors:
* Martin Wittemann (martinwittemann)
************************************************************************ */
/**
* <h2>Form Controller</h2>
*
* *General idea*
*
* The form controller is responsible for connecting a from with a model. If no
* model is given, a model can be created. This created form will fit exactly
* to the given form and can be used for serialization. All the connections
* between the form items and the model are handled by an internal
* {@link qx.data.controller.Object}.
*
* *Features*
*
* * Connect a form to a model (bidirectional)
* * Create a model for a given form
*
* *Usage*
*
* The controller only work if both, a controller and a model are set.
* Creating a model will automatically set the created model.
*
* *Cross reference*
*
* * If you want to bind single values, use {@link qx.data.controller.Object}
* * If you want to bind a list like widget, use {@link qx.data.controller.List}
* * If you want to bind a tree widget, use {@link qx.data.controller.Tree}
*/
qx.Class.define("playground.FormController",
{
extend : qx.core.Object,
/**
* @param model {qx.core.Object | null} The model to bind the target to. The
* given object will be set as {@link #model} property.
* @param target {qx.ui.form.Form | null} The form which contains the form
* items. The given form will be set as {@link #target} property.
*/
construct : function(model, target)
{
this.base(arguments);
this.__bindingOptions = {};
if (model != null) {
this.setModel(model);
}
if (target != null) {
this.setTarget(target);
}
},
properties :
{
/** Data object containing the data which should be shown in the target. */
model :
{
check: "qx.core.Object",
apply: "_applyModel",
event: "changeModel",
nullable: true
},
/** The target widget which should show the data. */
target :
{
check: "playground.IForm",
apply: "_applyTarget",
event: "changeTarget",
nullable: true,
init: null
}
},
members :
{
__objectController : null,
__bindingOptions : null,
/**
* The form controller uses for setting up the bindings the fundamental
* binding layer, the {@link qx.data.SingleValueBinding}. To achieve a
* binding in both directions, two bindings are neede. With this method,
* you have the oppertunity to set the options used for the bindings.
*
* @param name {String} The name of the form item for which the options
* should be used.
* @param model2target {Map} Options map used for the binding from model
* to target. The possible options can be found in the
* {@link qx.data.SingleValueBinding} class.
* @param target2model {Map} Optiosn map used for the binding from target
* to model. The possible options can be found in the
* {@link qx.data.SingleValueBinding} class.
*/
addBindingOptions : function(name, target, targetProperty, bidirectional, model2target, target2model)
{
if (bidirectional == undefined) bidirectional = true;
//console.log(name, target, targetProperty, model2target, target2model);
this.__bindingOptions[name] = {target: target, targetProperty: targetProperty, model2target: model2target, target2model: target2model, bidirectional: bidirectional};
// return if not both, model and target are given
if (this.getModel() == null || this.getTarget() == null) {
return;
}
// renew the affected binding
var item = this.getTarget().getItems()[name];
var targetProperty = this.__bindingOptions[name][targetProperty];
//console.log(this.__bindingOptions[name]);
// remove the binding
this.__objectController.removeTarget(target? target : item, this.__getTargetProperty(item, targetProperty), name);
// set up the new binding with the options
this.__objectController.addTarget(
target? target : item, this.__getTargetProperty(item, targetProperty), name, bidirectional, model2target, target2model
);
},
/**
* Creates and sets a model using the {@link qx.data.marshal.Json} object.
* Remember that this method can only work if the form is set. The created
* model will fit exactly that form. Changing the form or adding an item to
* the form will need a new model creation.
*
* @param includeBubbleEvents {Boolean} Whether the model should support
* the bubbling of change events or not.
* @return {qx.core.Object} The created model.
*/
createModel : function(includeBubbleEvents) {
var target = this.getTarget();
// throw an error if no target is set
if (target == null) {
throw new Error("No target is set.");
}
var items = target.getItems();
var data = {};
for (var name in items) {
var names = name.split(".");
var currentData = data;
for (var i = 0; i < names.length; i++) {
// if its the last item
if (i + 1 == names.length) {
// check if the target is a selection
var clazz = items[name].constructor;
if (qx.Class.hasInterface(clazz, qx.ui.core.ISingleSelection) || qx.Class.hasInterface(clazz, qx.ui.core.IMultiSelection)) {
currentData[names[i]] = items[name].getModelSelection();
} else {
currentData[names[i]] = items[name].getValue();
}
} else {
// if its not the last element, check if the object exists
if (!currentData[names[i]]) {
currentData[names[i]] = {};
}
currentData = currentData[names[i]]
}
}
}
var model = qx.data.marshal.Json.createModel(data, includeBubbleEvents);
this.setModel(model);
return model;
},
// apply method
_applyTarget : function(value, old) {
// if an old target is given, remove the binding
if (old != null) {
this.__tearDownBinding(old);
}
// do nothing if no target is set
if (this.getModel() == null) {
return;
}
// target and model are available
if (value != null) {
this.__setUpBinding();
}
},
// apply method
_applyModel : function(value, old) {
// first, get rid off all bindings (avoids whong data population)
if (this.__objectController != null) {
var items = this.getTarget().getItems();
for (var name in items) {
var item = items[name];
var options = this.__bindingOptions[name];
var targetProperty = options[targetProperty];
this.__objectController.removeTarget(options[target]? options[target]: item, targetProperty, name);
}
}
// set the model of the object controller if available
if (this.__objectController != null) {
this.__objectController.setModel(value);
}
// do nothing is no target is set
if (this.getTarget() == null) {
return;
}
// model and target are available
if (value != null) {
this.__setUpBinding();
}
},
__getTargetProperty: function(item, targetProperty)
{
return targetProperty? targetProperty : this.__isModelSelectable(item) ? this.__isMultiModelSelectable(item)? "modelSelection" : "modelSelection[0]" : "value";
},
/**
* Internal helper for setting up the bindings using
* {@link qx.data.controller.Object#addTarget}. All bindings are set
* up bidirectional.
*/
__setUpBinding : function() {
// create the object controller
if (this.__objectController == null) {
this.__objectController = new qx.data.controller.Object(this.getModel());
}
// get the form items
var items = this.getTarget().getItems();
// connect all items
for (var name in items) {
var item = items[name];
var options = this.__bindingOptions[name];
if (options == null) {
var targetProperty = this.__getTargetProperty(item, null);
this.__objectController.addTarget(item, targetProperty, name, true);
} else {
var targetProperty = this.__getTargetProperty(item, options.targetProperty);
//this.__objectController.removeTarget(options.target? options.target : item, targetProperty, name);
//console.log(options.target? options.target : item, targetProperty, name, true, options.model2target, options.target2model);
this.__objectController.addTarget(
options.target? options.target : item, targetProperty, name, true, options.model2target, options.target2model
);
}
}
},
/**
* Internal helper for removing all set up bindings using
* {@link qx.data.controller.Object#removeTarget}.
*
* @param oldTarget {qx.ui.form.Form} The form which has been removed.
*/
__tearDownBinding : function(oldTarget) {
// do nothing if the object controller has not been created
if (this.__objectController == null) {
return;
}
// get the items
var items = oldTarget.getItems();
// disconnect all items
for (var name in items) {
var item = items[name];
var targetProperty = this.__bindindsOptions[name][targetProperty];
this.__objectController.removeTarget(item, targetProperty, name);
}
},
/**
* Returns whether the given item implements
* {@link qx.ui.core.ISingleSelection} and
* {@link qx.ui.form.IModelSelection}.
*
* @param item {qx.ui.form.IForm} The form item to check.
*
* @return {true} true, if given item fits.
*/
__isSingleModelSelectable : function(item) {
return qx.Class.hasInterface(item.constructor, qx.ui.core.ISingleSelection);
},
__isMultiModelSelectable : function(item) {
return qx.Class.hasInterface(item.constructor, qx.ui.core.IMultiSelection);
},
__isModelSelectable : function(item)
{ return (qx.Class.hasInterface(item.constructor, qx.ui.core.ISingleSelection) && qx.Class.hasInterface(item.constructor, qx.ui.form.IModelSelection)) ||
(qx.Class.hasInterface(item.constructor, qx.ui.core.IMultiSelection) && qx.Class.hasInterface(item.constructor, qx.ui.form.IModelSelection))
}
}
});
qx.Interface.define("playground.IForm",
{
members :
{
addButton : function(button) {},
reset : function () {},
getItems : function () {},
validate : function () {},
redefineResetter : function () {},
getValidationManager : function () {}
}
});
qx.Mixin.define("playground.MBuilder",
{
members :
{
__widgetRegistry : null,
__bindQueue: null,
__forms: null,
build: function(definition)
{
var db = this.__widgetRegistry;
if (!db) {
this.__widgetRegistry = {};
}
var bind_queue = this.__bindQueue;
if (!bind_queue)
{
this.__bindQueue = {};
}
if (!this.__forms)
{
this.__forms = [];
}
return this.__create(definition);
},
_registerObject : function(id, obj)
{
if (id)
{
this.__widgetRegistry[id] = obj;
}
},
__create : function(definition)
{
if (qx.core.Variant.isSet("qx.debug", "on")) {
this.__validateEntry(definition);
}
var widget = new definition.create();
this.set(widget, definition.set);
if (qx.Class.hasInterface(widget.constructor, playground.IForm))
{
// if (!definition.id)
// {
// this.error("The form's ID is mandatory");
// }
var form = {form: widget };
if (definition.validator)
{
widget.getValidationManager().setValidator(definition.validator);
}
var controller = null;
if (definition.model && definition.model.controller)
{
controller = this.__create(definition.model.controller);
controller.setTarget(widget);
//widget.setController(controller);
}
else
{
controller = new playground.FormController (null, widget);
}
form.controller = controller;
this.__forms.push(form);
}
this._registerObject(definition.id, widget);
this.__flushBindingQueue(widget, definition);
this.__configureLayout(widget, definition);
this.__runAddMethods(widget, definition);
this.__addListeners(widget, definition);
this.__addBindings (widget, definition);
if (definition.init)
{
definition.init.call(this, widget);
}
if (this._getLastForm('form') == widget)
{
this._getLastForm('controller').createModel();
qx.lang.Array.remove(this.__forms, this._getLastForm());
}
return widget;
},
__flushBindingQueue : function (widget, definition)
{
if (this.__bindQueue[definition.id])
{
for (var i = 0; i < this.__bindQueue[definition.id].bind.length; i++)
{
var bind = this.__bindQueue[definition.id].bind[i];
if (qx.Class.hasInterface(widget.constructor, playground.IForm) && bind.type == "field")
{
this._getFormController(widget).addBindingOptions(bind.targetProperty, widget, bind.property, bind.bidirectional, bind.targetModel, bind.toSerialize);
}
else
{
widget.bind(bind.property, widget, bind.targetProperty, bind.options);
}
}
delete this.__bindQueue[definition.id];
}
},
__runAddMethods : function (widget, entry)
{
if (entry['add']) //include the children first
{
if (entry.lazy)
{
widget.addListenerOnce("appear", function (e) {
this.__buildChildren(widget, entry['add'], 'add', true);
}, this);
}
else
{
this.__buildChildren(widget, entry['add'], 'add', true);
}
}
for (var add in entry)
{
if (qx.lang.String.startsWith(add, 'add'))
{
if (add == 'add') continue;
var include = widget[add] != null;
if (!include)
{
this.warn("Missing method " + add + " in " + widget);
}
if (entry.lazy)
{
widget.addListenerOnce("appear", function (e) {
this.__buildChildren(widget, entry[add], add, include);
}, this);
}
else
{
this.__buildChildren(widget, entry[add], add, include);
}
}
}
},
__buildChildren: function(widget, children, method, include)
{
for (var i = 0; i < children.length; i++)
{
var entry = children[i];
var obj = null;
if (entry.create)
{
obj= this.__create(entry);
if (include)
{
widget[method](obj);
if (entry.position)
{
obj.setLayoutProperties(entry.position);
}
}
var clazz = obj.constructor
var form = this._getLastForm('form');
if ((qx.Class.hasInterface(clazz, qx.ui.form.IForm) && form))
{
form.addItem(entry.id, obj, entry);
if (qx.Class.hasInterface(clazz, qx.ui.core.ISingleSelection) || qx.Class.hasInterface(clazz, qx.ui.core.IMultiSelection))
{
this.__configureModel(obj, entry);
}
}
}
else if (widget[method])
{
widget[method].apply(widget, entry);
}
}
},
_getLastForm : function(att)
{
var form = this.__forms[this.__forms.length-1];
if (form && att)
return form[att];
return form;
},
_getFormController : function(widget)
{
for (var i = 0, len = this.__forms.length; i < len; i++)
{
if (this.__forms[i].form == widget)
{
return this.__forms[i].controller;
}
}
return null;
},
__configureModel : function(obj, entry)
{
var clazz = obj.constructor;
var form = this._getLastForm('form');
if (entry.model && entry.model.store)
{
var storeEntry = entry.model.store;
var store = qx.lang.Type.isArray(storeEntry)? this.getById(storeEntry) : this.build(storeEntry);
var idPath = entry.model.controller.set.modelPath || "id";
delete entry.model.controller.set.modelPath;
var controller = this.build(entry.model.controller);
controller.setTarget(obj);
store.bind("model" + (storeEntry.items && !qx.lang.String.startsWith(storeEntry.items, "[")? "." + storeEntry.items : storeEntry.items? storeEntry.items : ""), controller, "model");
var targetProperty = "value";
var target = obj;
if (qx.Class.hasInterface(obj.constructor, qx.ui.core.IMultiSelection))
{
target = controller;
targetProperty = "selection";
}
else if (qx.Class.hasInterface(obj.constructor, qx.ui.core.ISingleSelection))
{
targetProperty = "modelSelection[0]";
}
this._getFormController(form).addBindingOptions(entry.id, target, targetProperty);
}
},
__configureLayout : function(widget, entry)
{
if (entry.layout != null)
{
var layout = new entry.layout.create();
for (set in entry.layout.set)
{
if (qx.lang.Type.isArray(entry.layout.set[set]))
{
for (var i = 0; i < entry.layout.set[set].length; i++)
{
if (qx.lang.Type.isArray(entry.layout.set[set][i]))
{
layout["set" + qx.Bootstrap.firstUp(set)].apply(layout, entry.layout.set[set][i]);
continue;
}
layout["set" + qx.Bootstrap.firstUp(set)].apply(layout, entry.layout.set[set]);
break;
}
}
else
{
layout.set(set, entry.layout.set[set]);
}
}
widget.setLayout(layout);
if (entry.layout.init)
{
entry.layout.init.call(layout);
}
}
},
__addBindings : function(widget, entry)
{
if (entry.bind)
{
for (var i = 0; i < entry.bind.length; i++)
{
var bind = entry.bind[i];
var target = qx.Bootstrap.isString(bind.target)? this.getById(bind.target) : bind.target;
if (target)
{
if (qx.Class.hasInterface(widget.constructor, playground.IForm) && bind.type == 'field')
{
this._getFormController(widget).addBindingOptions(bind.targetProperty, target, bind.property, bind.bidirectional, bind.toModel, bind.toSerialize);
}
else
{
widget.bind(bind.property, target, bind.targetProperty, bind.options);
}
}
else if (qx.Bootstrap.isString(bind.target))
{
this.__bindQueue[bind.target] = this.__bindQueue[bind.target] || {};
this.__bindQueue[bind.target].bind = this.__bindQueue[bind.target].bind || [];
this.__bindQueue[bind.target].bind.push({ type: bind.type, from: widget, fromProperty: bind.property, targetProperty: bind.targetProperty, options: bind.options, model2target: bind.toModel, target2model: bind.toSerialize, bidirectional: bind.bidirectional });
}
}
}
},
__addListeners : function(widget, entry)
{
var events = entry.listen;
if (events)
{
var String = qx.lang.String;
var base = "_on" + (entry.id ? String.firstUp(entry.id) : "");
var conf, func, context;
for (var i=0, l=events.length; i<l; i++)
{
conf = events[i];
if (conf.handler)
{
func = conf.handler;
}
else
{
func = base + String.firstUp(conf.event);
}
func = qx.lang.Type.isString(func)? this[func] : func;
if (qx.core.Variant.isSet("qx.debug", "on"))
{
if (!func)
{
this.error('Missing method: ' + func + ' to add event listener "' + conf.event + '" to instance of class "' + entry.create.classname + '"');
continue;
}
}
context = conf.context? conf.context : this;
widget.addListener(conf.event, func, context, conf.capture);
}
}
},
__validateEntry : function(entry)
{
if (!entry.create) {
throw new Error("Missing create information to select class to create!");
}
if (entry.listen && !entry.id) {
throw new Error('Missing ID to add event listeners to instance of class "' + entry.create.classname + '"');
}
},
getById : function(id)
{
var db = this.__widgetRegistry;
if (db)
{
var obj = db[id];
if (obj) {
return obj;
}
}
this.warn("Missing widget with ID: " + id);
return null;
},
set : function(obj, data, value)
{
var setter = qx.core.Property.$$method.set;
var getter = null;
if (qx.Bootstrap.isString(data))
{
if (!obj[setter[data]])
{
if (obj["set" + qx.Bootstrap.firstUp(data)] != undefined) {
obj["set" + qx.Bootstrap.firstUp(data)](value);
return;
}
else if (qx.lang.String.contains(data, "."))
{
if (this._setNested(obj, data, value))
{
return obj;
}
}
if (qx.core.Variant.isSet("qx.debug", "on"))
{
this.warn("No such property: " + data);
}
return obj;
}
return obj[setter[data]](value);
}
else
{
for (var prop in data)
{
var out = false;
if (!obj[setter[prop]])
{
if (obj["set" + qx.Bootstrap.firstUp(prop)] != undefined) {
obj["set" + qx.Bootstrap.firstUp(prop)](data[prop]);
continue;
}
else if (qx.lang.String.contains(prop, "."))
{
if (this._setNested(obj, prop, data[prop]))
{
continue;
}
}
if (qx.core.Variant.isSet("qx.debug", "on"))
{
this.warn("No such property: " + prop);
}
continue;
}
obj[setter[prop]](data[prop]);
}
return obj;
}
},
_setNested: function(obj, prop, value)
{
var atts = qx.util.StringSplit.split(prop, ".");
var obj_to_set = obj;
for (var i = 0, len = atts.length; i < len; i++)
{
if (i == (len - 1))
{
obj_to_set.set(atts[i], value);
return true;
}
else
{
obj_to_set = obj_to_set.get(atts[i]);
if (!obj_to_set)
{
break;
}
}
}
return false;
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment