Tuesday, October 4, 2011

Creating a contact book using Flex, AIR and SQLite database: Part 10

In this tutorial we will start working on the Export and Import features.

We are going to add 3 features to our contact book application - Export, Import and Backup.

Why are they needed?

Imagine that you have this application installed on another computer or a mobile device, and you want to transfer all your contacts from one database to another. This can be done by exporting the data and saving it into a file, then, from the other device, import this file and load all the contacts.

The Backup feature is similar, yet different. Sometimes, you might want to create backup files to make sure you can undo any changes you've made to restore the database to whatever it was at the time when you saved it. With the Backup function, you can just press a save button and the file gets exported into a specific directory (without asking the user to set the directory themselves). The backups can be loaded from the application using a Load backup function.

So, in total we have 3 buttons to add to your application - Export, Import and Backup. In this tutorial we will create the first feature, Export.

The files that we are going to save and read will be XML files. The first function we will create is called exportXML(), and it islinked to the Export button. Here we create the buttons and call this function:

<mx:FormItem label="Data transfer">
<s:Button label="Export contacts" click="exportXML();" />
<s:Button label="Import contacts"/>
<s:Button label="Backup"/>
</mx:FormItem>

The exportXML() function will be used to retreive all the contacts from the database, and send this info to a handler function, which is called doExport:

private function exportXML():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, fname, lname, phone, email FROM contacts ORDER BY id";
stat.execute( -1, new Responder(doExport));
b_save.enabled = false;
b_delete.enabled = false;
}

In the doExport() function, we retreive the data and use it to generate an XML string. Just create a String variable, and populate it this way:

private function doExport(evt:SQLResult):void {
var xmlStr:String = "";
xmlStr += "<?xml version='1.0' encoding='UTF-8'?>\n";
xmlStr += "<contacts>\n"
for (var i:int = 0; i < evt.data.length; i++) {
xmlStr += "<contact fname='" + evt.data[i].fname + "' lname='" + evt.data[i].lname + "' phone='" + evt.data[i].phone + "' email='" + evt.data[i].email + "'/>\n"
}
xmlStr += "</contacts>\n"

}

Now we need to create a new File object, call a browseForSave() method to open a dialog window, which the user will use to select a directory to save the file to, and then we add an event listener for an Event.SELECT event, which calls a fileSave() function.

var myFile:File = new File();
myFile.browseForSave("Save XML file:");
myFile.addEventListener(Event.SELECT, fileSave);

The fileSave() function can be written inside this doExport function to use all the internal variables. Here, we will add ".xml" to the nativePath of the FIle object, so that it gets saved as an XML file. Then we create a stream, open it in writing mode, write the data using the writeUTFBytes() method and close it.

Here's what the whole doExport function looks like:

private function doExport(evt:SQLResult):void {
var xmlStr:String = "";
xmlStr += "<?xml version='1.0' encoding='UTF-8'?>\n";
xmlStr += "<contacts>\n"
for (var i:int = 0; i < evt.data.length; i++) {
xmlStr += "<contact fname='" + evt.data[i].fname + "' lname='" + evt.data[i].lname + "' phone='" + evt.data[i].phone + "' email='" + evt.data[i].email + "'/>\n"
}
xmlStr += "</contacts>\n"

var myFile:File = new File();
myFile.browseForSave("Save XML file:");
myFile.addEventListener(Event.SELECT, fileSave);

function fileSave(evt:Event):void {
myFile.nativePath+=".xml"
var stream:FileStream = new FileStream();
stream.open(myFile, FileMode.WRITE);
stream.writeUTFBytes(xmlStr);
stream.close();
}
}

And the full code:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx" 
   width="850" height="560"
   creationComplete="init();"
   title="Flex contact book"
   >
   
