Posts Tagged ‘DataGrid’

A very quick and simple fxDao client example

// August 3rd, 2009 // No Comments » // fxDao

About one month ago I released fxDao v 1.0.0, a simple PHP gateway used to execute queries on the server specifying them directly in your ActionScript code.

fxdao_example

fxDao Example Application

The main difference between the fxDao approach and others like the one of AS3FlexDB is that the main application code is loosely coupled with it. Using fxDao your main application calls static methods from DAO classes. Encapsulating the data access logic into DAO classes brings the advantage that in case data access logic changes, you have to rewrite only the DAO classes, leaving the rest of the application as is.

In this article I’ll show a very simple ActionScript DAO class example.

The main application code is listed below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    pageTitle="fxDao DAO Example"
    layout="vertical" horizontalAlign="left"
    paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10" 
    width="100%" 
    backgroundColor="#ffffff"
    initialize="onInitialize(event);"
    applicationComplete="onComplete(event);" viewSourceURL="srcview/index.html">
 
  <mx:Script>
  <![CDATA[
    import model.Contact;
    import utils.FxDaoUtils;
    import dao.ContactsDao;
    import mx.collections.ArrayCollection;
    import mx.events.FlexEvent;
 
    [Bindable]
    private var _data:ArrayCollection;
 
    private function onInitialize(event:FlexEvent):void
    {
      FxDaoUtils.loadProperties();
    }
 
    private function onComplete(event:FlexEvent):void
    {
      doReadAll();
    }
 
    private function doReadAll():void
    {
      ContactsDao.readAll(
        function(list:ArrayCollection):void {
          _data = list;
        }
      );
    }
 
    private function doSaveNew():void
    {
      var obj:Contact = getObj();
      ContactsDao.create(
        obj, 
        function(id:Number):void {
          obj.id = id;
          _data.addItem(obj);
          datagrid.selectedIndex = _data.length - 1;
          datagrid.scrollToIndex(datagrid.selectedIndex);
        }
      );
    }
 
    private function doUpdate():void
    {
      var obj:Contact = getObj();
      ContactsDao.update(
        obj,
        function():void {
          setObj(obj);
        }
      );
    }
 
    private function doDelete():void
    {
      var obj:Contact = getObj();
      ContactsDao.deleteById(
        obj.id,
        function():void {
          unsetObj(obj);  
        }
      );
    }
 
    /**
     * Return a Contact instance from form data.
     */
    private function getObj():Contact
    {
      var obj:Contact = new Contact();
      obj.id = Number(inId.text); 
      obj.firstName = inFirstName.text;
      obj.lastName = inLastName.text;
      obj.email = inEmail.text;
      return obj;      
    }
 
    /**
     * Update Contact object in data set.
     */
    private function setObj(obj:Contact):void
    {
      for each (var item:Contact in _data)
      {
        if (item.id == obj.id)
        {
          item.firstName = obj.firstName;
          item.lastName = obj.lastName;
          item.email = obj.email;
          break;
        }
      }
    }
 
    /**
     * Remove object from data set.
     */
    private function unsetObj(obj:Contact):void
    {
      var i:Number = 0;
      for each (var item:Contact in _data)
      {
        if (item.id == obj.id)
        {
          _data.removeItemAt(i);
          datagrid.selectedIndex = -1;
          break;
        }
        i ++;
      }
    }
  ]]>
  </mx:Script>
 
  <mx:HBox verticalAlign="middle">
    <mx:Image source="@Embed('../assets/fxDaoTiny.png')" />
    <mx:Label text="DAO Class Example" fontSize="18" color="#666666">
      <mx:filters>
        <flash.filters:DropShadowFilter 
            xmlns:flash.filters="flash.filters.*" 
            angle="45" blurX="4" blurY="4" 
            distance="2" 
            alpha=".4" color="0x000000" />
      </mx:filters>
    </mx:Label>  
  </mx:HBox>
 
  <mx:Panel
      width="100%" height="100%"
      title="Datagrid" 
      fontFamily="Arial" fontSize="12">
    <mx:DataGrid id="datagrid"
          width="100%" height="100%"
          dataProvider="{_data}">
      <mx:columns>
        <mx:DataGridColumn headerText="ID" dataField="id" />
        <mx:DataGridColumn headerText="First name" dataField="firstName" />
        <mx:DataGridColumn headerText="Last name" dataField="lastName" />
        <mx:DataGridColumn headerText="E-mail" dataField="email" />
      </mx:columns>
    </mx:DataGrid>
  </mx:Panel>
 
  <mx:Panel
      width="100%"
      title="Detail"
      fontFamily="Arial" fontSize="12">
    <mx:Form id="form"
          width="50%">
      <mx:FormItem label="ID">
        <mx:TextInput  id="inId"
            text="{datagrid.selectedItem.id}" 
            enabled="false"/>
      </mx:FormItem>
 
      <mx:FormItem label="First name">
        <mx:TextInput id="inFirstName"
            text="{datagrid.selectedItem.firstName}" />
      </mx:FormItem>
 
      <mx:FormItem label="Last name">
        <mx:TextInput id="inLastName"
            text="{datagrid.selectedItem.lastName}" />
      </mx:FormItem>
 
      <mx:FormItem label="E-mail">
        <mx:TextInput id="inEmail"
            text="{datagrid.selectedItem.email}" />
      </mx:FormItem>
    </mx:Form>
  </mx:Panel>  
 
  <mx:HBox width="100%">
    <mx:Button label="Save as new" click="doSaveNew();" />
    <mx:Button label="Update" click="doUpdate();" enabled="{datagrid.selectedItem != null}" />
    <mx:Button label="Delete" click="doDelete();" enabled="{datagrid.selectedItem != null}" />
    <mx:Spacer width="100%" />
    <mx:Button label="Read all" click="doReadAll();" />
  </mx:HBox>
