app:iterator processing one vs. multiple XML results

0 views
Skip to first unread message

pric...@xaware.com

unread,
Feb 28, 2008, 6:11:42 PM2/28/08
to Appcelerator Platform SDK
I've just started integrating Appcelerator with our product XAware,
which can return HTML results over http. I created a simple result
that returns a contact list such as:

<contactlist>
<contact>
<firstname>John</firstname>
<lastname>Smith</lastname>
<phone>719-555-1111</phone>
</contact>
<contact>
<firstname>Jane</firstname>
<lastname>Doe</lastname>
<phone>719-555-2222</phone>
</contact>
</contactlist>

I tried using the following app:http and app:iterator widgets, similar
to your techcrunch rss feed example:

<app:http on="l:get.contacts then get">
<uri method="get" uri="http://localhost:8090/xaware/bizview/
XAFiles/TestDemo1.xbd" response="l:get.contacts.response"></uri>
</app:http>

<app:iterator on="l:get.contacts.response then execute"
property="contactlist.contact">
<html:div>DATA FOR CONTACT:</html:div>
<html:div>#{firstname}</html:div>
<html:div>#{lastname}</html:div>
<html:div>#{phone}</html:div>
</app:iterator>

<app:message name="l:get.contacts"></app:message>

It seems to work fine if my XML results contain 2 or more contacts.
But if the results return only one contact, then the response message
is different and the app:iterator can't process it (a list of 1); no
results are displayed. I can reference the data with the value
syntax:
<div on="l:get.contacts.response then
value[contactlist.contact.firstname]"></div>

Does this mean that I have to code special processing if the list
contains only one item (is there a way to return the count of children
elements)? or can the app:iterator handle results when only one item
is returned?

The message trace shows the following two responses if the results
contain 2 contacts (enclosed in []) or 1 contact, respectively:

[3:40 PM] local:get.contacts.response => {"contactlist":
{"contact": [{"firstname": "John", "lastname": "Smith", "phone":
"719-555-1111"}, {"firstname": "Jane", "lastname": "Doe", "phone":
"719-555-2222"}]}}

[3:41 PM] local:get.contacts.response => {"contactlist":
{"contact": {"firstname": "John", "lastname": "Smith", "phone":
"719-555-1111"}}}

thanks
Paul
xaware.org

Andrew Midson

unread,
Feb 28, 2008, 6:19:06 PM2/28/08
to appcelerator...@googlegroups.com
I'm far from an expert but it looks as though the problem is that in the response with only one contact there is no list for the iterator to iterate over. I think that :
 
{"contactlist": {"contact": {"firstname": "John", "lastname": "Smith", "phone":"719-555-1111"}}}
 
needs to be:
 
{"contactlist":{"contact": [{"firstname": "John", "lastname": "Smith", "phone":"719-555-1111"}]}}
 
I'm afraid I don't have time to check this just now, but that would be my guess.
 
Regards
 
Andrew
 

Jeff Haynie

unread,
Feb 28, 2008, 6:21:36 PM2/28/08
to appcelerator...@googlegroups.com
You're correct in the way this works and it's stupid (I wrote it to do this).

When I transform from JSON to XML I don't know if an element will have more than 1 node.  So, I essentially wanted to be able to do simple stuff like:

contactlist.contact.firstname 

to give you the firstname

However, it breaks down in the case of multiples -- in which case if I see one than one of the same element I know it's an array.

What I'm not sure on is the correct implementation of how this should work.  I either have to assume that each element is an array or do something else (which I'm not sure what it is).


Anyone know of a generic purpose library for converting XML to JSON -- that already addresses this?



Jeff

Jeff Haynie

unread,
Feb 28, 2008, 6:25:10 PM2/28/08
to appcelerator...@googlegroups.com
Another idea is to fix this in the iterator and make it a little more smart such that if you have an object -- you assume it's an array with 1 element.

Maybe that would work....  Thoughts anyone?

Jeff Haynie

unread,
Feb 28, 2008, 6:36:46 PM2/28/08
to Appcelerator Platform SDK
OK - I've made a fix in the iterator. The fix assumes if the property
points to an object instead of an array - that it should assume it's
an array with the object as the only member. You should be able to
replace your app_iterator.js file under modules/app_iterator with this
file -- assuming you're using the current 2.0.2 release. Also, this
patch has been made to head and will be in next weeks 2.1 release. I
have to paste it inline -- can't figure out how to attach it using
google groups.