<fx:Script>
<![CDATA[
import flash.data.SQLConnection;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.data.SQLMode;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Rectangle;
import flash.net.Responder;
import flash.data.SQLStatement;
import flash.printing.PrintJob;
import mx.collections.ArrayCollection;
import mx.collections.Sort;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import flash.data.SQLResult;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.printing.FlexPrintJob;
import mx.printing.PrintDataGrid;
import mx.validators.Validator;
import mx.core.FlexGlobals;
import flash.filesystem.FileMode;

private var connection:SQLConnection;
[Bindable]
private var listData:ArrayCollection;

private function init():void {

var dbFile:File = File.applicationStorageDirectory.resolvePath("database.db");
connection = new SQLConnection();
connection.addEventListener(SQLEvent.OPEN, onOpen);
connection.openAsync(dbFile, SQLMode.CREATE);
}

private function onOpen(evt:SQLEvent):void{
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, fname TEXT, lname TEXT, phone TEXT, email TEXT)";
stat.execute(-1, new Responder(selectItems));
}

private function selectItems(evt:SQLResult):void{
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, fname, lname, phone, email FROM contacts ORDER BY id";
stat.execute( -1, new Responder(onSelected));
b_save.enabled = false;
b_delete.enabled = false;
}

private function onSelected(evt:SQLResult):void {
var tempSort:Sort;
if (listData!=null) {
tempSort = listData.sort;
}
listData = new ArrayCollection(evt.data);
listData.sort = tempSort;
listData.refresh();
}

private function createNew():void {
if(fieldsCheck()){
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO contacts (fname, lname, phone, email) VALUES (@fname, @lname, @phone, @email)";
stat.parameters["@fname"] = t_fname.text;
stat.parameters["@lname"] = t_lname.text;
stat.parameters["@phone"] = t_phone.text;
stat.parameters["@email"] = t_email.text;
stat.execute( -1, new Responder(selectItems));
}
}

private function onChange():void {
b_save.enabled = true;
b_delete.enabled = true;
t_fname.text = contactList.selectedItems[0].fname;
t_lname.text = contactList.selectedItems[0].lname;
t_phone.text = contactList.selectedItems[0].phone;
t_email.text = contactList.selectedItems[0].email;
if (contactList.selectedItems.length > 1) {
b_save.enabled = false;
}
}

private function onDelete():void {
for (var i:int = 0; i < contactList.selectedItems.length; i++){
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM contacts WHERE id=" + contactList.selectedItems[i].id;
stat.execute( -1, new Responder(selectItems));
}
t_fname.text = t_lname.text = t_phone.text = t_email.text = "";
}

private function onSave():void {
if (fieldsCheck()) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "UPDATE contacts SET fname=@fname, lname=@lname, phone=@phone, email=@email WHERE id=" + contactList.selectedItems[0].id;
stat.parameters["@fname"] = t_fname.text;
stat.parameters["@lname"] = t_lname.text;
stat.parameters["@phone"] = t_phone.text;
stat.parameters["@email"] = t_email.text;
stat.execute( -1, new Responder(selectItems));
t_fname.text = t_lname.text = t_phone.text = t_email.text = "";
}
}

private function clearFields():void {
t_fname.text = t_lname.text = t_phone.text = t_email.text = "";
}

private function fieldsCheck():Boolean {
var validatorErrors:Array = Validator.validateAll(nameValidators);
if (validatorErrors.length > 0) {
Alert.show("You have to fill in first name, last name and at least 1 other contact field!", "Oops!");
return false;
}else if (valid_phone.validate(t_phone.text).type != "valid" && valid_email.validate(t_email.text).type != "valid") {
Alert.show("You have to fill in at least 1 contact field, other name first name and last name! (You need to have at least 3 fields filled in, including first name and last name.)", "Oops!");
return false;
}else{
return true;
}
}

private function doSearch():void{
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, fname, lname, phone, email FROM contacts WHERE fname LIKE @search OR lname LIKE @search OR phone LIKE @search OR email LIKE @search ORDER BY id;";
stat.parameters["@search"] = "%" + t_search.text + "%";
stat.execute( -1, new Responder(onSelected));
b_save.enabled = false;
b_delete.enabled = false;
}

private function showAll():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, fname, lname, phone, email FROM contacts ORDER BY id";
stat.execute( -1, new Responder(onSelected));
b_save.enabled = false;
b_delete.enabled = false;
}

private function printData(dataP:Object):void{
var pJob:FlexPrintJob = new FlexPrintJob();
var pGrid:PrintDataGrid = new PrintDataGrid();

var col1:DataGridColumn = new DataGridColumn();
col1.dataField = "fname";
col1.headerText = "First name";
var col2:DataGridColumn = new DataGridColumn();
col2.dataField = "lname";
col2.headerText = "Last name";
var col3:DataGridColumn = new DataGridColumn();
col3.dataField = "phone";
col3.headerText = "Phone";
var col4:DataGridColumn = new DataGridColumn();
col4.dataField = "email";
col4.headerText = "Email";

if (pJob.start()) {
addElement(pGrid);

pGrid.width = pJob.pageWidth;
pGrid.height = pJob.pageHeight;

pGrid.dataProvider = dataP;
pGrid.columns = [col1, col2, col3, col4];

pJob.addObject(pGrid);

pJob.send();

removeElement(pGrid);
}
}

private function exportXML():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, fname, lname, phone, email FROM contacts ORDER BY id";
stat.execute( -1, new Responder(doExport));
b_save.enabled = false;
b_delete.enabled = false;
}

private function doExport(evt:SQLResult):void {
var xmlStr:String = "";
xmlStr += "<?xml version='1.0' encoding='UTF-8'?>\n";
xmlStr += "<contacts>\n"
for (var i:int = 0; i < evt.data.length; i++) {
xmlStr += "<contact fname='" + evt.data[i].fname + "' lname='" + evt.data[i].lname + "' phone='" + evt.data[i].phone + "' email='" + evt.data[i].email + "'/>\n"
}
xmlStr += "</contacts>\n"

var myFile:File = new File();
myFile.browseForSave("Save XML file:");
myFile.addEventListener(Event.SELECT, fileSave);

function fileSave(evt:Event):void {
myFile.nativePath+=".xml"
var stream:FileStream = new FileStream();
stream.open(myFile, FileMode.WRITE);
stream.writeUTFBytes(xmlStr);
stream.close();
}
}

]]>
</fx:Script>

