Monday, March 26, 2012

Accessing a property from a custom object

Hi all,
I am trying to mimic what I can do imperitively, in a declarative way for the purposes of a demo. I have no issue an accessing a web service that returns a custom object imperitively. For example:
function MakeTheCall()
{
DataTier.DoTheAsyncMethod(OnMethodComplete);
}
function OnMethodComplete(customer)
{
alert(customer.FirstName + " " + customer.LastName);
}

where the 'customer' is what the web service returns and is defined as something like:
public class MyCustomer
{
public string FirstName;
public string LastName;
//...and a couple of others
}

Again, imperitively, this is easy and requires little code, however declaratively is causing me issues. I can make the call but am unsure of how to extract say the 'FirstName' from the response object and place it into a textbox. Currently I having something like:
<textBox id="txtFirstName">
<bindings>
<binding id="custServiceBindingFirstName" property="text"
dataContext="custService"
dataPath="response.object"
automatic="false" />
</bindings>
</textBox>
which as you would expect, simply puts the text '[object Object]' into the text field, but I have tried a number of permutations and am unsure of how to extract the 'FirstName' property from the response.object so that the binding can put this value into the text field. I know the calls are being made fine in my delcarative code as I can see the call being made via Fiddler tool and can see the serialise properties. I am sure I am missing something simple.
Thanks in advance.You're so close :). Try changing the dataPath to "response.object.FirstName".
Atlas will basically split the data path (based on the dot), and treateach part as a property. In this case, "response" is a property ofcustService, "object" is a property of the return value ofcustService.get_response() and "FirstName" is a property of the returnvalue of custService.get_response().get_object().

That was one of the 1st things I tried but I get a javascript error saying that the 'Object doesn't support this property or method'.


Oh, I see. Interesting. When an object is serialized to JSON,properties like FirstName are really just fields. If the serializerwould actually generate "real properties" (ie. functions likeget_FirstName), and a getDescriptor implementation (to register theseproperties), then it should've worked.
I think this means you currently can't do this declaratively.

Okay, fair enough. Does that mean it probably will work in future builds? I would assume so.
Just as a FYI, the object gets serialised like so (taken from fiddler):
{"FirstName":"Joe","LastName":"Bloggs","AddressLine1":"Level 1","AddressLine2":"18 Tarmac rd","Suburb":"Scumsville","PostCode":1234,"DateOfBirth":new Date(2003,0,17,17,37,46)}
I'm pretty sure they will come up with a way that will make it possible.
If you want, you could probably hack the atlas scripts a bit to addsupport for it yourself (obviously this is not recommended). All you'dhave to do is extend this object, after it was 'deserialized', byadding a getDescriptor implementation and add the 'properties'. I couldhelp you with this, but the question is: how badly do you want to getthis working? :)

Well its not that important really, but your offer is tempting to simply learn the inner workings of Atlas better. I'll get back to you on that one Wilco. I might hack and slash at it first, then give you a shout if (when :-) ) I get stuck.

Thanks heaps for the offer.


Ralph Sommerer's post about the beautified sources may help you to read the source code.

Okay, I have given this a shot and have gone part way there. I have defined a type descriptor function, and have added a property 'FirstName' of type string to the type descriptor for my object. Next, I do a:
Web.Component.registerBaseMethod(this,'getDescriptor');

I am not sure if this is correct, in fact, I am just guessing at which 'registerBaseMethod' implementation I should be using (ie. the Web.Component one listed above or a different one).
I then register a 'getter' for the object:
o.get_FirstName = function() { return 'My Firstname Text'; };