Appcelerator.Module.Iterator = {
getName: function() {
return "appcelerator iterator"
},
getDescription: function() {
return "iterator widget"
},
getVersion: function() {
return 1
},
getSpecVersion: function() {
return 1
},
getAuthor: function() {
return "Jeff Haynie"
},
getModuleURL: function() {
return "http://www.appcelerator.org"
},
isWidget: function() {
return true
},
getWidgetName: function() {
return "app:iterator"
},
getActions: function() {
return ["execute"]
},
execute: function(id, parameterMap, data, scope) {
var compiled = parameterMap["compiled"];
var propertyName = parameterMap["property"];
var items = parameterMap["items"];
var table = parameterMap["table"];
var width = parameterMap["width"];
var headers = parameterMap["headers"];
var selectable = parameterMap["selectable"];
var array = null;
if (!compiled) {
compiled = eval(parameterMap["template"] + "; init_" +
id);
parameterMap["compiled"] = compiled
}
if (items) {
data = items.evalJSON() || []
}
if (propertyName) {
array = Object.getNestedProperty(data, propertyName) || []
} else {
array = data
}
var html = "";
if (!array) {
html = compiled(data)
} else {
if (table) {
html += '<table width="' + width + '" cellspacing="' +
parameterMap["cellspacing"] + '"><tr>';
headers.each(function(h) {
html += "<th>" + h + "</th>"
});
html += "</tr>"
}
// this is in the case we pass in an object instead of
// an array, make it an array of length one so we can iterate
if (!Object.isArray(array))
{
array = [array];
}

for (var c = 0, len = array.length; c < len; c++) {
var o = array[c];
if (typeof o != "object") {
o = {
"iterator_value": o
}
}
o["iterator_index"] = c;
o["iterator_length"] = len;
o["iterator_odd_even"] = (c % 2 == 0) ? "even": "odd";
if (table) {
if (o["iterator_odd_even"] == "odd") {
html += '<tr class="' +
parameterMap["rowOddClassName"] + '">'
} else {
html += '<tr class="' +
parameterMap["rowEvenClassName"] + '">'
}
}
for (idx in o) {
if (typeof o[idx] == "string") {
o[idx] = o[idx].replace(/'/, "\u2019")
}
}
html += compiled(o);
if (table) {
html += "</tr>"
}
}
if (table) {
html += "</table>"
}
}
var element = $(id);
if (selectable) {
element.setAttribute("selectable", selectable)
}
element.innerHTML = html;
Appcelerator.Compiler.dynamicCompile(element)
},
getAttributes: function() {
return [{
name: "on",
optional: true,
description: "Used to execute the iterator"
},
{
name: "items",
optional: true,
description: "Literal (or template-replaced) JSON array to
iterate over"
},
{
name: "property",
optional: true
},
{
name: "rowEvenClassName",
optional: true
},
{
name: "rowOddClassName",
optional: true
},
{
name: "table",
optional: true,
defaultValue: "false"
},
{
name: "width",
optional: true,
defaultValue: "100%"
},
{
name: "headers",
optional: true,
defaultValue: ","
},
{
name: "cellspacing",
optional: true,
defaultValue: "0"
},
{
name: "selectable",
optional: true
}]
},
compileWidget: function(A) {
this.execute(A["id"], A, null, "")
},
buildWidget: function(A, B) {
B["template"] =
Appcelerator.Compiler.compileTemplate(Appcelerator.Compiler.getHtml(A),
true, "init_" + A.id);
B["table"] = B["table"] == "true";
if (B["table"]) {
B["headers"] = B["headers"].split(",")
}
var C = !!(!B["on"] && B["items"]);
return {
"presentation": "",
"position": Appcelerator.Compiler.POSITION_REPLACE,
"parameters": B,
"wire": true,
"compile": C
}
}
};
Appcelerator.Core.registerModule("app:iterator",
Appcelerator.Module.Iterator)














On Feb 28, 6:25 pm, "Jeff Haynie" <jhay...@gmail.com> wrote:
> Another idea is to fix this in the iterator and make it a little more smart
> such that if you have an object -- you assume it's an array with 1 element.
> Maybe that would work....  Thoughts anyone?
>
>
>
> On Thu, Feb 28, 2008 at 6:21 PM, Jeff Haynie <jhay...@gmail.com> wrote:
> > You're correct in the way this works and it's stupid (I wrote it to do
> > this).
> > When I transform from JSON to XML I don't know if an element will have
> > more than 1 node.  So, I essentially wanted to be able to do simple stuff
> > like:
>
> > contactlist.contact.firstname
>
> > to give you the firstname
>
> > However, it breaks down in the case of multiples -- in which case if I see
> > one than one of the same element I know it's an array.
>
> > What I'm not sure on is the correct implementation of how this should
> > work.  I either have to assume that each element is an array or do something
> > else (which I'm not sure what it is).
>
> > Anyone know of a generic purpose library for converting XML to JSON --
> > that already addresses this?
>
> > Jeff
>
> > On Thu, Feb 28, 2008 at 6:11 PM, <pricha...@xaware.com> wrote:
>
> > > I've just started integrating Appcelerator with our product XAware,
> > > which can return HTML results over http.   I created a simple result
> > > that returns a contact list such as:
>
> > >  <contactlist>
> > >      <contact>
> > >          <firstname>John</firstname>
> > >          <lastname>Smith</lastname>
> > >          <phone>719-555-1111</phone>
> > >      </contact>
> > >      <contact>
> > >          <firstname>Jane</firstname>
> > >          <lastname>Doe</lastname>
> > >          <phone>719-555-2222</phone>
> > >      </contact>
> > >  </contactlist>
>
> > > I tried using the following app:http and app:iterator widgets, similar
> > > to your techcrunch rss feed example:
>
> > >  <app:http on="l:get.contacts then get">
> > >      <uri method="get" uri="http://localhost:8090/xaware/bizview/
> > > XAFiles/TestDemo1.xbd<http://localhost:8090/xaware/bizview/XAFiles/TestDemo1.xbd>"

pric...@xaware.com

unread,
Feb 28, 2008, 7:37:29 PM2/28/08
to Appcelerator Platform SDK
That sounds like a reasonable approach.

I re-deployed with the new app_iterator.js (I see the new code in my
deployed .war and in the expanded apache/webapps directory) - and I am
using 2.02. But I am still seeing the same results, the single
contact is not processed.

pric...@xaware.com

unread,
Feb 28, 2008, 7:50:43 PM2/28/08
to Appcelerator Platform SDK
I seem to be having a similar issue with the app:datatable as well.
It displays the table with 2 rows of data, but when 1 contact is
returned it only displays the header row and no data.

<app:datatable on="l:get.contacts.response then execute"
property="contactlist.contact" width="50%"
rowEvenClass="table_row_even" rowOddClass="table_row_odd">
<header property="lastname" align="left" width="40%">Last</
header>
<header property="firstname" align="left" width="40%">First</
header>
<header property="phone" align="left" width="20%">Phone</
header>
</app:datatable>

Jeff Haynie

unread,
Feb 28, 2008, 8:00:27 PM2/28/08
to appcelerator...@googlegroups.com, Appcelerator Platform SDK
Most likely a browser caching issue. Can you try and reload after
clearing cache?

Jeff Haynie

unread,
Feb 28, 2008, 8:11:15 PM2/28/08
to appcelerator...@googlegroups.com
OK - I'll need to apply a similar fix most likely.  I'll get that done here shortly.

pric...@xaware.com

unread,
Feb 29, 2008, 12:24:53 PM2/29/08
to Appcelerator Platform SDK
Thanks Jeff. That works great.

What's nice about this solution is that it works for the app:iterator
whether there are one or more rows, and the value syntax still works
when there is only one row (so the data is unchanged and the display
logic is all in the GUI where it should be):
<div on="l:get.contacts.response then
value[contactlist.contact.firstname]

(FYI. You were right, I had forgotten to clear the browser cache;
restarting Firefox doesn't clear the cache! And I also accidentally
word wrapped some lines when I pasted the code from this post into a
file, causing an error in the .js. It's all working now!)

Jeff Haynie

unread,
Feb 29, 2008, 2:02:11 PM2/29/08
to appcelerator...@googlegroups.com
Awesome. I have the same patch done for app:datatable if you want that as well.  It's in SVN and will be in next week's release.
Reply all
Reply to author
Forward
0 new messages