</mx:Application>

The application loads a configuration file on startup at line 25. The configuration file contains the fxDao entry point URL and the hash key used to calculate a checksum string which is passed to the gateway on each request. The checksum is used to build up a very basic level of security. Security in fxDao is certainly something we have to focus better on in the next future.

Once the application initialization is completed, a first call to the DAO class populates the DataGrid at line35. The ContactsDao.readAll method expects a callback Function as parameter. That function will be invoked once the result will be available: after all we are playing with asynchronous service calls.

The callback function at line 36 takes an ArrayCollection as parameter which means that the DAO class will serialize and deserialize data without the need for the main application to know anything about the data structures exchanged underneath. This is the reason why the main application can be considered loosely coupled with fxDao. The Value Object used in this example is Contact and its very simple structure is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package model
{
[Bindable]
public class Contact
{
  // -------------------------------------------------------------------------
  //
  // Properties
  //
  // -------------------------------------------------------------------------
 
  public var id:Number = -1;
  public var firstName:String = "";
  public var lastName:String = "";
  public var email:String = "";
 
  // -------------------------------------------------------------------------
  //
  // Constructor
  //
  // -------------------------------------------------------------------------
 
  /**
   * Constructor
   * 
   * Assumes an XML structure in the form:
   * 
   * <data>
   *   <c1>123</c1>
   *   <c2>first name</c2>
   *   <c3>last name</c3>
   *   <c4>email</c4>
   * </data>
   * 
   */
  public function Contact(data:XML = null)
  {
    if (data != null)
    {
      id = Number(data.c1);
      firstName = data.c2;
      lastName = data.c3;
      email = data.c4;
    }
  }
 
}
}

All other create, delete and update methods work more or less the same way. As you can see, this example application is very simple and let you remotely read, insert, update and delete records.

The ContactsDao class encapsulates the SQL queries used to read and write records into static methods invoked by the main application. Queries are executed through the fxDao gateway which means that an HTTP service is used to send commands to the server. This is why method calls are asynchronous.

The ContactsDao class source code is listed below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package dao
{
  import model.Contact;
 
  import mx.collections.ArrayCollection;
  import mx.managers.CursorManager;
  import mx.rpc.AsyncToken;
  import mx.rpc.events.ResultEvent;
 
  import utils.FxDaoUtils;
 
public class ContactsDao
{
  // -------------------------------------------------------------------------
  //
  // CRUD methods
  //
  // -------------------------------------------------------------------------
 
  // -----------------------------------
  // Create
  // -----------------------------------  
 
  public static function create(obj:Contact, 
                                userHandler:Function):void
  {
    var query:String = "INSERT INTO contacts SET "
      + "first_name='" + dquote(obj.firstName) + "', "
        + "last_name='" + dquote(obj.lastName) + "', "
        + "email='" + dquote(obj.email) + "'";
    doExecute(query, onCreate, userHandler);
  }
 
  private static function onCreate(event:ResultEvent,
                                   userHandler:Function):void
  {
    var id:Number = Number(event.result.rs.@insert_id);
    userHandler(id);
  }
 
  // -----------------------------------
  // Read
  // -----------------------------------
 
  public static function readAll(userHandler:Function):void
  {
    var query:String = "SELECT * FROM contacts";
    doExecute(query, onReadAll, userHandler);
  }
 
  private static function onReadAll(event:ResultEvent, 
                                    userHandler:Function):void
  {
    var list:ArrayCollection = new ArrayCollection();
    for each (var row:XML in event.result.rs.r) {
      list.addItem(new Contact(row));
    }
    if (userHandler != null) { 
      userHandler(list);
    }
  }
 
  // -----------------------------------
  // Update
  // -----------------------------------
 
  public static function update(obj:Contact,
                                userHandler:Function):void
  {
    var query:String = "UPDATE contacts SET "
      + "first_name='" + dquote(obj.firstName) + "', "
        + "last_name='" + dquote(obj.lastName) + "', "
        + "email='" + dquote(obj.email) + "' "
        + "WHERE id=" + obj.id;
    doExecute(query, onUpdate, userHandler);
  }
 
  private static function onUpdate(event:ResultEvent,
                            userHandler:Function):void
  {
    if (userHandler != null) {
      userHandler();
    }
  }
 
  // -----------------------------------
  // Delete
  // -----------------------------------  
 
  public static function deleteById(id:Number,
                                userHandler:Function):void
  {
    var query:String = "DELETE FROM contacts WHERE id=" + id;
    doExecute(query, onDeleteById, userHandler);
  }
 
  private static function onDeleteById(event:ResultEvent,
                                       userHandler:Function):void
  {
    if (userHandler != null) {
      userHandler();
    }
  }
 
  // -------------------------------------------------------------------------
  //
  // Private methods
  //
  // -------------------------------------------------------------------------
 
  /**
   * Execute given query.
   */
  private static function doExecute(query:String, 
                                    resultHandler:Function, 
                                    userHandler:Function = null):void
  {
    CursorManager.setBusyCursor();
    var params:Object = new Object();
    params.query = query;
    params.checksum = FxDaoUtils.calculateChecksum(query);
    var token:AsyncToken = FxDaoUtils.service.send(params);
    token.myData = {resultHandler: resultHandler, userHandler:userHandler};
  }
 
  /**
   * Double quotes in given string.
   */
  private static function dquote(value:String):String
  {
    return value.replace("'", "''");
  }
 
}
}

You can download the full source code of the example on the fxDao Google Code project home.

If you like fxDao and want to contribute in any way feel free to submit your request! Any kind of help will be appreciated.

References

How to implement bidirectional binding in Flex

// March 12th, 2008 // No Comments » // Tips & Tricks

In this post I’ll show you how to implement a bidirectional binding between a DataGrid and a Form in such a way that any change in the Form will automatically affect data displayed in the DataGrid.

To obtain such a bidirectional binding we have to:

  • define a model object
  • define a binding between Form fields and object properties
  • define a binding between DataGrid and the model object

Here is the simplified code to realize all this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
  xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:wg="com.webgriffe.samples.*">
  <mx:ArrayCollection id="employees">
    <wg:Person>
      <wg:name>Christina Coenraets</wg:name>
      <wg:phone>555-219-2270</wg:phone>
      <wg:email>ccoenraets@fictitious.com</wg:email>
    </wg:Person>
    <wg:Person>
      <wg:name>Joanne Wall</wg:name>
      <wg:phone>555-219-2012</wg:phone>
      <wg:email>jwall@fictitious.com</wg:email>
    </wg:Person>
   <wg:Person>
     <wg:name>Maurice Smith</wg:name>
     <wg:phone>555-219-2012</wg:phone>
     <wg:email>maurice@fictitious.com</wg:email>
   </wg:Person>
  </mx:ArrayCollection>  <wg:Person id="per"
    name="{inName.text}"
    phone="{inPhone.text}"
    email="{inEmail.text}" />
    <mx:Panel title="DataGrid e Form - binding bidirezionale">
    <mx:Label text="Select a row in the DataGrid control."/>
    <mx:DataGrid id="dg" dataProvider="{employees}"
      itemClick="per=Person(event.currentTarget.selectedItem);">
      <mx:columns>
        <mx:DataGridColumn
            dataField="name"
            headerText="Name"/>
        <mx:DataGridColumn
            dataField="phone"
            headerText="Phone"/>
        <mx:DataGridColumn
            dataField="email"
            headerText="Email"/>
      </mx:columns>
    </mx:DataGrid>
    <mx:Form>
      <mx:FormItem label="Name">
        <mx:TextInput id="inName" text="{per.name}"/>
      </mx:FormItem>
      <mx:FormItem label="Phone">
        <mx:TextInput id="inPhone" text="{per.phone}"/>
      </mx:FormItem>
      <mx:FormItem label="Email">
        <mx:TextInput id="inEmail" text="{per.email}"/>
      </mx:FormItem>
    </mx:Form>
  </mx:Panel>
</mx:Application>