Hi everybody, I'm still pretty new to Qooxdoo and have been having a good time
coming up to speed on it. It seems like a really nice package, so far. You guys
have really put a lot of EXCELLENT work into this. I'm super-impressed at the
overall attention to detail and organization expressed in the code and
documentation. It'd be nice if the documentation was fleshed out a little
more-- but hey, if you spend all your time documenting you don't get to write
as much cool code, eh? :-)
So anyway, can somebody help me a little please? I've been hammering on this
most of the day and I'm just not understanding where it's going wrong?
My ultimate goal is to call a backend (via rpc) to retrieve JSON data (from an
sql query), which will be returned in an "id / name" format. And then to use
that data to load up a list object (select box, combo box, whatever). I'm not
worried about the actual backend call for the moment-- I already know how to do
that just fine, so for now I'm just faking that part with a static JSON data
structure like so:
var deviceModel = qx.data.marshal.Json.createModel([
{ id: 1, name: "Router" },
{ id: 2, name: "Switch" },
{ id: 3, name: "Firewall" },
{ id: 4, name: "WiFi AP" }
]);
Before getting into the actual problem-- I'd also like to comment on the
general subject itself. I've read through a number of the qooxdoo dev list
postings and have seen numerous back-and-forth between people trying to figure
stuff out and who I think are the developers and qooxdoo gurus (does that
rhyme, btw?) and frankly it seems to me like the whole thing is more
complicated than it ought to be.
I have seen several statements seemingly questioning the fundamental necessity
of such a construct. For example:
"First of all, you should ask yourself if its really necessary to work with
id's here. You are in the JS
environment and you can use references to objects which is way more convenient
than having the
indirection of an id. But thats just a side note..." -- Martin Wittemann
And similar.
My immediate rebuttal to that is-- Sure, objects are nice, but many times a
List object is used to select an item which will then be handed off to a
back-end for some sort of processing, in which case having a reference to an
object could be considered more of a hindrance. It is not at all unusual to
assume that transmitting an ID value would be the most effective method of
communication to a back-end for the purpose of data lookup or something. And an
ID value is probably more what a back-end data store (SQL database or
something) is more likely to want anyway -- so NOT doing it that way actually
adds to the overhead necessary to get it all coded up, on both the back-end and
front-ends.
If I were thinking up the thing I think I would consider three typical cases:
1. A simple list (array) of items-- strings, objects-- whatever. When one is
selected, it is returned, whatever it is. As a variation, perhaps a flag could
also be set to return the item's index value instead, so it'd be a configurable
option which way to return the info.
2. A simple array of arrays, each containing two items. The first is the
return value (the 'id') the second is the display value (the 'name').
3. A simple array of hashes with formally named elements (e.g. "id", "name",
"foo", "bar", etc). Then the user could indicate which element should be the
displayed element and which element should be the returned element. The user
could opt to use the same element for both. And optionally, perhaps it could
also accept a hash of hashes-- operation similar, list order not guaranteed--
i.e., however it comes out.
BONUS: And then if one of those pre-arranged scenarios wasn't suitable, THEN it
would be fantastic to ALSO have the option of custom data binding /
conversion-- whatever.
In the case of having many, many list items to worry about, it could work like
your virtual list or virtual table model.
In that setup, I think you would cover probably (my guess) 70-80% of the
required cases in the first two or three modes, and would easily cover the rest
through your custom data binding mechanism.
That's my two cents on the matter :-)
Now, for what I actually need help with...
I found Martain Wittemann's write-up about using data binding along with his
code sample on the demo.qooxdoo.org playground. I worked it around slightly
just to make it work with my own example-- it was pretty much exactly what I
wanted from the git-go. This is what I have, from Martin's code:
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 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)
************************************************************************ */
/**
* @tag databinding
* @tag list contorller
* @tag form controller
*/
qx.Class.define("demobrowser.demo.data.FormAndListController",
{
extend : qx.application.Standalone,
members :
{
main: function()
{
this.base(arguments);
// create some dummy data
var data = {
firstname: "John",
lastname: "Whitten",
device: null
};
var model = qx.data.marshal.Json.createModel(data);
var deviceModel = qx.data.marshal.Json.createModel([
{id: "rtr", name: "Router"},
{id: "sw", name: "Switch"},
{id: "fw", name: "Firewall"},
{id: "wap", name: "Wifi AP"}
]);
// create the form
var form = new qx.ui.form.Form();
// firstname
var firstname = new qx.ui.form.TextField();
form.add(firstname, "Firstname");
// lastname
var lastname = new qx.ui.form.TextField();
form.add(lastname, "Lastname");
// device type
var deviceBox = new qx.ui.form.SelectBox();
new qx.data.controller.List(deviceModel, deviceBox, "name");
form.add(deviceBox, "Device");
// create the form and add it to the root
this.getRoot().add(new qx.ui.form.renderer.Single(form), {left: 30, top:
20});
// create a form controller!
var fc = new qx.data.controller.Form(model, form);
fc.addBindingOptions("Device", {converter: function(data) {
// model2target
for (var i = 0; i < deviceModel.getLength(); i++) {
if (deviceModel.getItem(i).getId() == data) {
return deviceModel.getItem(i)
}
}
return deviceModel.getItem(0);
}}, {converter: function(data) {
// target2model
return data.getId();
}});
// A button to log the models content
var logButton = new qx.ui.form.Button("Show model data in the log");
this.getRoot().add(logButton, {left: 240, top: 20});
logButton.addListener("execute", function() {
this.debug(qx.dev.Debug.debugProperties(model));
}, this);
}
}
});
However, when I lifted the code from his example and plugged it into my own, I
keep getting console errors (which you can see in context on down below) The
first of which is appears to be the show-stopper:
+ Uncaught TypeError: Cannot read property 'constructor' of
undefined Form.js:360
+ qx.Class.define.members.__isModelSelectable Form.js:360
+ qx.Class.define.members.addBindingOptions Form.js:136
+ qx.Class.define.construct Simple.js:53
Here is my code along with a comment block in the middle which illustrates
where the problem is occurring. Note that in my example, I am not creating a
form, but rather extending a form as a class. As an aside, I have several
examples of the "form-as-class" that run without errors at all. So I know for
certain it is directly related somehow to the data binding line. Note that in
order to run it, it must be instantiated as a class by something else and
rendered, etc.
qx.Class.define("reilly.ui.forms.Simple",
{
extend : qx.ui.form.Form,
properties :
{
// controller : { deferredInit : true },
// model : { deferredInit : true },
service : { init: "echo" }
},
events :
{
"onSave" : "qx.event.type.Data"
},
construct : function()
{
this.base(arguments);
// set up model for form
var formModel = qx.data.marshal.Json.createModel({
device: null
});
// set up device type combo box
var deviceName = "Device";
var deviceModel = qx.data.marshal.Json.createModel([
{ id: 1, name: "Router" },
{ id: 2, name: "Switch" },
{ id: 3, name: "Firewall" },
{ id: 4, name: "WiFi AP" }
]);
var device = new qx.ui.form.SelectBox().set({required: true});
new qx.data.controller.List(deviceModel, device, "name"); //
bind list model to selectbox-list
this.add(device, deviceName, null, "device");
// save button
var btnSave = new qx.ui.form.Button("Save");
btnSave.addListener("execute", function(e) {
this.fireDataEvent("onSave", this);
}, this);
this.addButton(btnSave);
// create form controller
var formCtlr = new qx.data.controller.Form(formModel, this);
//
=====================================================================================
// #### THE PROBLEM SEEMS TO BE HERE SOMEHOW - The errors
generated are below.
// #### "Simple.js:53" is the first live line following this
text
// ####
// #### + Uncaught TypeError: Cannot read property
'constructor' of undefined Form.js:360
// #### + qx.Class.define.members.__isModelSelectable
Form.js:360
// #### + qx.Class.define.members.addBindingOptions Form.js:136
// #### + qx.Class.define.construct Simple.js:53
//
=====================================================================================
// alter binding for 'device' control
formCtlr.addBindingOptions(deviceName, {
converter: function(data) {
for (var i = 0; i < deviceModel.getLength();
i++) {
if (deviceModel.getItem(i).getId() ==
data) {
return deviceModel.getItem(i);
}
}
}
},
{
converter: function(data) {
return data.getId();
}
});
//
=====================================================================================
this.setService("getSwitch");
}
});
Here is a code fragment which illustrates using the form class from above:
this.swForm = new reilly.ui.forms.Simple();
this.swForm.addListener("onSave", this.__handleSubmit, this);
this.swFormWindow = new reilly.ui.windows.PortalWindow("Switch
Form");
this.swFormWindow.add(new
qx.ui.form.renderer.Single(this.swForm));
this.swFormWindow.setVisibility("visible");
this.getRoot().add(this.swFormWindow, {left: 25, top: 25});
Can anybody shed any light as to what I'm doing wrong? I'd sure like to get
this figured out and move on to the next item.
Thanks for any help and comments!!
And thanks again for such an INCREDIBLY COOL toolkit!!
I don't have a clue how to pronounce it-- but Qooxdoo ROCKS !!! :-)
John Whitten------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel