Friday, November 16, 2007

How To : Send Ext datastore to .Net dataset with XML

There are many examples of using the Ext xmlreader to get data xml data into a datastore on the client, however I wanted to do the reverse and allow my CRUD application to update the modified datastore back to the server, updated with user changes.

On the client I created a 'serialize' function to convert the datastore to XML, and then did an Ext.data.Connection to post the data to the server.

SaveDStoServer :function(ds) {

var prog = Ext.MessageBox.wait("Saving data to server");

var ds_serialized= myscope.SerializeDS(ds);

var serv= new Ext.data.Connection();
serv.request({
url: "Service.asmx/Savedata",
params: {myID: 123, datastore:ds_serialized},
method: 'POST',
scope: this,
callback: function(options, success, response){
prog.hide();
if (success){
var xml = response.responseXML;
}
else {
if( response.status == -1 )
Ext.MessageBox.alert('Error on save','Server timeout')
else
Ext.MessageBox.alert('Error on save',response.responseText)
}
}
});
},



SerializeDS : function (ds) {

var columns = ds.fields.keys; // get columns from data store
var retdata ="&ltNewDataSet>";

ds.each ( function (rec) {
retdata +="&lt/datarow>";
for (var i=0; i< columns.length; ++ i)
retdata += "<" + columns[i] + ">" + rec.data[columns[i]]
+ "&lt/" + columns[i] + ">"
retdata += "&lt/datarow>";
});

retdata += "&lt/NewDataSet>";

return ""+retdata;
},
},


On the server side web service method, I create an XML document, create a reader and then read into the dataset. I tried using 'XMLdatadocument' but if did not seem able to create a schema and had an empty dataset. This may not be the most efficient way but it seems to work well. As always, be aware of web service parameter verification to prevent SQL injection.

[WebMethod]
public String Savedata (String SchID, String datastore)
{

DataSet ds = new DataSet();
XmlDocument doc = new XmlDocument();

// Load the xml into an XmlDocument
doc.LoadXml(datastore);
StringReader sreader = new StringReader(doc.DocumentElement.OuterXml);

ds.ReadXml(sreader);

foreach (DataRow row in dsCraft.Tables[0].Rows)
{
System.Diagnostics.Debug.WriteLine(row[0] + " " + row[1]);
}

return "success";
}

Monday, September 3, 2007

.Net Web Service with Ext data store

After using Ajaxpro with a couple of applications, I thought I should look into a more standardized approach for Ext/.Net integration. Specifically, how to bind SQL data on the server to a Ext data store. Ajaxpro's future seems uncertain.

First the Web Service:

[WebMethod]
public XmlDocument getSites(String Site, String Type, String Disc, String Manu, String query, String limit, String start, String callback)
{

XmlDocument xmlDoc = new XmlDocument();
DataSet ds = new DataSet();
String sql = " Select SITE as col1 from mytable where site = " + Site;

// create a connection
SqlDataAdapter oda = new SqlDataAdapter(sql, connStringSQL);
oda.Fill(ds);
xmlDoc.LoadXml(ds.GetXml());
return xmlDoc;
}
And the Ext code, with the .Net xml doc having 'Table' as record:

// parameters that get passed
var inparms ={query: '', start: '', limit: '', callback: '', Site: '', Type: '', Disc: '', Manu: ''};

var xmlread = new Ext.data.XmlReader({ record: 'Table' }, [
// set up the fields mapping into the xml doc
{name: 'col1'} ]);

// create the Data Store
var dsSites = new Ext.data.Store({
// load using HTTP
proxy: new Ext.data.HttpProxy({method:'POST',url: 'Service.asmx/getSites'}),
reader: xmlread, params: inparms });
.
.
.
dsSites.load( { params : inparms});
or
dsSites.load( {params : {query: '', start: '', limit: '', callback: '', Site: 'London', Type: '', Disc: '', Manu: ''});

Tuesday, August 14, 2007

Ext Combobox example... the very basics

I created a fairly sophisticated Ext application with editable grids and layouts several months ago. I now have a requirement for a new application. I thought I would start anew, resisting the temptation to just copy my old code and start modifying. I wanted to start with a clean slate but I was surprised how difficult it was to get started.

After some frustration with documentation and examples trying to get a simple combobox created, I distilled one of the examples down to the essentials. You should be able to install Ext, copy/paste the code below and you should be good to go. There are just two files you are creating, a HTML and ext_main.js...

HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>my combo</title>
<link rel="stylesheet" type="text/css" href="lib/ext/resources/css/ext-all.css" />

<!-- GC --> <!-- LIBS -->
<script type="text/javascript" src="lib/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="lib/ext/ext-all.js"></script>

<script type="text/javascript" src="ext_main.js"></script>

</head>
<body>
<h1>Simple Combo</h1>

<input type="text" id="local-states" size="20"/>

</body>
</html>

Code in ext_main.js:

/*
* Ext JS Library 1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://www.extjs.com/license
*/

// some data used in the examples
Ext.namespace('Ext.exampledata');

Ext.exampledata.states = [
['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
['CA', 'California'],
['CO', 'Colorado'],
['CN', 'Connecticut'],
['DE', 'Delaware'],
['DC', 'District of Columbia'] ];

var combos = {
init : function(){


// simple array store
var store = new Ext.data.SimpleStore({
fields: ['abbr', 'state'],
data : Ext.exampledata.states
});
var cbsite = new Ext.form.ComboBox({
store: store,
displayField:'state',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
emptyText:'Select a state...',
selectOnFocus:true
});

cbsite.applyTo('local-states');

}};
Ext.onReady(combos.init, combos);


Saturday, June 9, 2007

Teched 2007

Some thoughts on the Teched conference:
  • Very little or no negative discussion of Microsoft technologies. The talk about Microsoft Live did not once mention 'google'. I only once heard someone mention a non-Microsoft Ajax framework, when someone in a Q&A mentioned Dojo. When a presenter is so one sided in their presentation or so narrowly focused, it makes question what is being resented. For example, how can someone go on and on about how great the 'Web Developer Toolbar' is and not once mention Firebug?
  • A subtle implication that the 'buzz' around Ajax is in fact all about Microsoft's ASP.Net Ajax. Microsoft's framework is supposedly unique in that it uses OO concepts and 'prototype' in the javascript.
  • Alot of the developer hype was around Silverlight and how it allows developers to write C# code on the web client. This seems to be Microsofts latest effort for world domination. There is no way to make money writing javascript libraries, so with Silverlight, the plugin will relegate the browser to the role of a window frame. If successful, microsoft will then have all web development in C#, selling more copies of Visual Studio... and more $ for Micro$soft. Scary.
  • Office seems to be morphing into system for users to create and share rich browser based applications and documents, using Sharepoint.
  • I find it ironic that while microsoft is talking about the great capabilities of its Ajax toolkit, their Teched website is terrible. It is full of postbacks, arcane menuing and is not Firefox friendly. Even the ASP.Net/Ajax site has missed obvious opportunities for Ajax use in its interface.
  • There were some excellent presentations however. A couple of these were around security, with real-world examples of how systems have been breached and the implications.

Thursday, May 31, 2007

Browser bookmarks

Afraid you'll lose your bookmarks if your disk crashes, or when you jump to another computer? Some solutions I have found:

  • http://www.quickbookmarks.com This is basically an online repository for your bookmarks. Wherever you are in the world, you can log in and find that obscure bookmark you saved. The only downside I seen is you have to explicitly define each bookmark with no bulk import feature.
  • http://www.mybookmarks.com This page DOES allow importing of bookmarks but I'm not a big fan of the clunky interface. You just need to export you bookmarks to html (see below) and then import.
  • Firefox has a neat bookmark export feature that exports your bookmarks to an html page! Just go to Bookmarks/Organize and then File/Export and save to an HTML file for backup or stick it on your web page.

Wednesday, May 23, 2007

Using AjaxPro with Ext data store

As I posted on Ext forum I use the following modified code to use AjaxPro with Ext. It helps avoid emedding URLs in the proxy call.

ds = new Ext.data.Store({
proxy: new Ext.data.AjaxProxy(myAsp, "GetMovies"),
reader:reader,
remoteSort: true
});


And AjaxProProxy.js, modified from Rodiniz:

Ext.data.AjaxProxy = function(ajaxProObject, method) {
Ext.data.AjaxProxy.superclass.constructor.call(this);
this.ajaxProObject = ajaxProObject;
this.method = method;
};
Ext.extend(Ext.data.AjaxProxy, Ext.data.DataProxy, {

// Harley's load function
load: function(params, reader, callback, scope, arg) {
if(this.fireEvent("beforeload", this, params) !== false) {
var s = [];
for (var x in params) {
s[s.length] = "params[\"" + x + "\"]";
}
s = s.join(",");
var o = {
params: params || {},
request: {
callback : callback,
scope : scope,
arg : arg
},
reader: reader,
callback: this.loadResponse,
scope: this
};

eval("this.ajaxProObject[this.method](" + s + ", this.loadResponse, o)");

} else {
callback.call(scope||this, null, arg, false);
}
},


// Rodiniz's response handler

loadResponse: function(response, request) {

var o = response.context;

var result;
try {
result = o.reader.read(response.json);

}catch(e){
this.fireEvent("loadexception", this, o, response, e);
o.request.callback.call(o.request.scope, null, o.request.arg, false);
return;
}
o.request.callback.call(o.request.scope, result, o.request.arg, true);
},


slight mod in Rodiniz's reader....
read : function(response){

var r= new Object(); //to handle errors
var obj=response + "*/";
var doc = eval(obj);
if(r.error){
throw r.error;
}
return this.readRecords(doc);
},

Tuesday, May 15, 2007

Ext Combobox - Typeahead and mode local

The Ext combobox has a 'mode' property that can be either 'remote' or 'local'. These terms are somewhat misleading. They should really be called something like 'manual load' and 'auto load'. A 'remote' mode combobox gets its data store's load method called behind the scenes. A 'local' mode combo requires an explicit data store 'load' call to get the store and combobox loaded up with data.

Meanwhile, it seems that the only way to get 'typeahead' to work in a combobox is if mode:local. I'd also recommend Trigger:all and I'm not sure why anyone would not want to Trigger:all. This causes the entire combobox dropdown to be listed when the user clicks the down arrow.

var cb = new fm.ComboBox({
typeAhead: true,
mode:'local',
store: dsdevice,
forceSelection: true,
triggerAction: 'all',
displayField: 'fld1',
valueField: 'fld2',
lazyRender:true,
editable:true
});

Friday, May 11, 2007

dataset on the server....grow your own sql

I have spent some time trying to get the Ext datastore translated to a .Net dataset for updating but I've come to the conclusion that its just not worth the effort. I've heard others say that its not worth working with datasets except for simple out of the box applications and now I believe that too.

The dataset that gets sent to the server side routine needs to have its row's 'setmodified' or 'setadded' method set and then the dataset needs to be 'merged'. Then, it is just a black box in terms of what happens and it is difficult to get the underlying SQL when there are problems. I have done the following on the server side for the updates. I have a handler for the Ext data store 'update' event so that every field change on the grid will cause this code to fire. I added a timestamp column to the table to handle optimistic concurrency.

Code:

conn = new OleDbConnection(ads.ConnectionString);
conn.Open();

foreach (DataRow row in ds.Tables[0].Rows) // should always be one row
{

sqlcmd = "UPDATE TblOperations SET ";
sqlcmd += " Device = '" + row["Device"].ToString() + "', ";
sqlcmd += " Station = '" + row["Station"].ToString() + "' ";
sqlcmd += " where ID_key = " + row["ID_key"].ToString();
if (! row["timest"].ToString().Equals("") )
sqlcmd += " and timest = '" + row["timest"].ToString() + "' ";

cmd = new OleDbCommand(sqlcmd, conn);
affected += cmd.ExecuteNonQuery();
}

if ( affected == 1 )
return "";
else
return "error"



Now I need to get 'adds' and 'deletes' handled.

Dataset on the server.... ugh

I wrote the following to send the ext.datastore to the server, in response to a 'save button' click.

Code:
var editedds = myextdatastore.getModifiedRecords();

var ds4 = new Ajax.Web.DataSet();
var dt4 = new Ajax.Web.DataTable();

for(var i = 0, len = myextdatastore.fields.keys.length; i < i =" 0," len =" editedds.length;">


My C# routine correctly reads the dataset on the server but I'm disappointed that now I have to create all the insert/update/delete logic by creating 'insertcommand','updatecommand' etc. I thought the whole point of using ADO datasets was that it would handle all this logic. It seems like there is no way to get around having to code this on the server.

Below is my c# code. It returns no errors but it doesn't update either.....

Code:public static String SaveAll(System.Data.DataSet ds )
{
OleDbDataAdapter oda = getAccessAdapter();

OleDbCommandBuilder bld = new OleDbCommandBuilder(oda);
bld.GetInsertCommand(true);
OleDbCommand command;

command = new OleDbCommand(
"INSERT INTO TblOperations (Device,Operation) " +
"VALUES ( ?,?)");

command.Parameters.Add("@Device", OleDbType.VarChar, 40, "Device");
command.Parameters.Add("@Operation", OleDbType.VarChar, 40, "Operation");

oda.InsertCommand = command;

command = new OleDbCommand("UPDATE TblOperations SET Device=?, Operation=? WHERE ID_key=?");
command.Parameters.Add(new OleDbParameter("Device", OleDbType.VarChar , 50));
command.Parameters.Add(new OleDbParameter("Operation", OleDbType.VarChar, 50));
command.Parameters.Add(new OleDbParameter("ID_key", OleDbType.Numeric, 0));

oda.UpdateCommand=command;

oda.Update(ds, "Table1");

return " hello world " + oda.UpdateCommand.CommandText;

}

Ext - .Net dataset updating with grid

I am enjoying my Ext/ajaxpro grid and it seems to work great for viewing .net datasets. My next goal is to allow inline editing and updates to the dataset and of course return the modified dataset to the server for processing