On the project I am currently on, we have had some troubles with some of the JSON responses returned from the server. Specifically if the strings contained escaped characters. We are using ExtJs on this project and upon receiving the response on the client, we were doing an Ext.decode(response.responseText); This was throwing exceptions, like error: unterminated string literal.
All the values that caused problems were escaped characters.
\"
\\
\r
\n
\t
It turns out that those escape sequences in the response are treated as part of the JSON source, not the JSON data. If your data contains escaped characters, the '\' needs to be encoded so that it becomes part of the data, and the Ext.decode(or eval) will then remove the extra '\'. The list above becomes:
\" --> \\\"
\\ --> \\\\
\r --> \\r
\n --> \\n
\t --> \\t
\\ is particularly bad if it is the last character in the data and it is not encoded, as it will combine with the field terminating ", and cause the JSON to fail.
\n or \r, cause the JSON source to have a return in the middle of the JSON, thus invalidating it.
\" will cause the field to prematurely terminate, causing the JSON to fail.
\t is really pretty harmless, as it actually tabs the data, but at least it doesn't cause the JSON to fail. Other ones that may be of concern, but haven't investigated.
\b - backspace
\f - formfeed
\v - vertical tab
\' - single quote(haven't noticed this one being a problem)
Wednesday, July 29, 2009
Monday, April 13, 2009
Grid Panel as a form panel Element
We recently ran into a little problem in that we needed a grid panel to be displayed on a form panel. It displayed fine but it was left justified and made the form look pretty bad. One of my co-workers stumbled on to an undocumented feature that solves the problem. I just wanted to share it and post it for my own future reference.
For the life of us, we couldn't figure out how to tell the GridPanel to render in the fieldItem column and not the fieldLabel (All the other items let you define a fieldLabel property, but the GridPanel didn't have this option) column of the FormPanel. We finally found out that there is a property that is not documented anywhere, or anywhere that I can find, that tells the GridPanel that it is a formItem. So, in order to treat a GridPanel like a formItem all you have to do is set a property "isFormField" to true, slick huh. Now if you want to, you can also define a fieldLabel for that as well. This will align your GridPanel with all your other fieldItems.
Hope this is helpful.
For the life of us, we couldn't figure out how to tell the GridPanel to render in the fieldItem column and not the fieldLabel (All the other items let you define a fieldLabel property, but the GridPanel didn't have this option) column of the FormPanel. We finally found out that there is a property that is not documented anywhere, or anywhere that I can find, that tells the GridPanel that it is a formItem. So, in order to treat a GridPanel like a formItem all you have to do is set a property "isFormField" to true, slick huh. Now if you want to, you can also define a fieldLabel for that as well. This will align your GridPanel with all your other fieldItems.
Hope this is helpful.
Wednesday, February 25, 2009
Manipulating Arrays in JavaScript
In my recent work I have had to do a lot of array manipulation in JavaScript. While doing this I learned a few tricks, that I did not want to forget and thought others might benefit from.
Some of these things are simple, but I include here for completeness. I welcome any feedback on any array tricks that you have learned.
More to come later...
Some of these things are simple, but I include here for completeness. I welcome any feedback on any array tricks that you have learned.
- Adding to an array
var array = [];
array.push(5835);
- Deleting from an array
var array =[];
array.push(5835); // index of 0
array.push(6835); // index of 1
array.push(7835); // index of 2
delete array[1];
The resulting array would be [5835, 7835], but the indexes would still be [0,2]. JavaScript does not reorder the indexes to always be zero based.
- Array Looping
Although you can loop through an array normally:
for(var i = 0; i < array.length; i++) {
alert("Index was " + i + ", value was " + array[i]);
}
You can get into trouble, if you are doing array manipulation like deleting. Like I stated in #2, there is no more index 1, so you would get a JavaScript error.
Because of this I have become a fan of the following loop:
for(var i in array) {
if (typeof array[i] == "function") {
continue;
}
alert("Index was " + i + ", value was " + array[i]);
}
A couple of things to note, first is the check to make sure the element is not a function. All arrays have a remove method inherent in them, so you need to skip that. Second, you will see that the indexes are indeed, 0, 2 and not 0, 1, if you use the dataset from #2.
More to come later...
Monday, October 27, 2008
Dynamic Size Arrays in javascript
Recently I needed to create a dynamic array in javascript. I didn't want to have two complete arrays defined and choose based off an if statement. So to do this I used the push() command. It easily allowed me to tailor the contents of the array based on various boolean flags. The array I was creating was for the tools param of a new Ext.Panel object. As you can see in the sample code below, the publish button is only added to the tool array if the isShareable flag is true.
var tools = [];
tools.push({
id:'toggle',
handler: this.onToggle.createDelegate(this),
qtip: "toggle"
});
tools.push({
id:'help',
handler: this.onHelp.createDelegate(this),
qtip: "help"
});
tools.push({
id:'gear',
handler: this.onConfigure.createDelegate(this),
qtip: "configure"
});
tools.push({
id:'refresh',
handler: this.onRefresh.createDelegate(this),
qtip: "refresh"
});
if (this.isShareable) {
tools.push({
id:'save',
handler: this.onPublish.createDelegate(this),
qtip: "publish"
});
}
tools.push({
id:'close',
handler: this.onClose.createDelegate(this),
qtip: "close"
});
var tools = [];
tools.push({
id:'toggle',
handler: this.onToggle.createDelegate(this),
qtip: "toggle"
});
tools.push({
id:'help',
handler: this.onHelp.createDelegate(this),
qtip: "help"
});
tools.push({
id:'gear',
handler: this.onConfigure.createDelegate(this),
qtip: "configure"
});
tools.push({
id:'refresh',
handler: this.onRefresh.createDelegate(this),
qtip: "refresh"
});
if (this.isShareable) {
tools.push({
id:'save',
handler: this.onPublish.createDelegate(this),
qtip: "publish"
});
}
tools.push({
id:'close',
handler: this.onClose.createDelegate(this),
qtip: "close"
});
Monday, September 22, 2008
NoClassDefFound Exceptions tied to PermGen Space
Today I spent a good deal of time trying to figure out why I was getting a bunch of NoClassDefFoundErrors. The changes I made were not significant, but I did make one configuration change to JBoss. It seems the default is to compile the JSP pages with Java 1.4, so I changed it to 1.5, as a new JSP page used some Java 1.5 generics.
When I tried to bring the application back up, I got the following different errors each time I restarted JBoss.
javax.servlet.ServletException: java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/problem/DefaultProblem
java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/Argument
NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/SingleTypeReference
Finally, I got a PermGen space error.
A friend suggested increasing my PermGen memory in JBoss, which I did by adding the following line to the run.bat file in the JBoss bin directory:
set JAVA_OPTS=%JAVA_OPTS% -XX:PermSize=512m -XX:MaxPermSize=512m
This fixed the problem. So when having weird org/eclipse NoClassDefFoundErrors being thrown, try increasing your PermSize.
When I tried to bring the application back up, I got the following different errors each time I restarted JBoss.
javax.servlet.ServletException: java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/problem/DefaultProblem
java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/Argument
NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/SingleTypeReference
Finally, I got a PermGen space error.
A friend suggested increasing my PermGen memory in JBoss, which I did by adding the following line to the run.bat file in the JBoss bin directory:
set JAVA_OPTS=%JAVA_OPTS% -XX:PermSize=512m -XX:MaxPermSize=512m
This fixed the problem. So when having weird org/eclipse NoClassDefFoundErrors being thrown, try increasing your PermSize.
Thursday, September 4, 2008
Custom Events in ExtJS
I was kind of new to the idea of event driven applications, but after reading a little about it fell in love with it. I immediately had some applications in my work where it would solve particular problems I was having. Having never implemented this before, I didn't know where to start. Searching the ExtJS forum I uncovered some good information, but not enough to just jump right into it.
I thought I would blog about it while it was still fresh in my mind.
First you need to extend the Observable class for the class that you want to add a new custom event to. Then in a method called when the class is constructed, you need to add the new custom event. I needed to throw a custom event whenever the tab changed. As you can see from the code below, that a 'tabchange' event triggered a call to onTabChange function. This function then fires the 'myCustomEvent' event. That is all it takes to create a custom event. Of course, nothing will happen if there are not any listeners registered for that event.
Example.Events = function(config) {
this.initialize(config);
};
Ext.extend(Example.Events, Ext.util.Observable, {
initialize: function(config) {
this.addEvents('myCustomEvent');
this.panel = new Ext.TabPanel({
.....
};
this.panel.on('tabchange', this.onTabChange.createDelegate(this));
}
onTabChange : function(tabPanel, tab) {
var index = -1;
if (tab.getIndex) {
index = tab.getIndex();
}
this.fireEvent('myCustomEvent', index);
},
}
In my case, I had widgets that existed on tabs and they need to know when their tab was activated. So inside those widgets they needed to obtain a reference to the Example.Events object for our application, and then register as a listener and tell it which function to call back on. I did this in the widget constructor, as shown below.
in constructor:
var event= Example.Parent.getEvent();
event.on('myCustomEvent', this.tabSelected.createDelegate(this));
this.myTabIndex = event.getMyIndex();
The call back function:
tabSelected : function(index) {
if (index == myTabIndex) {
// Do something if your tab is activated
}
},
That's it. There is nothing more to it. And it works like a charm.
I thought I would blog about it while it was still fresh in my mind.
First you need to extend the Observable class for the class that you want to add a new custom event to. Then in a method called when the class is constructed, you need to add the new custom event. I needed to throw a custom event whenever the tab changed. As you can see from the code below, that a 'tabchange' event triggered a call to onTabChange function. This function then fires the 'myCustomEvent' event. That is all it takes to create a custom event. Of course, nothing will happen if there are not any listeners registered for that event.
Example.Events = function(config) {
this.initialize(config);
};
Ext.extend(Example.Events, Ext.util.Observable, {
initialize: function(config) {
this.addEvents('myCustomEvent');
this.panel = new Ext.TabPanel({
.....
};
this.panel.on('tabchange', this.onTabChange.createDelegate(this));
}
onTabChange : function(tabPanel, tab) {
var index = -1;
if (tab.getIndex) {
index = tab.getIndex();
}
this.fireEvent('myCustomEvent', index);
},
}
In my case, I had widgets that existed on tabs and they need to know when their tab was activated. So inside those widgets they needed to obtain a reference to the Example.Events object for our application, and then register as a listener and tell it which function to call back on. I did this in the widget constructor, as shown below.
in constructor:
var event= Example.Parent.getEvent();
event.on('myCustomEvent', this.tabSelected.createDelegate(this));
this.myTabIndex = event.getMyIndex();
The call back function:
tabSelected : function(index) {
if (index == myTabIndex) {
// Do something if your tab is activated
}
},
That's it. There is nothing more to it. And it works like a charm.
JavaScript handling of time zones
On the project I am currently working on, we had a problem to solve regarding time zones with our application. From the web interface the user was entering time for some of the fields. The time was being sent to the server and stored in a database. The problem we ran into was the client time zone was different than the server timezone. The database on the server was converting all times that were saved to the server time zone. When the data was retrieved, it was displaying the server time values and presenting it to the user as if it were client local time. Obviously we were not taking time zones into account.
Like most problems, there is usually more than one way to solve it. The way we solved it, was to make sure that all of the times that were being passed back and forth through the REST web service calls were in GMT (Zulu) time. The database can convert to local time, but before it is sent to the client, it is converted back to GMT. Likewise, in the browser, the user can enter time in local time, but before it is sent to the server it is converted to GMT time.
The rub came in finding the right java script date functions to allow for conversion to and from GMT. The GMT string representations that were being passed back and forth, needed converted to the clients local time. This was easily accomplished by using the java script Date constructor.
var localTimeZoneDate = new Date(gmtTimeZoneDateStr);
The date components the UI implemented, needed to be initialized with milliseconds from epoch time. To get that information we simply called Date.parse(localTimeZoneDate);
On the server it was equally easy to convert the value retrieved from the database to GMT.
String dateFormatStr = "EEE, dd MMM yyyy HH:mm:ss z";
SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatStr );
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = dateFormat.parse(timeStr);
In reviewing what we did, there may have been an easier solution. Both the Java Date object and the JavaScript Date object, are able to return milliseconds from epoch. It would have been much simpler to just send that value back and forth, and not worry about the string format.
Like most problems, there is usually more than one way to solve it. The way we solved it, was to make sure that all of the times that were being passed back and forth through the REST web service calls were in GMT (Zulu) time. The database can convert to local time, but before it is sent to the client, it is converted back to GMT. Likewise, in the browser, the user can enter time in local time, but before it is sent to the server it is converted to GMT time.
The rub came in finding the right java script date functions to allow for conversion to and from GMT. The GMT string representations that were being passed back and forth, needed converted to the clients local time. This was easily accomplished by using the java script Date constructor.
var localTimeZoneDate = new Date(gmtTimeZoneDateStr);
The date components the UI implemented, needed to be initialized with milliseconds from epoch time. To get that information we simply called Date.parse(localTimeZoneDate);
On the server it was equally easy to convert the value retrieved from the database to GMT.
String dateFormatStr = "EEE, dd MMM yyyy HH:mm:ss z";
SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatStr );
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = dateFormat.parse(timeStr);
In reviewing what we did, there may have been an easier solution. Both the Java Date object and the JavaScript Date object, are able to return milliseconds from epoch. It would have been much simpler to just send that value back and forth, and not worry about the string format.
Subscribe to:
Posts (Atom)