<fx:Declarations>
<fx:Array id="nameValidators">
<mx:StringValidator source="{t_fname}" property="text" required="true" />
<mx:StringValidator source="{t_lname}" property="text" required="true" />
</fx:Array>
<mx:PhoneNumberValidator id="valid_phone" source="{t_phone}" property="text" required="true" />
<mx:EmailValidator id="valid_email" source="{t_email}" property="text" required="true" />
</fx:Declarations>
<s:HGroup width="500">
<mx:AdvancedDataGrid id="contactList" width="570" height="400" dataProvider="{listData}" change="onChange();" allowMultipleSelection="true">
<mx:columns>
<mx:AdvancedDataGridColumn dataField="fname" headerText="First name"/>
<mx:AdvancedDataGridColumn dataField="lname" headerText="Last name"/>
<mx:AdvancedDataGridColumn dataField="phone" headerText="Phone"/>
<mx:AdvancedDataGridColumn dataField="email" headerText="Email"/>
</mx:columns>
</mx:AdvancedDataGrid>
<s:VGroup horizontalAlign="center">
<mx:Form>
<mx:FormItem label="First name" required="true"><s:TextInput id="t_fname"/></mx:FormItem>
<mx:FormItem label="Last name" required="true"><s:TextInput id="t_lname"/></mx:FormItem>
<mx:FormItem label="Phone"><s:TextInput id="t_phone" restrict="0-9 " /></mx:FormItem>
<mx:FormItem label="Email"><s:TextInput id="t_email"/></mx:FormItem>
<mx:FormItem>
<s:Button id="b_new" label="Create new item" click="createNew();" />
<s:Button id="b_clear" label="Clear fields" click="clearFields();" />
<s:Button id="b_save" label="Save changes" enabled="false" click="onSave();" />
<s:Button id="b_delete" label="Delete selected" enabled="false" click="onDelete();" />
</mx:FormItem>
<mx:FormItem/>
<mx:FormItem label="Search"><s:TextInput id="t_search"/></mx:FormItem>
<mx:FormItem>
<s:HGroup>
<s:Button id="b_search" label="Search" click="doSearch();" />
<s:Button id="b_showall" label="Show all" click="showAll();" />
</s:HGroup>
</mx:FormItem>
<mx:FormItem/>
<mx:FormItem label="Printing">
<s:Button label="Print selected" enabled="{(contactList.selectedItems.length>0)?true:false}" click="printData(contactList.selectedItems);" />
<s:Button label="Print all" click="printData(contactList.dataProvider);" />
</mx:FormItem>
<mx:FormItem/>
<mx:FormItem label="Data transfer">
<s:Button label="Export contacts" click="exportXML();" />
<s:Button label="Import contacts"/>
<s:Button label="Backup"/>
</mx:FormItem>
</mx:Form>
</s:VGroup>
</s:HGroup>

</s:WindowedApplication>

Here are the possible results that you can get in the exported XML file:

<?xml version='1.0' encoding='UTF-8'?>
<contacts>
<contact fname='Kirill' lname='Poletaev' phone='555 5555555' email='kirill@bossmail.com'/>
<contact fname='John' lname='Johnson' phone='' email='john@jmail.com'/>
<contact fname='Todd' lname='Jacobson' phone='' email='todd@somemail.com'/>
</contacts>

Next time we will add a feature to read these files.

Thanks for reading!

Related:

Creating a contact book using Flex, AIR and SQLite database: Part 1
Creating a contact book using Flex, AIR and SQLite database: Part 2
Creating a contact book using Flex, AIR and SQLite database: Part 3
Creating a contact book using Flex, AIR and SQLite database: Part 4
Creating a contact book using Flex, AIR and SQLite database: Part 5
Creating a contact book using Flex, AIR and SQLite database: Part 6
Creating a contact book using Flex, AIR and SQLite database: Part 7
Creating a contact book using Flex, AIR and SQLite database: Part 8
Creating a contact book using Flex, AIR and SQLite database: Part 9
Creating a contact book using Flex, AIR and SQLite database: Part 11
Creating a contact book using Flex, AIR and SQLite database: Part 12
Creating a contact book using Flex, AIR and SQLite database: Part 13
Creating a contact book using Flex, AIR and SQLite database: Part 14
Creating a contact book using Flex, AIR and SQLite database: Part 15
Creating a contact book using Flex, AIR and SQLite database: Part 16

No comments:

Post a Comment