Showing posts with label Flex. Show all posts
Showing posts with label Flex. Show all posts

Sunday 8 November 2009

Bit of a problem with Firefox, Safari and FLEX uploads to a Domino Server

After much head scratching over an issue I had with uploads FLEX and Firefox, namely authenticated uploads work in IE but do not in Firefox i have discovered the cause. As described by Jake Howlett over on his excellent CodeStore site FLEX uses a class called the FileReference to get and then upload the file or files the user selects.

The problem comes in that for some reason Firefox does not sent the DomAuthSessId cookie over in the header.. for example here is a header receieved from FLEX from an authenticated session using the FLEX HTTPRequest object.

*** New Request -- Parse and Check Request: Session 7, Thread 1ba4, Clock 1602816
GET /econtent/AC_OETags.js HTTP/1.1
Host: www.atuin.com:81
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.15) Gecko/2009101601 Firefox/3.0.15 (.NET CLR 3.5.30729)
Accept: */*
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://www.atuin.com:81/econtent/econtent.html
Cookie: DomAuthSessId=47F0EA4947495C8C4C6E9FAF6665CD73
If-Modified-Since: Thu, 05 Nov 2009 00:07:20 GMT

and here is the next header received by the server from FLEX again running on Firefox in the same session using the FileReference.upload() method

*** New Request -- Parse and Check Request: Session 8, Thread 7c4, Clock 1617184
POST /econtent.nsf/Gallery?createDocument HTTP/1.1
Accept: text/*
Content-Type: multipart/form-data; boundary=----------GI3ae0Ij5gL6ei4KM7GI3ae0Ef1ei4
User-Agent: Shockwave Flash
Host: www.atuin.com:81
Content-Length: 8644
Connection: Keep-Alive
Cache-Control: no-cache
As you can see no DomAuthSessId.. and what happens well the Domino Server does what it is meant to do, it sends back the Logon screen to the Flex program. In case you are wondering this data was obtained from my server using the following Server Console commands prior to test running the FLEX
tell http debug thread on | off (default level)
tell http debug postdata on | off (for client POST data)
tell http debug responsedata on | off (for server response data)
Now this is a major FUBAR and it is odd to be complaining about FF and not IE but there you go. It seems FF starts an entirely new browser instance to do the FileReference.upload() hence the no DomAuthSessId being passed.

However all is not lost you think, the FileReference takes a URLRequest object and it has the ability to be coded to load the Header with values should you need them. Well yes it does and you can set any number of values in the URLRequest object BUT and it is a bigh BUT Flex ignores them, so there is no way out that way!.

Nothing I have tried to date allows me to use the pre-authentication of the browser instance my flex is running in to send a file to the server via the FileReference object. FECK! and yet it works perfectly in IE8 (a fact which is particularly galling) it is FireFox (and from my testing Safari) that are the problems on this occasion and it holds true for Flex 3 and Flex 4 and from my browsing of the Adobe site not likely to be acknowledged as a problem let alone fixed any time soon.

So far the only work around I can come up with is to spawn out to a standard browser child window upload the file there using a Domino File Upload control and then use an external JS function to talk to the parent flex program when the file sucessfully uploads.. which is a bloody pain in the arse!

The whole situation is not helped by the Domino server not liking a standard POST method of getting data from the client to the server..

Anyway there you go .. back to the head scratching

Friday 30 October 2009

eContent - The start of a new "home" project

have been ruminating about a home project (rather than work) that will exercise my Domino and Flex muscles [ Flex my Flex if you will ;) ] and I am sort of settled on a content managment system called eContent. I have got all the ideas in my head at the minute and they are currently being Mind-Mapped into some form of sensible order.

I have got the front page and logon up and running and I may be asking for volunteers to road test it in the next couple of weeks. It will [when and if it is finished ] be released open-source, so if anyone is interested in idiot testing it when I get something to test, please drop me an email.

This is the front screen [click for a bigger image] and I shall be blogging the life cycle of the project as I go for the moment there isn't a lot to say other than .. Happy Birthday "eContent"


Friday 23 October 2009

FLEX - Logging onto a Domino server from inside a FLEX app

Now don't queue up to shout at me.. this is a bit of a kludge and it works, however it does rely on some facilities of domino that may or may not be continued to be supported.

When you have Session Authentication enabled on your server and you log onto the server a cookie is dropped called DomAuthSessId and this cookie is passed back and forth to the server with every HTML page that is served.

Luckly in FLEX when you  log on to the server and use the HTTPService or WEBService objects this cookie is passed back to the server on each POST or GET that you call.. But how do you get this cookie there in the first place? Well the easiest way is to lock down your NSF so that Anonymous has no access and when accessed from the web the default server logon screen is displayed. Now far be it from me to say that it looks like shite but lets be fair is is rather 1990's in appearance. It would be much nicer if you could log on from the nice shiney RIA that Flex provides.

This is how I did it.

I need a HTTPService object to do the logon
<mx:HTTPService id        ="myLogon"
                    url      ="http://www.mynotesserver.com/names.nsf?logon"
                    method   ="POST"                
                    fault    ="logonFail()"    
                    result   ="logonResult(event)">    
                    <mx:request xmlns="">
                            <username>{username.text}</username>
                            <password>{password.text}</password>
                            <redirectto>http://www.mynotesserver.com/logonok?openagent</redirectto>
                    </mx:request>
</mx:HTTPService>
OK what's happening above
The HTTP Server i have defined will goto the URL specified in the URL setting which as you can see is the familiar names.nsf?logon URL and it will be a POST transaction rather than a GET
If the call successfully returns well formated XML actionscript function logonresult() will fire. If the connection fails OR poorly formatted XML is returned (as is the case if the call returns an HTML error page) the actionscript function logonFail() is called.
At the bottom i have defined 3 Paramaters that will be passed username, password and redirectto. Username is bound to a text input field called username in the flex application likewise password is bound to a similar field. Redirectto on the other hand points at logonok?openagent and this agent will be called if and only if the logon is processed by the server and the user is authenticated.

The logonFail() function looks like this

private function logonFail():void
{            
                Alert.show("Sorry - Your Logon Attempt failed")
}
The logonResult() function looks like this
private function logonResult(event:ResultEvent):void
            {                    
                 Alert.show("Hello there You are In!");        
            }
And the LS agent LogOnOk looks like this
Sub Initialize
    Print |Content-Type:text/xml|        
    Print |<?xml version="1.0" encoding="ISO-8859-1"?>|    
    Print | <loggedon>yes</loggedon> |    
End Sub

As you can see the LS simply returns some well formed if relatively meaningless XML should the signon fail for any reason some nasty HTML will be returned which will cause Flex to fire the logonFail() function.

I then place the USERNAME and PASSWORD field somewhere in the FLEX app with a LOGON button which calls actionscript myLogon.send() (myLogon being the ID of the HTTPService) and .send() initiates the call to the server.

Now although FLEX does not refresh the page the SWF is embedded in every time the server is contacted it does accept the cookies that are sent back from the server and placed them as normal on the page. So when you do something like the above in your own Flex app, and logon is sucessful when you go and look you will see the DomAuthSessId cookie appear, this then gets passed on each successive HTTPService call to your server (for as long as the cookie survives)

FLEX - What to do when your returned data has only one record

In Flex the easiest way to get data back from the server (any server) is to provide it with nicely formatted XML which Flex can cope with very easily and pop it into an ArrayCollection object. However what happens when your data set has only one record?

Yes Flex falls over because it cannot force the returned single item data type into an ArrayCollection object for example you may have this actionscript assuming you have some XML coming down that is called myData with rows of myArticles that have fields called myType and myKey in it
private var myData:ArrayCollectionmy;
Data = event.result.myData.myArticle;
If only one or no records are returned then flex throws an error and you have to code your way around it like this
private var myData:ArrayCollectionmy;
try
{
     myData = event.result.myData.myArticle;
} catch(error:Error) {
     var rcd:Object= new Object();
     rcd.myType = event.result.myData.myArticle.myType;
     rcd.myKey = event.result.myData.myArticle.myKey;
     myData = new ArrayCollection()
     myData.addItem(rcd)
}

What is this code doing?
Well the bit in the TRY{} tries to stuff the returned data into the Defined but uninstansiated ArrayCollection object called myData, if it succeeds (ie there was more than one record) everything is fine and the code will continue. If however there is only one record the CATCH{} code is run. I am not particularly interested in the error that is produced but it must be defined in the catch(error:Error) structure.

First I have to instansiate the ArrayCollection object with a new statement otherwise the object is null and you will get another error.

Then I create a new vanilla flex object called rcd

This object is populated with properties with names that match exactly the properties of the XML node

Once all the properties are set I add it into the ArrayCollection object and everything is fine

*** Note *** this code does not pick up on ZERO records.. this is because I NEVER return an empty data set. If the data set is empty i will construct a row that indicated to the user that no records where available. I suggest you do the same.

Thursday 22 October 2009

FLEX - Getting the URL when you are in a flex App

There are times when you want to get the URL either in it's entirity or just bits of it inside Flex. For example when you want to send a link to a document so that flex knows to go to that particular document and open it when the Flex app is initiated.

This is relatively simiple all you need to do is instansiate and access the BrowserManager object, from this object you can get all sorts of useful information about the URL that holds the FLEX object.

You will need to import the following classes.
import mx.utils.URLUtil;
import mx.managers.IBrowserManager;
import mx.managers.BrowserManager;
and the declare vars for the bits of the URL you want to get at
private var serverName:String;
private var portName:int;
private var protocolName:String;
Then in your application.CreationComplete event function lets call it initApp() you need this
private function initApp():void
(
browserManager = BrowserManager.getInstance();
browserManager.init("", "Browser Window Title");
var url:String = browserManager.url;
serverName = mx.utils.URLUtil.getServerName(url);
portName = mx.utils.URLUtil.getPort(url);
protocolName = mx.utils.URLUtil.getProtocol(url);
}

You then have your current server url all nicely in vars and you do not need to hard code your URLs in the rest of the code. (you will need to make the vars [Bindable] if you want to use them in other objects!)

There are loads of other methods associated with the BrowserManager Class which you can find here.

Friday 17 July 2009

Flex,Domino and Web Services 101

Okay Dokey, the last 2 Flex posts used JSON from a Web Agent, which is fine if that is your cup of tea, but Domino comes complete with easy to use and deploy WebServices and it would be a shame not to use them.

I will take it as read you know all about web services but for those that don't a Web Service is a way of letting applications written in languages like FLEX's MXML or Java interact with your data with the minimum of fuss and bother. You define the webservice and it gets published by domino as a WSDL which is a formal XML data stream. Your application then knows what methods and data your webservice offers for getting data, posting data etc and you app can use the methods it finds in the WSDL data to interact with the back end data.

Anyway I have a simple Notes database with 1 form called STOCK with the following fields
ItemKey
Category
SubCategory
ItemName
OnHandQuantity

I have a view sorted (but not categorised) by ItemKey the view is called MyStockView

So I go to the CODE section in the DDE and open the "Web Services Providers Section"

On the Web Services Properties Dialog I do the following:

I Give the WebService a NAME of StockService
I give it a PORT TYPE CLASS of getStock
I give it a PROGRAMMING MODEL of RPC
I give it SOAP MESSAGE FORMAT of DOC/LITERAL
I include "Operational Name in SOAP Action"
The PORT TYPE NAME is getStock
The SERVICE ELEMENT NAME is getStockService
The SERVICE PORT NAME is Domino

In the "Declarations" section i put this code
Dim ThisSession As NotesSession
Dim ThisDB As NotesDatabase
Dim StockDoc As NotesDocument
Dim StockView As NotesView

Class getStock
Sub New
Set ThisSession = New NotesSession
End Sub

Public Function getStock(stockKey As String) As String

If StockDoc Is Nothing Then
result=getStockDoc(stockKey)
getStock=result
End If
End Function

End Class
This is a VERY VERY basic web service!
Web Services are defined in CLASS objects and this service contains one class called getStock, it contains 1 method that instansiated the getStock object. It is passed one parameter stockKey
which is passed to a function called getStockDoc which does the actual work of geting and returning data.

I then have to create the getStockDoc() function in the Web Service object .. like this
Private Function GetStockDoc(stockKey As String) As String
Set ThisDB = ThisSession.CurrentDatabase
If Not (ThisDB.IsOpen) Then
GetStockDoc= "Error opening database"
Exit Function
End If
'Check that view exists in the database
Set StockView = ThisDB.GetView("StockByKey")
If StockView Is Nothing Then
GetStockDoc = "Error in search"
Exit Function
End If
'Get a document by provided search key
Set StockDoc = StockView.GetDocumentByKey(stockKey, True)
If StockDoc Is Nothing Then
GetStockDoc = "Cannot find Stock"
Exit Function
End If
GetStockDoc= StockDoc.ItemName(0)
End Function
This does what it says on the tin.. it checkes the DB exists, the view exists and then goes and gets the document found using the stock key. the function returns either an error message if something goes wrong or the Item Name if it succeeds.

Having done all this I try out my webservice by going to this URL

http://www.unseenuni.com/mystock.nsf/StockService?wsdl

And if every thing goes according to plan i should get some nicely formatted XML. The content of the XML is very interesting if you are a real geek, but the domino server does all the interesting shit so as long as you get lots of XML your web service is working and you can they create applications that can "consume" it.

OH very important this.. DOMINO will take all the important bits of your server and make them UPPER CASE!! so getStock will become GETSTOCK

OK I fire up the other side of the coin now and create my MXML file called WS1.mxml in this case and i open the application with the normal Application tags
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
</mx:application>
inside these tags I create my webservice definition
<mx:WebService
id='myservice'
wsdl="http://www.unseenuni.com/mystock.nsf/stockservice?wsdl">
</mx:WebService>
You will not that i have given it an ID so i can refer to it in my MX code later on
and I define where the web service object can get the WSDL XML from the server. This will be loaded from the server automatically when the application is loaded at run time.

Inside the mx:WebService tag i create the following code
<mx:operation name='GETSTOCK' result='myresult(event)'>
<mx:request>
<STOCKKEY>
{mykey.text}
</STOCKKEY>
</mx:request>
</mx:operation>


Although the WSDL defines the method GETSTOCK in the webservice I have to define an MX object to interact with it. So I create an mx:operation which has the same name as the WSDL (note the upper case !!) and it also defines an mx function myresult(event) which will be fired when the webservice operation returns data.

The STOCKEY tag defines the data that will be passed to the method again note the UPPERCASE! In this case I have defined this as the bindable value of a Text input box which I will define in a moment... but first i will define the script for the result handler myresult();

<mx:Script>
<![CDATA[
import mx.controls.Text;
import mx.controls.Alert;
import mx.rpc.events.ResultEvent
[Bindable]
private var msg:String
private function myresult(event:ResultEvent):void
{
msg = (event.result as String)
mx.controls.Alert.show(msg)
}
]]>
</mx:Script>


As you can see when the myresult() function is triggered it will display the content s of the Event result as text in an alert box.. not very exciting i know but I want to keep the code down to a minimum.

lastly I create a text input box and button.

<mx:TextInput x="25" y="10" id="mykey"/>
<mx:Button x="25" y="40" label="Button" click='myservice.GETSTOCK.send()'/>
The text input box is called mykey and use {mykey.text} in the STOCKKEY tag of the Webservice Operation definition earlier..

The Button calls the Webservice's GETSTOCK's method by calling .send(), this then calls the webservice method passes it to the server and waits for some data to come back when the function myresult() will be triggered and processed.

So when i compile up my flex project it looks like this on the screen



and if i type in a valid Item Key and click the button .. this happens



Easy Peasy!

Now webservices are usually way way way more complex than this one, but you get the idea :-) and it only took 31 lines of MXML to define the process that consumed it. In the next post I will use Web Services to Update data on the server.

Tuesday 14 July 2009

More Flexy loveliness = Categorized with Summaries

In the last post I popped up an example of categorized views and in this we will add the additional functionality of Summaries.

To recap the last post

01. Create a GroupCollection object

02. Assign a source property of the Group Collection object to the AdvancedDataGrid's dataProvider

03. Create a New Grouping Object

04. Create a new GroupingField Object or Objects that specify the field(s) on which to group

05. Assign the Grouping property of the GroupingCollection to the Grouping Object

06. Refresh the Grouping Collection

07. Assign the GroupingCollection to the Dataprovider or the AdvancedDataGrid.

But what if you want to have some summarised .. well that is relatively easy too. Lets try for this.. With summary data at the end of each category


Ok Lets go.

01. I go to the GroupingField definitions I created yesterday
Originally it looked like this <mx:GroupingField name="cat"/>

02. I drop the / from the end and create a tag pair
<mx:GroupingField name="cat">
&lt/mx:GroupingField>

03. Inside this tag pair I create a <mx:summaries> and </mx:summaries> tag pair

04. Inside the summaries Tags i create a <mx:SummaryRow summaryPlacement="last"> tag pair.. Note the SummaryPlacement attribute. "last" will place it at the end of the category and "first" will put it and the start.

05. Inside the SummaryRow i create a <mx:fields> tag pair

06. Inside the fields tag pair i create
<mx:SummaryField dataField="qty" operation="SUM" label="summary" />
The source of the data is set in dataField which in this case is the "qty" field in the XML pulled from the domino agent.
The Operation is "SUM" (or totalise all the QTY values in the category) other options can be MIN, MAX, AVG and COUNT

07. I repeat 1-6 for the other field "subcat" that I am summarising on

08. Your code will now look like this.

<mx:Grouping>
<mx:GroupingField name="cat">
<mx:summaries>
<mx:SummaryRow summaryPlacement="last">
<mx:fields>
<mx:SummaryField
dataField="qty"
operation="SUM"
label="summary" />
</mx:fields>
</mx:SummaryRow>
</mx:summaries>
</mx:GroupingField>
<mx:GroupingField name="subcat">
<mx:summaries>
<mx:SummaryRow summaryPlacement="last">
<mx:fields>
<mx:SummaryField dataField="qty" operation="SUM" label="summary" />
</mx:fields>
</mx:SummaryRow>
</mx:summaries>
</mx:GroupingField>
</mx:Grouping>
09. Now i am going to use a flex function called a rendererProvider, which is a MXML component that is used to render a particular item in your project.

10. I create a new MXML file called SummaryText.mxml in a subdirectory called Renderers off the directory i have my main MXML file in.

11. The code looks like this and basically all it is , is a LABEL component the which will display the total that is calculated for the category. Note the {data.summary} the .summary refers back to the LABEL I used in the <mx:SummaryField> statement above

<?xml version="1.0" encoding="utf-8"?>
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml"
text="Total Quantity {data.summary}">
</mx:Label>
12. Having saved the SummaryText.mxml file I return to the AdvancedDataGrid definition in my main MXML file. Just above the closing </mx:AdvancedDataGrid> tag I create a new set of tags that attach the renderer to the AdvancedDataGrid. the code looks like this

<mx:rendererProviders>
<mx:AdvancedDataGridRendererProvider
dataField="summary"
columnIndex="1"
columnSpan="2"
renderer="Renderers.SummaryText"/>
</mx:rendererProviders>
Of note here is the the dataField attribute points at LABEL I used in the <mx:SummaryField> definition (and beware it IS case sensitive)
Also if i set the columnSpan to "0" it will span all columns in the grid. I have chosen 2 here cos it looks better in the finished application.
Also the renderer attribute is made up of [the path to the renderer MXML file].[File name without extenstion] beware this too is case sensitive.

13. Once that is done ... compile up your app and there you have it.. summary values in your view. Note this is all done by FLEX as I have NOT changed the agent that supplies the data from the Domino application.

I have Popped this into a NSF and zipped it up with the Flex Builder 3 project files if you want to have a look see.. This is the same file as yesterday.. except the summarised page is FlexView2 ad the SWF is called CategoryView2.swf.. you can get it here .. Same provisio as yesterday, This will NOT work on your server as you need to change the URL for the HTTPService object and recompile the SWF for it to work!!! Enjoy and again if you have any questions drop me an email :-)

Categorised Views in Flex - It's really easy

OK .. a Flex Post for youse in Domino land.

First some stuff about the what I used.

Flex 3 SDK
Notepad ++
Domino 8.5.0 server
Domino 8.5.0 DDE

I was asked recently how I would FLEX a multi-category expand/collapsible view which is admittedly dead easy in the Notes Client. Well it is almost as easy in Flex.

This is what I did to illustrate the method.

01. I created a form that looks like this


02. I created a view that looks like this

03. I created an agent that does this
04. I opened Notepad++ and created a file called CategoryView1.mxml and typed up these 47 lines of code
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="myData.send()">
<mx:HTTPService id="myData"
url="http://www.unseenuni.com:81/flexview.nsf/getdata?openagent"
result="dataResult(event)"/>
<mx:Script>
<![CDATA[
import mx.controls.Text;
import mx.controls.Alert;
import mx.rpc.events.ResultEvent
import mx.collections.ArrayCollection
[Bindable]
private var items:ArrayCollection = new ArrayCollection
private var msg:String
private function dataResult(event:ResultEvent):void
{
items= event.result.items.item;
myGroup.refresh(true)
}
private function dostuff(event:MouseEvent):void
{
if(event.target.data.key)
{
msg = event.target.data.key
mx.controls.Alert.show(msg)
}
}
]]>
</mx:Script>

<mx:AdvancedDataGrid x="10" y="10" id="Grid1" designViewDataType="tree" width="719" height="343" creationComplete="myGroup.refresh()">
<mx:dataProvider>
<mx:GroupingCollection id="myGroup" source="{items}">
<mx:Grouping>
<mx:GroupingField name="cat"/>
<mx:GroupingField name="subcat"/>
</mx:Grouping>
</mx:GroupingCollection>
</mx:dataProvider>
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Item" dataField="item"/>
<mx:AdvancedDataGridColumn headerText="Qty" dataField="qty"/>
<mx:AdvancedDataGridColumn headerText="Price" dataField="price"/>
</mx:columns>
</mx:AdvancedDataGrid>

</mx:Application>

05. I compiled up the mxml to a SWF using the SDK (a lot easier if you use FLEX BUILDER!)

06. I Imported the resulting file CategoryView1.swf as a File Resource in my NSF

07. I created a Page in my NSF with this Passthru HTML
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="CategoryView1" width="100%" height="100%"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="CategoryView1.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="sameDomain" />
<embed src="CategoryView1.swf" quality="high" bgcolor="#869ca7"
width="100%" height="100%" name="CategoryView1" align="middle"
play="true"
loop="false"
quality="high"
allowScriptAccess="sameDomain"
type="application/x-shockwave-flash"
pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>

08. I fired up a browser and went to the page's I just created URL and this is what appeared.



It really is that easy... So lets look at this in detail.

The Domino agent returns nice simple XML - have a look at ../flexdata.nsf/getdata?openagent and you will see it in "raw" form, althought the parent view is sorted it is not categorised it is an ordinary flat view. We will let Flex do the sorting and grouping once it gets the data.

The MXML is only 47 lines long and was created as follows :-
(replace [] with < adn >)
01. I created an application

[mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"]

[/mx:Application]


This defines the application and it is always like this, note the tag pair is closed!


02. Inside the application tags I place a definition of the HTTPService I am going to use to provide the data to the application


[mx:HTTPService id="myData"
url="http://www.unseenuni.com:81/flexview.nsf/getdata?openagent"
result="dataResult(event)"/]


This breaks down into
id this is the id by which I will refer to the service later in the code
url this is the URL which will provide the data
result this is the actionscript code that will run when data is recieved from the url

03. The next section is the Actionscript code that my flex application will use

<mx:Script>
<![CDATA[
import mx.controls.Text;
import mx.controls.Alert;
import mx.rpc.events.ResultEvent
import mx.collections.ArrayCollection
[Bindable]
private var items:ArrayCollection = new ArrayCollection
private var msg:String
private function dataResult(event:ResultEvent):void
{
items= event.result.items.item;
myGroup.refresh(true)
}
]]>
</mx:Script>


Firstly I import the various Adobe Supplied libaries that i will need
Then I create a Bindable ArrayCollection object call items, this will store the returned data.
Then I define the dataResult() function that I attached to the HTTPService object. Remember this code gets executed when the URL in the HTTPService recieves data from the URL. In this instance the items ArrayCollection is loaded with the data returned from the URL. FLEX will see it as XML and I can access it through the event.result object by name.

04. Now I define my on screen objects

<mx:AdvancedDataGrid x="10" y="10" id="Grid1" designViewDataType="tree" width="719" height="343" creationComplete="myGroup.refresh()">
<mx:dataProvider>
<mx:GroupingCollection id="myGroup" source="{items}">
<mx:Grouping>
<mx:GroupingField name="cat"/>
<mx:GroupingField name="subcat"/>
</mx:Grouping>
</mx:GroupingCollection>
</mx:dataProvider>
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Item" dataField="item"/>
<mx:AdvancedDataGridColumn headerText="Qty" dataField="qty"/>
<mx:AdvancedDataGridColumn headerText="Price" dataField="price"/>
</mx:columns>
</mx:AdvancedDataGrid>


I use the AdvancedDataGrid because it has support for Grouped (read Categorised Objects) you will not I do not ascribe a datasource to the AdvancedDataGrid Object. Instead I create a DataProvider object INSIDE the AdvancedDataGrid Tag pair! very important than!

Inside the DataProvider Object I create first a GroupingCollection object, I would do this even if I had only one category. Inside that I define a Grouping object and then as manu GroupingField objects as I need. In this instance the Fields Cat and SubCat.

I then define the Columns I want to display. Note i do NOT include the columns that will be my groups!

And that is it... compile it up, stick it in your NSF and Robert is your mother's brother.

I have uploaded the NSF and FLEX BUILDER PROJECT here.. but the NSF will not work on your server unless you change the URL in the MXML from my server to your own, recompile the SWF and delete the orginal SWF from the NSF and attach the new one as a new file resource.. i include it only so you can see the code not run it :-)

If you have any questions just drop me a line ... enjoy

Sunday 24 May 2009

A rogue pair of FLEX purple underpants have mounted an assault on the yellow-only wash of my lotus web dev laundry

I have been a bit remiss with the Flex series I started a while back and I do apologise for the discontinuity and I have started doing some more examples that I will pop up on here in the next few days.

There is a part of me that is a wee bit ... embarrassed is not the right word ... concerned is perhaps better ... that whilst the movers and shakers in the Domino world are "Doing it" the Xpage way I have veered off into another RIA technology.

I have never been a fanboy of any particular technology I am more inclined to use the tools that I have access to and which get the job done. Now I have had a bit of a play with X-Pages and they are indeed a wonderful if slightly not-quite-there-yet thing. As the new Domino techniques develop I have found myself moving towards a place where Domino is a back end data store, like DB2, MySQL or Oracle

I am not sure quite why I this is happening, but technologies & frameworks like AJAX, FLEX etc do take a fair bit of the drudgery out of coding for the web. I found that it was easier to have a slight dichotomy between the app as seen on the web and the app as seen in the notes client rather than to have to make concessions in one or the other for the sake of conformity.

Taking this approach also means I can deliver web apps for a broader range of server installs. Back to V6.* rather than confine myself to V8.* servers thus giving more of my user base a nice warm feeling of being included in the "new" stuff.

This is not to say that I am leaving all of the tools of Notes/Domino behind. I still use Domino Security, Author and Reader fields and loads of LS and JAVA based agents to provide the data handling facilities.

I suppose that another reason that I am heading in this direction is that Domino does not exist in isolation in my sphere of reference. It exists along side data silos in DB2 and Oracle which in the client we can now leverage with Live text linking to composite apps, widgets and side bar apps in the 8* client, which is of course marvellous for those folk on the Full Version 8 client. In the real world where I code not all my users are on V8 and it will (given the current economic climate etc etc) be some time until some of the non-power users have the tin capable of running the full client to its full advantage. In the interim so as not to exclude these users from the momentum of change, I will use the power of non-yellow-tech then it seems prudent to do so.

In several conversations I have had on this topic, there has been an air of disapproval from some that I am at snubbing all the work done to improve things on the platform in the last couple of years. I do not think that I am , I leverage the best for my users with the tools that I have and the facilities they have to use it, if that appears "disloyal" well so be it. I am paid to service my users not some ethereal dogma of yellow oneness. ;-)

For those of you not yet on Version 8 and perhaps held back by budgetary constraints my message would be that you can move forward if you step outside what some refer to as "the bubble" and look at other methods that will maintain your creative momentum with the tools and servers you have to hand.

Monday 13 April 2009

Flex for Thick Gits #6 - Populating a list box from a Domino Agent

OK we are going to look at a real example. I am assuming you have FLEX BUILDER installed for this.. although you can do it with just the SDK, it is a lot easier if you are in FLEX BUILDER.

01. Fire up your Notes Designer Client and create a new blank application called flexdata.nsf on your developement server.

02. Create a form called mydata and populate it like this
The ItemKey is a computed field "Computing on Composed" to @Unique

03. Save your form

04. Create a view called mydataview and have it select ONLY forms = "mydata"
sort it by ItemKey Ascending not categorised.

05. Create an agent call it getdata and give it these properties


06. In the Initialize sub write the following code

Sub Initialize

Dim ThisSession As New NotesSession
Dim ThisDb As NotesDatabase
Dim ThisView As NotesView
Dim ThisDoc As NotesDocument

Set ThisDB = ThisSession.CurrentDatabase
Set ThisView = ThisDB.GetView("mydataview")
Set ThisDoc = ThisView.GetFirstDocument
Print |Content-Type:text/xml|
Print |<?xml version="1.0" encoding="ISO-8859-1"?>|
Print |<items>|
Do While Not (ThisDoc Is Nothing)
Print |<item>|
Print |<key>|+ThisDoc.ItemKey(0)+|</key>|
Print |<name>|+ThisDoc.ItemName(0)+|</name>|
Print |<price>|+Format(ThisDoc.ItemPrice(0),"##0.00")+|</price>|
Print |</item>|
Set ThisDoc = ThisView.GetNextDocument(ThisDoc)
Loop
Print |</items>|
End Sub
All this code is doing is looping around the documents in the view and printing them out as XML
You could use the inbuilt &readviewentries function to return the XML however I want to keep the XML produced as simple as possible for the moment.

The most important thing to note are the top two Print Statements
Print |Content-Type:text/xml|
Print |<?xml version="1.0" encoding="ISO-8859-1"?>|
this ensures that the rest of what is produced is XML!

07. Change the ACL on your file to be Default = Manager so you can test access to the nsf without having to logon (you would NOT do this in real life!)

08. Open the data base in the Notes Client and create 4 or 5 items

09. Goto your browser and enter the url
http://[myserver]/[mynsf path]/flexdata.nsf/getdata?openagent
for example on my server that would be
http://www.unseenuni.com:81/flex/flexdata.nsf?getdata

10. You should see data like this

SUCCESS!!

OK you can close down your Notes Client for now and start the Flex Builder Application

11. Create a new project called DomonoData1 using File then New then Flex Project

12. Open the script TAB and paste in this
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="myData.send()">
<mx:HTTPService id="myData" url="http://www.unseenuni.com:81/flexdata.nsf/getdata?openagent" result="dataResult(event)"/>

<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent
import mx.collections.ArrayCollection
[Bindable]
private var items:ArrayCollection = new ArrayCollection
private function dataResult(event:ResultEvent):void
{
items= event.result.items.item;
}
]]>
</mx:Script>
<mx:FormItem label="item" width="272" height="48">
<mx:List id="itemlist"
rowCount ="2"
dataProvider ="{items}"
labelField="name" height="46" width="229"/>
</mx:FormItem>
</mx:Application>

Replace the URL in the HTTPService tag with the URL that points to the agent on YOUR server!!

13. Click on the RUN button in the Flex Builder Toolbar


14. Your browser should open and you will see something like this


OK lets look at the code in some more detail
A = This is the application definition. Note that the last line of the code is the closing tag for this item. All the code is enclosed in these tags, with the exception of the &lt:?xml ... ?> xml definition tag. I have also added a creationComplete function called myData.send() this function will run when the FLEX app has totally loaded and will trigger the HTTPService object myData to go and get data.

B = this is the definition of the HTTPService agent, it has an id of myData and you will note this is name that is referenced in the creationComplete function defintion of the <Application> tag. I supply it with the URL that provides the application with XML data from the server I also supply it with a function that will run when data is recieved in this instance it is a function called dataResult and i am passing it the event object that the result event creates.
C = these two imports tell the Flex compiler to bring in the extra MXML code for RPC events and ArrayCollections. This code is not automatically brought in so we have to add it in manually.

D = the [Bindable] metatext tells Flex that the var that follows will be available for binding to other data capable objects. The var declaration that follows creates a new empty ArrayCollection called items

E = This is the function I defined as the result event handler in the HTTPService object B
The HTTPService object result event object contains the XLM returned as a nicely formatted property. So I can assign this data to the ArrayCollection var I just created. It is worther of note that the event.result object holds the data in the same hierarchy as defined in the XML i created
If you remember each record was stored in an [item] and these were surrounded by an [items] tag. So the data we want is found in the event.result.items.item which is (for me anyway) a little odd.. i would have put it in the event.result.items but it isn't so BE Careful!

Lastly here are the elements on the page itself.... a FormItem and a List. The important thing here is the dataProvider="{items}" line. This tells the list item to bind to the items ArrayCollection which has been populated by the dataResult() function triggered by the creationComplete event.

30 lines of Flex code, a domino form, view and agent .. and you have data moving from your domino server down to your flex app .. :-)

I have popped the NSF and the FLEX project directory up to the web and you can download it here. Simply move the FlexData.nsf DB to your server and sign it. the Flex project.. copy into your Flex Project dir and load.. and away you go.. enjoy. :-)

Flex for Thick Gits #5 - Getting data from a remote source

OK lets get down and dirty and jump to the interesting bit.. how to get data into the app from an external source, like your domino server :-)

You do this using the HTTPService class. This inbuilt component allows your app to go to a URL and read the data that the URL returns. Usually (but not always) this will be XML. Once the data is returned you can use it in your app and that is what we will look at today.

1. The HTTPService Object

You create the object in MXML all you need to do this is the URL that will provide the data and function that will "deal" with the data. So it will look something like this
<mx:HTTPService
id="mydata" url=http://www.unseenuni.com/flexdata.nsf/getdata1?openagent
result="myResultHandler=(event)"/>
2. Getting Data

Simply creating the object doesn't actually go and get any data, you have to invoke the send() method. Now a word of caution here if you are going to use the data for a view or as list for say a combo box you app needs to be fully loaded so all the components are ready to receive the data. In MXML there is a useful even called creationComplete which is fired when your application has completely loaded and has reached a stable condition. This is the perfect time to go and get your data! This is analogous to the onLoad event in the traditional browser DOM.

3. Using the returned data

There are actually 2 ways to use the data that is returned. When the URL returns data it is stored in the lasResult property of the HTTPService object. So lets say your Domino agent returns the following data

<myXML>
<myItem>
<itemName&gtWidget 1</itemName>
<itemPrice&gt15.95</itemPrice>
</myItem>
<myItem>
<itemName&gtWidget 2</itemName>
<itemPrice&gt11.95</itemPrice>
</myItem>
<myItem>
<itemName&gtWidget 3</itemName>
<itemPrice&gt17.63</itemPrice>
</myItem>
</myXMLa>
All I need to do to access the myItem Collection is to query the HTTPService object (which if you remember had an id of mydata the code would look like this
mydata.lastResult.myXML.myItem
This can be useful when debugging but the one of most use is the event handler i set up in the HTTPService object myResultHandler(event) this event will be fired when the URL you specified has returned all the data it is going to return.

If you are familiar with LS or JS the way you code up the function is very similar it looks like this

private function myResultHandler(event:ResultEvent):void{ ...do something here... }
the returned data is available within this function as property of the event object. Using the XML returned in the example above i would access the myItem collection like this
event.result.myXML.myItem
Now all we need to do is make that data available to the other objects in your application and we do this by making a BINDING ArrayCollection. An ArrayCollection object is an extension to the flex ARRAY object.Now you can use a plain old simple ARRAY object in Flex for storing the data but if you intend to populate controls, filter or sort the data then the ARRAYCOLLECTION object is the one to go for because it comes will loads of extra functionality the ARRAY object does not posses. The most crucial of this is if the underlying data is refreshed (ie you go to the server and get a new set of data) if you use an ARRAYCOLLECTION all the bound objects that use that ARRAYCOLLECTION are automatically updated. If you use an ARRAY this does NOT happen!

This is really very very easy and in my next post I will show how to do this.

Sunday 12 April 2009

Flex for Thick Gits #4 - Creating a Class

OK I was going to talk about the Event Object in more detail in this post however, i realized that I hadn't talked about classes. Now there is neither time nor space to talk in detail about Object Oriented Programming (OOP), so if you don't know what a Class, a Property or a Method is, drop me a line and I will send you some links. ;-)

Now in Flex there are some things called Value Objects also called Data Transfer objects and they hold only data related to the object and no data about flex or business logic and you implement them as ActionScript Classes. The are called Data Transfer Objects because quite often they are used as containers for the data returned from or sent to a back end database

So how do I create a Value Object?

In Flex Builder you do it in your project select File / New / ActionSrcipt Class


You will be presented with a dialog like this



First give it a PACKAGE name, this will create a folder of the same name in your project tree. Then give the class a NAME, this create a file in the PACKAGE directory called Items.as, lastly make the class PUBLIC by clicking the Public option in the Modifiers section. There are other options I have no displayed, don't worry about them nowm just click on the FINISH button at the bottom of the dialog, When you do this you will be taken to the class editor and it will look like this.

Now I will want to use this class to hold data and then bind that data to an object I need to add something to the class like this

The [Bindable] line means that every property in this class will be used when this class is bound to another object in the application (like a list box etc). Next we have to give our class some properties..



I am adding itemName, itemKey and itemPrice, not that I have typed each property (the bit to the right of the : )

Lastly.. for this example I need to build a constructor method that will use to build an object from this class.The constructor is the public function Items() section.
This function requires 3 parameters, cItemName, cItemKey and cItemPrice, these parameters are then assigned to their equivalent property. You should remember that the type of the parameter you pass MUST match the property type you have given the property! It would be a BAD thing to try and assign a NUMBER to a property you have defined as a STRING :-)

To add this class into your application you would then switch back to the project MXML file and in the script block below the &lt![CDATA] I add this

import tells the complier to go to the myObjects directory and load in the Items.as file and create it as a class that I can use in the application. To create a object based on this class all i need to do is this

The [Bindable] tag allows me to BIND the object that follows to another object and then I have declared a new variable called myData with a type of Items.
this is the equivalent of the DIM statement in LS, something like
Dim myDate as NotesDateTime

I have create an instance of the object but it has yet to be instansiated, i do this with the following line
myData now exists as an object with the values passed to it in line 12.

... and that folks is how to create, design and implement a basic class in FLEX.

Friday 10 April 2009

Flex for Thick Gits #3 - Events what FUN!

I am going to assume you folk know what events are and not go into an detailed explanation, Basically one of the great unwashed (or users) does something in your app like clicking on a button and quite rightly that click does something. The "click" is the event and you have written some wonderful code that get processed when the "click" happens.

Now the most basic way of doing this is to put the code in the object .. like this

<mx:Label
id='myLabelThingie'
text="I'm Waiting"/>

<mx:Button
id="bigButtonOne"
label="Click Me Hard"
click="myLabelThingie.text='OOOO that was good'"/>


OK there are two things here... the first is a LABEL object which has an id of myLabelThingie A Label is a very simple object that will hold some text, in this example it starts of displaying the text I'm Wating. I need an id so that I can access it later on. Please make sure your IDs are always unique in your application!!

Next to it there is a BUTTON object. The text on it reads "Click me Hard" it looks like this


When a user clicks on the object the click=".." kicks in and the text on the label, accessed via it's ID is changed to "OOOO that was good".. like this


Just the same as LS, JS and even VB ;-)

this is a fine and dandy way of doing things but when you want to be really smart it does limit it you a little.. so you build functions to do the exciting stuff and call them in the Click Event of the object. functions are written in ActionScript and are placed in a <mx:Script> tag, Like this :-

<mx:Script>
<![CDATA[
private function clickMe().void
{
myLabelThingie.text='OOOO that was good';
}
]]>
</mx:Script>

<mx:Label id='myLabelThingie' text="I'm Waiting"/>

<mx:Button id="bigButtonOne" label="Click Me Hard" click="clickMe()"/>



Note that the actionscript code is nothing that even closely resembles well formed XML and this is why we surround it with the <![CDATA][ ... ]]> tag. If you forget this you will have LOADS of fun with errors!

Also of note here is that functions that types (the :void bit) this means that the function will not return any data. It is not strictly required, the complier will moan at you if you don't type your functions. Best practise is to :void functions that do not return things, it helps you and those that come after you recognise the function as a non-return function:-)

If you wanted to pass some data to the function, lets say some text for the label you can do this but you MUST type the value passed, just like in dear old LS.

<mx:Script>
<![CDATA[
private function clickMe(myWeeMessage:String).void
{
myLabelThingie.text=myWeeMessage;
}
]]>
</mx:Script>

<mx:Label id='myLabelThingie' text="I'm Waiting"/>

<mx:Button id="bigButtonOne" label="Click Me Hard" click="clickMe()"/>



Notice the :String - this is the equivilant of this LS

function clickMe(myWeeMessage as String)

OK now I want to mention a VERY important event creationComplete This event is fired when a component is all ready and waiting in the application. Every object in your app has a creationComplete event. The app itself has a creationComplete event and this is fired when all the component objects contained in the application are loaded and ready and have broadcast their creationComplete events upwards to the Application.

The Application level creationComplete is analogous to the onLoad() event in JS.

All your Flex applications have a built in "listener" function that listens for events, you don't need to know how it does this just know that it does and it does and know that it is your friend!

When the "listener" function "hears" an event being fired somewhere in the application, it generates an event object and this event object contains lots of useful information. The event object's contents depend on the event that was "heard" by the listener" but all events objects are based on the same Actionscript base class.

In my next wander into FLEX land I will look at the event object in more detail

Saturday 28 March 2009

Flex for Thick Gits #2 -MXML is not a Roman Date!

Right it is Saturday, there is no rugby worth the watching so what better way to spend the time - lets talk about MXML.

MXML is what you right FLEX apps in. (At this point i should mention that an AIR app is just a FLEX app that runs outside the browser using a runtime .. ok?) it is just ordinary common or garden XML that describes the application, once you have written the MXML you compile it and you have your FLEX application.

If you use FLEX BUILDER it takes a lot of the pain out of the "painting" of the application and I urge you to download it and try it, it will make your transistion into a FLEX developer so much easier that writing the MXML first hand.

OK lets look at some MXML



(Click Image to make bigger)

Since MXML is XML the first line of any application has to be
<?xml version="1.0"?>

This defines it as XML, you will note that it does not have a closing </xml> tag, this is fine so don't worry about it.

Next we have
<!-- DominoYesMayBe Is A Love God -->

This is a comment, you may want to make yours more meaningful, however if you feel you must extol my god like appearance to the world please feel free to do so ;-)

Next we have
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

This is the start of your MXML, everything to do with your application will happen between this opening tag and the corresponding closing tag </mx:Application>

Next we have
<mx:VBox>

This is the opening tag of a visual component called a vBox, it has to have a corresponding end tag of </mx:VBox> which you will see several lines further on.

Then there is
<mx:TextInput id="myText" text="Calypso Duck Snot" />

A TextInput is the visual equivalent of an HTML <input type='text'> input field. Note I have given it an ID of myText and some default text of "Capypso Duck Snot". Those eagled eyed folk will notice there is no closing tag for this item. Well the / before the > closes the tag so that you don't need a </mx:TextInput>

Also you will not that the vBox tags enclose the TextInput tag this means that visually the TextInput will be inside the vBox.

Both the vBox and the TextInput are neatly enclosed in the <mx:Application> tags. Thus placing them inside your application - This is as it should be for this is the Tao of XML :-)

OK.. in the next post we will create another little application and have a look at variables!

Friday 27 March 2009

Flex for Thick Gits - My version of a flex "hello world" first app - "Calypso Duck Snot"

Okey Dokey Peeps.

Flex what is it and how do I "do" it?
Good Question and hopefully this will be the first in a series of initially basic "how to" posts for the absolute beginner on how to do sexy things with flex.

So what is Flex? Flex is a highly productive FREE open source framework for building and maintaining sexy looking web applications that you can deploy on all major browsers (using the pervasive flash plugin) and desktops using the AIR runtime and like notes it can be deployed on most OS's

Now the first thing you can do is down load the Flex IDE called Flex builder currently at V3 this gives you a 60 day trial after which you have to buy it. So I hear you scream where is the FREE bit? Well you don't actually need Flex Builder as you can develop in a decent text editor, like Notepad++ .

I suggest you download Flex Builder and get all you can out of it during the Trial, as it will do all the boring bits for you and if you can't get the $$ for it you should have a decent grounding in the way the code is put together. The IDE is built around Eclipse so it should be familiar to devs who have moved up to the V8.5 designer client, but more on that in later posts.

So you have downloaded and installed your Trial of Flex Builder from the link above.. it is OK to do this even if you have the 8.5 DDE editor installed the two installs are separate and won't interfer with each other. You click on the chunky Flex Builder Icon on your desktop and voila it opens... (click on any of these piccies to make 'em BIG)



Now I will go into what does what in later posts for now lets just do the DominoYesMayBe version of a "Hello World" application. So Click FILE then NEW then FLEX PROJECT ...like this



and then you will get a nice dialog asking for a name and other things that will define the project.. for now just given it a name and click FINISH

... and your project will be created and ready for you to play with. The screen itself is a bit scary cos it comes up in script mode and that can be a bit intimidating because it starts ?XML which is enough to make some developers go pale and have palpitations :-)


Take a moment to compose yourself and click on the Design Tab, the screen should look like this


The big grey bit is your canvas onto which you will drag components and do your designing of the UI. So I drag a Button component from the list panel on the left and drop it on the grey canvas.



Over on the right i change the LABEL text for the button to have something more edifying than BUTTON. In this case "Click me Now".. (the actual text on the button will not change until you move focus away from the property field)



OK next I drag a LABEL from the component panel onto the canvas.. I size it using the corner anchors so that it is relatively large, then change

A) The ID of the label to be MyWord
B) I cleared the LABEL text.. cos I dont want it to say LABEL when the project starts
C) the Font Size to be 30pt

it looks like this ...



and that is it for my design.. now lets make the button do something.. switch to the SOURCE tab. You will notice that the source has changed.. These are the components and their properties that you have added to the canvas... it will look like this ...


Go down to the line that reads
<mx:Button x="59" y="31" label="Click Me Now" width="136"/>

and before the /> type this:
click="MyWord.text='Calypso Duck Snot';"

It should now look like this...


(BTW if you typed all the source into a notepad and used the SDK to compile it it would result in the same application!)

Ok you are ready to test it, so press [ctrl]+[s] or File then Save to save your project and then on the tool bar click the RUN button.



Your browser will open and there will be your button in all its glory ... Click it go on you know you want to... and when you do up pops CALYPSO DUCK SNOT in the label.

Here is my Version live on the interwebthingie .. (oh BTW you find all the files you need to go live in the bin-release folder of your project directory)

So that's it.. your first Flex App, it wasn't hard was it. In the next post I will try and a bit sexy with some more components and more events... WHAT FUN!

Disqus for Domi-No-Yes-Maybe