And all this works, generates no errors, and in the declarative section of the Atlas markup, the "response.object.FirstName" now works and returns the hardcoded text I have used in the line above. My questions are:
Is my usage ofWeb.Component.registerBaseMethodcorrect? Should I be using the registerBaseMethod of a different class?
I am now not sure how to generically examine the returned serialised object for its list of properties, and determine their types, then add them into the type descriptor. I imagine there are some support routines around this but I haven't found them yet. I could write up a lengthy string parsing routine but that doesn't sound like much fun. Any direction here would be helpfull.
Thanks.
P.S. As you are probably aware, this is all in the AtlasCore.js file. Also note, this excercise is not that important, but it is heling me to familiarise myself with the Atlas libraries.

Hi,
I think your problem should be related to mine:http://forums.asp.net/1080824/ShowPost.aspx so I tried setting datapath to dot convention, as Wilco suggests, but it still doesnt work. What is interesting is that when i use dataPath like "anything.something" my controls show empty space (even in this case) instead of "undefined" word. I tried all combinations of my custom class names like _actualTO.Brn, Xdmpactualto.Brn etc etc..but no result :(
Adam


The method registerBaseMethod is part of the Function prototype (ie.all functions have this function :)). You should generally call thisfunction like:
MyComponent.registerBaseMethod(this, 'getDescriptor');
You should also do this _inside_ 'MyComponent'.
It would probably be easier to modify the JSON.deserialize implementation, to something like:
this.deserialize = function(data) {
var x = eval('(' + data +')');
return new ObjectWrapper(x);
}
[...]
Web.ObjectWrapper = function(obj) {
var _obj = obj;
for (var member in _obj) {
// Create properties for each non-function in the wrapped object.
if (typeof(_obj[member]) != "function") {
this['get_' + member] = function() {
return _obj[member];
}
this['set_' + member] = function(value) {
_obj[member] = value;
}
}
}
this.getDescriptor = function() {
var td = new Web.TypeDescriptor();
for (var member in _obj) {
if (typeof(_obj[member]) != "function") {
td.addProperty(member, Object.getType(_obj[member]));
}
}
return td;
}
}
Type.registerClass('Web.ObjectWrapper', null, Web.ITypeDescriptorProvider);
I haven't tested this, so I don't know if this would actually work. But I think something like this would work.

Okay, I have modifications as per your post above. My current code looks like this:

this.deserialize =function(data)
{
var o = eval('('+data +')');

// Previous code
// return o;

// New code.
return new Web.DeserialisedPropertyWrapper(o);
}
}


// The object that 'wraps' the returned/serialised object. This object provides a type descriptor
// that defines each property within the deserialised object. Without this, the properties are
// not available within the declarative markup sections.
Web.DeserialisedPropertyWrapper = function(obj) {
var _obj = obj;

for (var member in _obj) {
// Create properties for each non-function in the wrapped object.
if (typeof(_obj[member]) != "function") {
this['get_' + member] = function() { // getter
return _obj[member];
}
this['set_' + member] = function(value) { // setter
_obj[member] = value;
}
}
}

// Set up property descriptions in our type descriptor for the object wrapper.
this.getDescriptor = function() {

var td = new Web.TypeDescriptor();
for (var member in _obj) {
if (typeof(_obj[member]) != "function") {
td.addProperty(member, Object.getType(_obj[member]));
}
}
return td;
}
}

// Make sure our new property wrapper object is registered and available.
Type.registerClass('Web.DeserialisedPropertyWrapper', null, Web.ITypeDescriptorProvider);

It 'mostly works' however the issue is that the 'getter' always returns the value of 'DateOfBirth'. Reason is because the'DateOfBirth' is what is the last field is when setting up the functions for each getter. Subsequently, the variable'member' is set at'DateOfBirth' when the function exists. The code for each getter in each function reads'return _obj[member]' which obviously equates to'return _obj['DateOfBirth'];' (because DateOfBirth is what member last equalled).
My javascript is pretty poor and this is probably the real issue, however I need to ensure that the function that each getter uses equates to 'return _obj['FirstName']; ' or something similar, not an actual variable that is defined when the 'getter' is setup. Hope I am making sense here, but I am unsure how to achieve this in javascript.

No comments:

Post a Comment