<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
	<channel>
		<title>Codestore Activity Log</title>
		<description>Latest ten updates to codestore, be they blogs or articles.</description>
		<language>en-gb</language>
		<link>http://www.codestore.net</link>
		<lastBuildDate>Wed, 10 Mar 2010 03:20:33 -0500</lastBuildDate>
		<atom:link href="http://www.codestore.net/store.nsf/rss.xml" rel="self" type="application/rss+xml" />

		<item>
			<title>Flex: Using A SharedObject to Remember User Settings | Blog</title>
			<pubDate>Wed, 10 Mar 2010 03:20:33 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Try this:</p> <ol> <li>Open the <a href="http://www.codestore.net/apps/contacts.nsf">Contact Manager app</a>.  <li>Change the columns that are visible by using the "column chooser" button, as below:<br>&nbsp;<img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/0E7F75DEAB186A5B862576E200335210/$file/image_ad337285-748e-4279-8ff2-f739e6ff96ff.png" width="285" height="224">  <li>Quit the browser.  <li>Re-open the browser and visit the app again.  <li>Notice how the visible columns are the same ones as before you closed the browser!</li></ol> <p>The View component I've been developing recently now uses the Flex equivalent to the cookie - the <a href="http://learn.adobe.com/wiki/display/Flex/Shared+Objects">SharedObject</a> - to remember your choice of columns across sessions.</p> <p class="sidePanel">Note that the same column preferences are shared across all browsers on your PC as the SharedObject is per Flash player install rather than per browser.</p> <p>For now it won't remember your preferred order of columns (did you know you can drag and drop columns to change the order!?). I'll work on that. Nor does it remember the width of them. I'll work on that too. </p> <p>In the mean time <a href="http://www.codestore.net/store.nsf/rsrc/bloggifs42/$file/contacts_v19.zip">here's the updated source code</a> for both the Domino database and the Flex source code. Enjoy. More to come...</p> <p>...can anybody think of anything else this View component is lacking before I can consider it a universal solution for all your Notes-to-Flex migration tasks?</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100310-0320?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100310-0320</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100310-0320</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100310-0320?Open#comments</comments>
			<slash:comments>1</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100310-0320</wfw:commentRss>
		</item>
		<item>
			<title>Flex: A Toggle-Style Column Component for Your Views | Blog</title>
			<pubDate>Tue, 9 Mar 2010 03:28:58 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Adding even more functionality to Flex "view" I've <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100203-0300?OpenDocument">been</a> <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100217-0812?OpenDocument">building</a> I've now added a "toggle column" to the list of available column types:</p> <p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/EF6179B0AA033F5A862576E100383B4F/$file/image_39441e0a-56c2-42ab-8323-9ab2efa07aa3.png" width="349" height="241"></p> <p>The idea is simple. You click the icon in the column to toggle a field value on the back end document. The icon in the view is either on or off and is dictated by the value stored in the field. It's an idea <a href="http://www.codestore.net/store.nsf/unid/BLOG-20090403?OpenDocument">I've mentioned before</a> which I've now made into a re-usable and customizable component.</p> <p>In this example I'm using the idea of being able to mark certain contacts as favourites and you can see a working example in <a href="http://www.codestore.net/apps/contacts.nsf">the Contact Manager app</a>. The component itself can be re-used in almost any scenario though.</p> <p>Adding a toggle column to a View is as simple as adding one line of XML to the View's configuration, which is specified in the backend Domino database, as discussed <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100112-0306">previously</a>.</p> <p>The XML looks like this:</p> <p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/0446F17DE5272021862576E10034170D/$file/image3_2.png" width="549" height="52"></p> <p>As you can see it's fairly easy to configure and customize. You can easily change the icon, field name and what the "on"/"off" values should be for the field. You can also add a tooltip for the column. </p> <p>The Column value of "starred" refers to the name of the XML node of each document which holds the field value we're interested. When the grid is first loaded it will show the icon as on or off depending on the value in the "starred" node. </p> <p>Clicking on the toggle column on sets the value of the field called "Favourite" to "1". Clicking it again sets the value to "".</p> <p>To see the XML used in the demo open <a href="http://www.codestore.net/apps/contacts.nsf/vwContactsAsXML">the Contacts view as XML</a>. This is the XML format that defines the structure and data of the View component. Simple, no?</p> <h4>Taking It Further</h4> <p>If you want to go further than simply modifying a single field at a time and want to perform a more involved action then you can do what I tend to always do and add a self-referencing "computed for display" field to the backend Domino form called something like "Action". In the WQS agent you can then check the value of this field. If the value is something like "Approve" then you can run a set of actions against the document. </p> <p>In this scenario the XML data for each row just needs to define a true/false value to let Flex know whether to show the icon as on or off depending on whether it's approved or not. The value for the fieldValueOn would be "Approve" and for fieldValueOff it would be "Unapprove". The actual XML data for approved documents would be "Unapproved" and for unapproved documents the column value would be "Approve". If that makes sense.</p> <h4>Summary</h4> <p>It's the simple little components like this that show how powerful Flex can be once you get going with it. With the View component and the components I'm adding to it I feel like this is getting to the point where it's a viable product that can be re-used in live applications. In fact<em>&nbsp;</em>I already am using it in live applications for paying customers.</p> <p><strike>In the next couple of days I'll update the downloadable version of the app with updated Flex source code. Before I do I want to blog about another addition to the code -- the ability to store a user's choice of columns across sessions using the Flash/Flex equivalent of cookies - the SharedObject. Give me a day or three.</strike> </p> <p><strong>Update:</strong> <a href="http://www.codestore.net/store.nsf/rsrc/bloggifs42/$file/contacts_v19.zip">Here's the updated source code and Domino db</a>.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100309-0328?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100309-0328</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100309-0328</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100309-0328?Open#comments</comments>
			<slash:comments>4</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100309-0328</wfw:commentRss>
		</item>
		<item>
			<title>Domino's POP3 Server Breaks HTML Emails by Removing Characters | Blog</title>
			<pubDate>Wed, 3 Mar 2010 01:14:58 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>There's a bug when sending and retrieving HTML email from Domino that has been plaguing me for years now. Now I've finally decided to take the time to investigate fully and find a fix.</p> <p>First, imagine the code below:</p><pre class="code2"><span class="TPkeyword1">While </span>i&lt;<span class="TPnumber">100 </span>                
        j <span class="TPoperator">= </span><span class="TPnumber">0</span>
        html <span class="TPoperator">=</span><span class="TPstring">""</span>
        <span class="TPkeyword1">While </span>j&lt;i
                html <span class="TPoperator">= </span>html <span class="TPoperator">+ </span><span class="TPstring">"split string "</span>
                j<span class="TPoperator">=</span>j<span class="TPoperator">+</span><span class="TPnumber">1   </span>
        <span class="TPkeyword1">Wend</span>
                                
        <span class="TPkeyword1">Set </span>mail <span class="TPoperator">= </span><span class="TPkeyword1">New </span>Email<span class="TPbracket">()</span>
        mail.Subject <span class="TPoperator">= </span><span class="TPstring">"Test " </span><span class="TPoperator">+ </span><span class="TPkeyword1">CStr</span><span class="TPbracket">(</span>i<span class="TPbracket">)</span>
        mail.HTML <span class="TPoperator">= </span>html
        mail.<span class="TPkeyword4">Send</span><span class="TPbracket">(</span><span class="TPstring">"jake howlett"</span><span class="TPbracket">)       </span>
                
        i<span class="TPoperator">=</span>i<span class="TPoperator">+</span><span class="TPnumber">5</span>
<span class="TPkeyword1">Wend</span></pre>
<p>What this does is send me 20 test emails. Each email is increasingly longer than the one before it and they all just repeat the words "split string" over and over.</p>
<p class="sidePanel">The code to send the email is based on my <a href="http://www.codestore.net/store.nsf/unid/BLOG-20091021-0127?OpenDocument">Email class</a>. All it does is create a multi-part MIME message. Nothing out of the ordinary. If you're using the MIME classes to send emails then this probably applies to you.</p>
<p>At some point in the loop the length of the message will get to such a size that something very worrying happens, as you can see below: </p>
<p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/C09F753B11162C74862576DB0027D283/$file/image_d4dbc923-524e-4dab-83e3-a033998db388.png" width="406" height="128"> </p>
<p>Notice the missing p?! </p>
<h4>The Problem</h4>
<p>From my investigations I've concluded the following:</p>
<p><strong>If you use a 3rd party mail client (like Thunderbird) to download a multi-part HTML email from a mail file on a Domino server using POP3 then the POP3 server will <em>remove</em> the 655th character and put a line break in its place.</strong> </p>
<p>Looking at the very same email in the original mail file - using the Notes client - there's <strong>no</strong> missing character.</p>
<p>What I've also noticed is that it only removes the first 655th char. Not every subsequent 655th char in the whole string. </p>
<p>Obviously this can be very, very bad. At it's least worst, like in this example, it just looks like a typo. However, I've had numerous bug reports because it's broken the string <em>inside</em> an HTML tag, resulting in un-clickable links or -worse still - emails that just show raw HTML.</p>
<p>The problem seems only to occur when sending emails where the HTML content is made up of one very long string. I guess using very long strings is generally a bad idea in any case, but there's nothing actually wrong with it, and nothing half as wrong as the server removing characters. </p>
<h4>The Solution</h4>
<p>What we need to do is avoid very long lines of HTML code in the email. The obvious solution is to add line breaks in the HTML <em>as</em> you build it in your code. </p>
<p>Unfortunately, in my case, I have way too many instances of code sending HTML email to make it practical to go and add new line breaks in to each email. Instead, what I did was put a fail-safe in to the Email class which sends the email. At the point it adds the HTML to the email it adds a new line at every point an HTML tag is closed, like so:</p><pre class="code2"><span class="TPkeyword1">Call </span>stream.WriteText<span class="TPbracket">(</span>Replace<span class="TPbracket">(</span><span class="TPkeyword1">Me</span>.str_HTMLPart, <span class="TPstring">"&gt;"</span>, <span class="TPstring">"&gt;"</span><span class="TPoperator">+</span><span class="TPkeyword1">Chr</span><span class="TPbracket">(</span><span class="TPnumber">10</span><span class="TPbracket">)))</span>
</pre>
<p>There's still a slight chance that there could be a long string that avoids this rule, but it's unlikely enough for me to feel happy this has resolved the issue for now.</p>
<h4>Summary</h4>
<p>So, you've been warned. If you're sending HTML emails to a Domino server, make sure you split the HTML string down in to sizeable chunks so that users who access their email via POP3 don't see broken HTML.</p>
<p>It all leaves me wondering why on earth the POP3 server would replace the 655th character in the first place. Assuming it does of course. My investigation wasn't exactly extensive but, from what I can tell, it definitely looks like it does. Why though? What's significant about 655? It's not like it's a base 2 number or anything.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100303-0114?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100303-0114</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100303-0114</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100303-0114?Open#comments</comments>
			<slash:comments>20</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100303-0114</wfw:commentRss>
		</item>
		<item>
			<title>How Many Programming Languages Is It Possible To Know? | Blog</title>
			<pubDate>Mon, 1 Mar 2010 01:53:07 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>As I go about learning to develop with ASP.NET I can't help thinking what I think about every time I start learning a new programming language or skill -- does my head have the space for all the new information?!</p> <p>While I'm sure the amount of knowledge currently stored in my head doesn't even come close to pushing the limits of the human brain I do sometimes feel a bit over-whelmed when I consider how much knowledge I need to retain in order to do my daily job.</p> <p>It all makes me think of the age-old saying: </p> <blockquote> <p><a href="http://en.wikipedia.org/wiki/Jack_of_all_trades,_master_of_none">Jack of all trades, master of none</a>.</p></blockquote> <p>Does adding yet another skill to my tool belt mean I'll be just a little less able at one of those I already use?</p> <p>Or, just as worrying, does learning something new mean something else is forgotten. Gotten rid of to make space. I remember reading that Einstein used to try and forget the phone numbers of people he no longer called, so that space was freed-up for something else. </p> <p>Perhaps we each have our own amount of RAM that can be used. Perhaps this decreases with age. Perhaps I should consider stopping trying to learn more than I really need to and concentrate on getting better at the ones I do know.</p> <p>Anybody ever think like this or, as I expect, am I just weird?</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100301-0153?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100301-0153</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100301-0153</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100301-0153?Open#comments</comments>
			<slash:comments>28</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100301-0153</wfw:commentRss>
		</item>
		<item>
			<title>Say Hello To Evelyn Howlett | Blog</title>
			<pubDate>Mon, 22 Feb 2010 05:50:24 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Yesterday morning at 5-something o'clock Karen woke up in the early stages of labour. Within minutes I'd called the midwifes. Remember last time we'd planned a home birth for Minnie, but left it too late to call and ended up speeding to hospital in an ambulance. Didn't want to repeat that experience.</p> <p>It was a good job I called so soon, as it had snowed during the night and the midwife on call had to come from a town about 14 miles away, which took her an hour. An hour after she arrived, so did Evelyn.</p> <p>Here's a photo of her in the style of the one <a href="http://www.codestore.net/store.nsf/unid/BLOG-20061212">of Felix</a> and <a href="http://www.codestore.net/store.nsf/unid/BLOG-20080624">of Minnie</a> on the days they were born. She looks a lot more like her brother than her sister.</p> <p><img style="border-bottom: 0px; border-left: 0px; margin: 10px 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/FECA684BF7C1591C862576D2004109F8/$file/image3.png" width="510" height="376"> </p> <p>The birth was all very straight-forward, which I guess it's easy for me to say. Mother and baby are doing well. </p> <p>My plan is to work from the house for at least this week and just spend an hour or so at the laptop each day to keep on top of things. At least that's the plan.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100222-0550?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100222-0550</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100222-0550</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100222-0550?Open#comments</comments>
			<slash:comments>55</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100222-0550</wfw:commentRss>
		</item>
		<item>
			<title>Are Branded Goods Ever Worth The Cost? | Blog</title>
			<pubDate>Thu, 18 Feb 2010 06:05:30 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>About five weeks ago our 3 year old Hoover washing machine broke for the second time in its short life. The problem was the same as the first time it broke -- the concrete ballast had fallen to pieces. You know when it happens as it starts to sound like a train is coming through the house when it's on full spin.</p> <p>First time it happened (just outside of its 1 year warranty) they came and fixed it within a couple of weeks (and charged us for it). I remember it well, as the Hoover-supplied "engineer" left all the concrete pieces, the old drum, boxes and various other bits on our doorstep as he left. I pointed out he'd forgotten them only to be told they're not Hoover's property so not their responsibility to dispose of. Yeah, thanks for that. </p> <p>But anyway, forward on just over a year later and it's happened again. The concrete that makes up the weight is in bits. Lots of bits. Oh, and lots of dust. It's everywhere. It's now obvious it's an inherent problem with the machine. Looking at the ballast it appears to be concrete of the grade you'd expect in a breeze block. Now I'm now engineer (well, actually I do have a degree in it) but even I know that's not going to last very long.</p> <p>Whether the machine has an inherent problem or not the fact is Hoover's customer service is truly terrible. </p> <p>Here's what happened in the five weeks since it broke:</p> <ul> <li>Rang Hoover "customer services" to tell them it had broken.</li> <li>Told them it was the exact same issue as before but they said an "engineer" would need to visit anyway.</li> <li>Before this first visit I am <em>forced</em> in to paying for either a &pound;110 labour charge or taking a &pound;159 policy with Domestic &amp; General (D&amp;G) to cover the labour charge <em>and</em> any other repairs for a year. Made sense so I went for the &pound;159 option. </li> <li>Wait a week for a man to visit, who takes one look inside the utility room door and say "It's your concrete's gone, love". Yeah, you don't say.</li> <li>Man then puts parts on order.</li> <li>Wait a week and call. Parts still on order.</li> <li>Trouble just about every neighbour on the road to do a load of washing for us.</li> <li>Wait a week and call. Parts still on order.</li> <li>Trouble just about every neighbour on the road to do a load of washing for us.</li> <li>Wait a week and call. Parts still on order.</li> <li>Trouble just about every neighbour on the road to do a load of washing for us.</li> <li>Decide enough is enough and that we might as well buy a new one for the same cost of the policy.</li> <li>Call D&amp;G to cancel policy. They say we can but that we'd be liable to a &pound;90 call-out charge for the man who was charged with coming to point out the obvious.</li> <li>Realise they have us by the short and curlies and there's little we can do but wait.</li> <li>Wait a week and call. Parts still on order.</li> <li>Trouble just about every neighbour on the road to do a load of washing for us.</li> <li>Karen calls and get very irate. Not until she point our her 3rd child is due any day now does the lady take pity and decide to lodge an internal complaint and to waiver the call-out fee.</li> <li>Three days later they confirm they've cancelled and we have nothing to pay.</li> <li>We go to Argos and order the cheapest washing machine we can find.</li> <li>Due for delivery next Tuesday. Well over 6 weeks since we stopped using the other.</li></ul> <p>So we got the refund for the policy, but who won? It certainly doesn't feel like we have! We're just back where we started. No better off and certainly worse off, as we owe all our neighbours a lot of favours.</p> <p>What we've learnt is that buying a top of the range branded domestic appliance is a complete waste of money. I can't remember exactly what we paid for the washing machine but it was in the region of &pound;400. We did this in the naive belief that it was better and would last.</p> <p>The machine that it replaced cost Karen about &pound;200 and lasted her about 10 years! When I buy things I have an expected life-span for them. With something like a washing machine it's in the region of 10 years. Certainly not three.</p> <p>So, what we've done (aside for vowing never to buy Hoover again) is buy a cheapo one instead. As has been pointed out to us recently "They're all just the same plastic crap inside nowadays anyway". </p> <p>We've bought this new cheap one on the premise that if it breaks outside of the warranty then it is, in effect,&nbsp; disposable. When the cost to repair it equals the cost of a new one what are you supposed to do? As much as I hate the idea of sending the "old" one to the tip, what am I supposed to do? </p> <p>There are some things in life you take for granted until they break. I didn't realise how much we relied on a washing machine until now. Well, not so much me, but my very pregnant wife who was way more patient with this whole saga than I would have been.</p> <p>It all leaves me thinking extended warranties and the like are a complete waste of money. Just buy cheap and replace it when broke. I never thought I'd be suggesting such a wasteful idea, but it's the only real option here. Hoover should be ashamed of the terrible, terrible customer service.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100218-0605?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100218-0605</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100218-0605</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100218-0605?Open#comments</comments>
			<slash:comments>29</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100218-0605</wfw:commentRss>
		</item>
		<item>
			<title>Flex: Open Attachments From The View | Blog</title>
			<pubDate>Wed, 17 Feb 2010 08:12:02 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>As part of the <a href="http://www.codestore.net/apps/contacts.nsf">Contact Manager app</a> I showed how you can <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100203-0300">display a paper clip icon in the column</a> of a datagrid to represent documents with attachments. </p> <p>Let's take this one step further and allow the user to download any one of the attachments straight from the view, without having to open the document.</p> <p>If you re-open the demo app you should see a new option in the columns-to-show dropdown called Attachments:</p> <p>&nbsp;<img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/EA1C13C033476791862576CD004DFFD1/$file/image_42068d56-4cf7-471b-9f40-85f812803a70.png" width="408" height="246"> </p> <p>If you enable this column you'll see a dropdown box in the last column of any row that contains attachments. Clicking on a file name will launch/save it (depends on your browser preferences).</p> <p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/B7B82936426253B3862576CD004E004B/$file/image_18551307-f14e-4e3f-b1ca-44b54bccd402.png" width="408" height="238">&nbsp; </p> <p>Not an essential feature in every app but a nice example of something simple to achieve that adds value in certain circumstances.</p> <h4>How Did I Do It?</h4> <p>First thing I did was change the XML that defines both the columns and the data for the grid/view. Here it is:</p> <p>&nbsp;<img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/E467FB5211C172B1862576CD004E0152/$file/image_f1b0ab02-0e23-4245-93cf-87a2dd2b983b.png" width="579" height="311"> </p> <p>Notice theirs a column of type "download" which has a value of "files" and an attribute called "separator". This tell it to look for the "files" node of each document node and explode it using the value passed as a separator.</p> <p>In the XML above it would give us an array of two file name strings - one for a PDF and one for a Word doc. It should be fairly obvious how this XML is produced in a Notes view, no?</p> <p>In Flex, from within my View component, when looping the &lt;columns&gt; XML to build each column for the grid I added the following code:</p><pre class="code2"><span class="TPkeyword2">if </span><span class="TPbracket">(</span>column.hasOwnProperty<span class="TPbracket">(</span><span class="TPstring">"@type"</span><span class="TPbracket">) </span><span class="TPoperator">&amp;&amp; </span>column.@<span class="TPkeyword5">type</span><span class="TPoperator">==</span><span class="TPstring">"download"</span><span class="TPbracket">){</span>
 <span class="TPkeyword2">var </span>dlRenderer:ClassFactory <span class="TPoperator">= </span><span class="TPkeyword2">new </span>ClassFactory<span class="TPbracket">(</span>net.codestore.flex.DownloadColumnRenderer<span class="TPbracket">)</span>;

 dlRenderer.properties <span class="TPoperator">= </span><span class="TPbracket">{</span>
  columnName: column.<span class="TPkeyword5">valueOf</span><span class="TPbracket">()</span>,
  itemSeparator: column.@separator<span class="TPoperator">||</span><span class="TPstring">"; "</span>,
  view: <span class="TPkeyword2">this</span>
 <span class="TPbracket">}</span>;

 col.headerRenderer <span class="TPoperator">= </span><span class="TPkeyword2">new </span>ClassFactory<span class="TPbracket">(</span>net.codestore.flex.DownloadHeaderRenderer<span class="TPbracket">)</span>;
 col.itemRenderer <span class="TPoperator">= </span>dlRenderer;
 col.resizable <span class="TPoperator">= </span><span class="TPkeyword1">false</span>;
 col.<span class="TPkeyword5">width</span><span class="TPoperator">=</span><span class="TPnumber">55</span>;
<span class="TPbracket">}</span>
</pre>
<p>The code for the DownloadColumnRenderer class looks like this:</p><pre class="code2">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:Box xmlns:mx="<a class="TPurn" href="http://www.adobe.com/2006/mxml">http://www.adobe.com/2006/mxml</a>" horizontalAlign="center" verticalAlign="middle"&gt;
        
        &lt;mx:Script&gt;
                &lt;![CDATA[
                        import net.codestore.flex.View;
                        
                        [Bindable]
                        private var _columnName:String;
                        
                        [Bindable]
                        private var _separator:String;
                        
                        [Bindable]
                        private var _view:View;
                        
                        public function set columnName(colName:String):void{
                                _columnName = colName;
                        }
                        
                        public function set view(view:View):void{
                                _view = view;
                        }
                        
                        public function set itemSeparator(separator:String):void{
                                _separator = separator;
                        }
                        
                        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
                super.updateDisplayList(unscaledWidth, unscaledHeight);
             
                if (data &amp;&amp; data.hasOwnProperty(_columnName) &amp;&amp; data[_columnName].toString().length&gt;0){
                        button.visible = true;
                        button.dataProvider = data[_columnName].split(_separator);
                } else {
                        button.visible=false;
                }
                        }
                        
                ]]&gt;
        &lt;/mx:Script&gt;
        
        &lt;mx:PopUpMenuButton id="button" icon="{IconLibrary.PAPER_CLIP_ICON}"
                click="_view.openDocumentAttachment(data.@id, button.dataProvider[0])"
                itemClick="_view.openDocumentAttachment(data.@id, event.label)"
                height="20" width="45" cornerRadius="2"/&gt;
&lt;/mx:Box&gt;
</pre>
<p>Notice that we've passed to the DownloadColumnRenderer the instance of the View component in which it lives. It's this instance of the View that we call the openDocumentAttachment() method on. This method looks like this and lives inside View.mxml:</p><pre class="code2">public <span class="TPkeyword2">function </span>openDocumentAttachment<span class="TPbracket">(</span>docId:<span class="TPkeyword2">String</span>, fileName:<span class="TPkeyword2">String</span>, save:<span class="TPkeyword2">Boolean</span><span class="TPoperator">=</span><span class="TPkeyword1">false</span><span class="TPbracket">)</span>:<span class="TPkeyword1">void</span><span class="TPbracket">{</span>
 <span class="TPkeyword2">var </span>_fileRef:FileReference <span class="TPoperator">= </span><span class="TPkeyword2">new </span>FileReference<span class="TPbracket">()</span>;

 <span class="TPkeyword2">var </span>urlReq:URLRequest <span class="TPoperator">= </span><span class="TPkeyword2">new </span>URLRequest<span class="TPbracket">()</span>;
 urlReq.url <span class="TPoperator">= </span>parentApplication.basePath <span class="TPoperator">+ </span><span class="TPstring">"0/"</span><span class="TPoperator">+</span>docId <span class="TPoperator">+ </span><span class="TPstring">"/$file/"</span><span class="TPoperator">+</span>fileName;

 <span class="TPkeyword2">if </span><span class="TPbracket">(</span>save<span class="TPbracket">){</span>
  _fileRef.download<span class="TPbracket">(</span>urlReq<span class="TPbracket">)</span>; 
 <span class="TPbracket">} </span><span class="TPkeyword2">else </span><span class="TPbracket">{</span>
  navigateToURL<span class="TPbracket">(</span>urlReq, <span class="TPstring">"_blank"</span><span class="TPbracket">)</span>;
 <span class="TPbracket">}</span>
<span class="TPbracket">}</span>
</pre>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p>All quite straight-forward and a quick example of how useful custom renderers can be in AdvancedDataGrids.</p>
<p>YMMV.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100217-0812?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100217-0812</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100217-0812</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100217-0812?Open#comments</comments>
			<slash:comments>4</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100217-0812</wfw:commentRss>
		</item>
		<item>
			<title>Don't Use The GetView Method In a Loop | Blog</title>
			<pubDate>Fri, 12 Feb 2010 03:30:37 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>I have a bit of code in a WebQueryOpen agent, which loops a field called "Members", which stores a list of Notes-style user names and prints to the browser their name in a formatted style. The code is very simply:<pre class="code2"><span class="TPkeyword1">Set </span>item <span class="TPoperator">= </span>web.<span class="TPkeyword3">document</span>.<span class="TPkeyword4">GetFirstItem</span><span class="TPbracket">(</span><span class="TPstring">"dspMembers"</span><span class="TPbracket">)</span>
                
<span class="TPkeyword1">Forall </span>v <span class="TPkeyword1">In </span>web.<span class="TPkeyword3">document</span>.<span class="TPkeyword4">GetFirstItem</span><span class="TPbracket">(</span><span class="TPstring">"Members"</span><span class="TPbracket">)</span>.<span class="TPkeyword3">values</span>
 <span class="TPkeyword1">Call </span>item.<span class="TPkeyword4">AppendToTextList</span><span class="TPbracket">( </span><span class="TPstring">"&lt;li&gt;"</span><span class="TPoperator">+</span>GetUserDetails<span class="TPbracket">( </span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>v<span class="TPbracket">)</span>, <span class="TPstring">"Formal" </span><span class="TPbracket">) </span><span class="TPoperator">+</span><span class="TPstring">"&lt;/li&gt;"</span><span class="TPbracket">)</span>
<span class="TPkeyword1">End Forall</span></pre>
<p>No prizes for working out what those does. For each member listed in the document it adds a bullet point to the displayed list of members. How the name of the user is displayed is governed by a separate function.</p>
<p>This GetUserDetails() function is a bit like an extended @NameLookup for LotusScript. I keep it in my "CommonRoutines" Script Library and it's accessible from all my agents. It looks like this:&nbsp; </p><pre class="code2"><span class="TPkeyword1">Function </span>GetUserDetails<span class="TPbracket">(</span><span class="TPkeyword3">username </span><span class="TPkeyword1">As String</span>, detail <span class="TPkeyword1">As String</span><span class="TPbracket">) </span><span class="TPkeyword1">As String</span>
        <span class="TPkeyword1">Dim </span>uname <span class="TPkeyword1">As </span><span class="TPkeyword2">NotesName</span>
        <span class="TPkeyword1">Dim </span>userDoc <span class="TPkeyword1">As </span><span class="TPkeyword2">NotesDocument</span>
        <span class="TPkeyword1">Dim </span>userView <span class="TPkeyword1">as </span><span class="TPkeyword2">NotesView</span>
        
        <span class="TPkeyword1">Set </span>uname <span class="TPoperator">= </span><span class="TPkeyword1">New </span><span class="TPkeyword2">NotesName</span><span class="TPbracket">(</span><span class="TPkeyword3">username</span><span class="TPbracket">)</span>
        
        <span class="TPkeyword1">If </span>web.directory.<span class="TPkeyword3">IsOpen </span><span class="TPkeyword1">Then </span><span class="TPcomment">'web.directory is "names.nsf"</span>
        
                <span class="TPkeyword1">Set </span>userView <span class="TPoperator">= </span>web.directory.<span class="TPkeyword4">getView</span><span class="TPbracket">(</span><span class="TPstring">"($VIMPeople)"</span><span class="TPbracket">)</span>
                
                <span class="TPkeyword1">If Not </span>userView <span class="TPkeyword1">Is Nothing Then</span>
                        
                        <span class="TPkeyword1">Set </span>userdoc <span class="TPoperator">= </span>userView.<span class="TPkeyword4">GetDocumentByKey</span><span class="TPbracket">(</span>uname.<span class="TPkeyword3">Abbreviated</span>, <span class="TPkeyword1">True</span><span class="TPbracket">)</span>
                        
                        <span class="TPkeyword1">If Not </span>userdoc <span class="TPkeyword1">Is Nothing Then</span>
                                
                                <span class="TPkeyword1">If </span>detail <span class="TPoperator">= </span><span class="TPstring">"Long" </span><span class="TPkeyword1">Then</span>
                                        GetUserDetails <span class="TPoperator">= </span>userdoc.Salutation<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span>_
                                        <span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span><span class="TPkeyword1">Left</span><span class="TPbracket">(</span>userdoc.FirstName<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span>, <span class="TPnumber">1</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span>userdoc.Lastname<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span>_
                                        <span class="TPoperator">+</span><span class="TPstring">", " </span><span class="TPoperator">+ </span>userdoc.CompanyName<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">"&lt;br /&gt;"</span><span class="TPoperator">+</span>userdoc.OfficePhoneNumber<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span>_
                                        <span class="TPoperator">+</span><span class="TPstring">"&lt;br /&gt;&lt;a href=""mailto:"</span><span class="TPoperator">+</span>userdoc.MailAddress<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span><span class="TPoperator">+</span><span class="TPstring">"""&gt;"</span><span class="TPoperator">+</span>userdoc.MailAddress<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span><span class="TPoperator">+</span><span class="TPstring">"&lt;/a&gt;"</span>
                                                    
                                <span class="TPkeyword1">Elseif Lcase</span><span class="TPbracket">(</span>detail<span class="TPbracket">) </span><span class="TPoperator">= </span><span class="TPstring">"formal" </span><span class="TPkeyword1">Then</span>
                                        GetUserDetails <span class="TPoperator">= </span>userdoc.Salutation<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span><span class="TPkeyword1">Left</span><span class="TPbracket">(</span>userdoc.FirstName<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span>, <span class="TPnumber">1</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span>userdoc.Lastname<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span>
                                        
                                <span class="TPkeyword1">Elseif Lcase</span><span class="TPbracket">(</span>detail<span class="TPbracket">) </span><span class="TPoperator">= </span><span class="TPstring">"fullname" </span><span class="TPkeyword1">Then</span>
                                        GetUserDetails <span class="TPoperator">= </span>userdoc.Salutation<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span>userdoc.FirstName<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">" " </span><span class="TPoperator">+ </span>userdoc.Lastname<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">)</span>
                                                                       
                                <span class="TPkeyword1">Else </span><span class="TPcomment">'Unknown format. Must want field value?</span>
                                        <span class="TPkeyword1">If </span>userdoc.<span class="TPkeyword4">HasItem</span><span class="TPbracket">(</span>detail<span class="TPbracket">) </span><span class="TPkeyword1">Then</span>
                                                GetUserDetails <span class="TPoperator">= </span>userdoc.<span class="TPkeyword4">GetItemValue</span><span class="TPbracket">(</span>detail<span class="TPbracket">)(</span><span class="TPnumber">0</span><span class="TPbracket">)</span>
                                        <span class="TPkeyword1">Else</span>
                                                GetUserDetails <span class="TPoperator">= </span>uname.<span class="TPkeyword3">Abbreviated</span>
                                        <span class="TPkeyword1">End If</span>
                                <span class="TPkeyword1">End If</span>
                        <span class="TPkeyword1">Else</span>
                                GetUserDetails <span class="TPoperator">= </span>uname.<span class="TPkeyword3">Abbreviated</span>
                        <span class="TPkeyword1">End If</span>
                <span class="TPkeyword1">Else</span>
                        GetUserDetails <span class="TPoperator">= </span>uname.<span class="TPkeyword3">Abbreviated</span>
                <span class="TPkeyword1">End If</span>
        <span class="TPkeyword1">Else</span>
                GetUserDetails <span class="TPoperator">= </span>uname.<span class="TPkeyword3">Abbreviated</span>
        <span class="TPkeyword1">End If</span>
        
<span class="TPkeyword1">End Function</span>
</pre>
<p>The idea is that, given a name like Jake Howlett/ROCKALL it uses the address book to return a name in the form Mr J Howlett, Rockall Design ltd, Nottingham. Or you can just use it to get a field's value by name. If for any reason it can't find the user document or work out what to return it just returns the user name in abbreviated form.</p>
<p>It all works well, but, after not very long I noticed the WQO agent which used it was taking longer and longer to run. The slowness of the WQO was directly proportional to the number of Members. Most of you can probably see why. If not, then the title of this page should give you a clue.</p>
<p>The problem with my code is, of course, that I'm repeatedly calling the getView() method. Consider this from <a href="http://www.nsftools.com/tips/PerfTips.htm#codetips">Julian's list of preformance tips</a>:</p>
<blockquote>
<p>If you need to use a reference to a view multiple times in your code, get the view only once and share the reference (either using a global or static variable, or by passing a NotesView object as a parameter in functions/subs/methods). Accessing views using getView is a very expensive operation</p></blockquote>
<p>It turned out that each call to web.directory.getView("($VIMPeople)") was taking 0.3s. For 100 members that means it takes way, way too long to open. Remember no web page should take longer than 7s to open!</p>
<p>So, taking Julian's advice I turned the user view in the directory in to a global variable as part of <a href="http://www.codestore.net/store.nsf/unid/BLOG-20080211?OpenDocument">the WebSession class</a>. Agents that were taking 20 seconds or more to load are now taking less than one!</p>
<p>I had no idea this was such bad practice. More than ten years with Notes and I'm still learning the basics...</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100212-0330?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100212-0330</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100212-0330</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100212-0330?Open#comments</comments>
			<slash:comments>20</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100212-0330</wfw:commentRss>
		</item>
		<item>
			<title>Flex App Basics: And Finally - Source Code Download | Blog</title>
			<pubDate>Wed, 10 Feb 2010 03:29:22 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Enough waffle Jake, just give us the code. Ok, I've been teasing you long enough. Here's <a href="http://www.codestore.net/store.nsf/rsrc/bloggifs42/$file/contacts.zip">the download of both the Flex source code and the Notes database</a> for the <a href="http://www.codestore.net/apps/contacts.nsf/">Contact Manager app</a> we've been looking at.</p> <p>Download the Zip file and extract the NSF to your server and the Zip within the Zip to your PC somewhere. Then, from within Flex Builder, go to File -&gt; Import and browse to this Zip. This will import all the code as a ready-to-compile project. </p> <p>Before you run it - once it's imported - open the Constants.as file from the root "/src" folder and then change the default path to wherever you put the NSF. Save the file and hit the compile/run/play button (green triangle). It should compile and run. You can then start adding documents to the backend database in Notes. Et voila.</p> <p>As a recap, here are the ten posts I've made over the last month or so, which describe various aspects of the code:</p> <ol> <li><a href="http://www.codestore.net/unid/BLOG-20100112-0306">Building Your Views Remotely</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100113-0353">Extending Basic View Behaviour</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100115-0307">Using Components To Simplify Your Code</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100121-0622">Opening Documents From a View</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100122-1015">Managing Document Attachments</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100125-0327">Multiple File Uploads</a> (note: read the comments to this post if you plan on using it!) <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100127-0347">Alert Is To Flex as MessageBox Is To LotusScript</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100203-0300">Display Column Values As Icons</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100204-0331">Creating an Icon Library</a>  <li><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100205-0345">The Form Container</a> </li></ol> <p>What it's <em>not</em> is a step-by-step guide that let's a beginner get to grips with Flex development. If you're looking for a more in-depth guide to learning Flex basics then you could try <a href="http://www.jamesward.com/2010/01/24/first-steps-in-flex-screencasts/">this comprehensive series of screencasts</a>.</p> <p>What it is is an ill-thought-out series of ramblings with very little coercion. By making it a numbered series I may well have given the wrong impression. Sorry. While each of the ten posts has its own merits there's little point reading them in order, really. Although, if you're going to pick apart the Flex app itself it might be worth doing so.</p> <p>Even if you have no use of a Contact Manager (heck, if you needed a contact manager then this probably isn't of much use anyway) the Flex app represents a conglomeration of as many of my current "best practices" as I could squeeze in. The code contains a lot of the goodness that I've learnt over the past year or more of my love affair with Flex. Primarily it demonstrates the ever-important concept of using re-usable components wherever possible.</p> <p>Enjoy. If you have any questions, let me know...</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100210-0329?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100210-0329</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100210-0329</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100210-0329?Open#comments</comments>
			<slash:comments>18</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100210-0329</wfw:commentRss>
		</item>
		<item>
			<title>Flex App Basics 10: The Form Container | Blog</title>
			<pubDate>Fri, 5 Feb 2010 03:45:15 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>We've talked about how to create <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100115-0307">a View component</a> so it follows that we need a Form component to compliment it. </p> <p>Let's take another look at the src folder of our Flex app. Notice I've added a folder called "forms" to keep each different form in -- one for the documents in the contacts view and one for the companies view. </p> <p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/0F7CB15A8A671B1A862576C100359426/$file/image_2a92ad6f-2834-4332-801a-1131d6393264.png" width="303" height="392"> </p> <p>These two forms are based on the Form.mxml component that you can see in the net.codestore.flex.* package/folder. This Form component contains all the things common between both the forms based on it. Things like the layout of the fixed-position button bar, the validation routine, the HTTP service to fetch and post the data from the server. </p> <p>Let's look at the Contact.mxml code, as below: </p> <p><img style="border-right-width: 0px; margin: 10px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/F4E197EB3193A2F9862576C1003594CA/$file/image_6ffa4ef7-7d40-4c75-bb13-9a0885fe611d.png" width="567" height="557">&nbsp; </p> <p>Notice how the main root tag is a Form. This means it is based on the Form component I mentioned. In the Form component I've defined 3 different objects that can be used to define the form -- content, buttons and the validators. Using these three properties of the Form we can quickly build a form without worrying about adding all the bits that are already in Form.mxml.</p> <p>It's inside Form.mxml that we add the buttons and content in to the right place. It's also where we loop the array of validators at the point the form is submitted and prevent submission on failure.</p> <p>Here's the code that runs when the Form element is first opened:</p><pre class="code2">[Bindable]
public <span class="TPkeyword2">var </span>validators:<span class="TPkeyword2">Array</span>;

[Bindable]
public <span class="TPkeyword2">var </span><span class="TPkeyword5">content</span>:Container;

[Bindable]
public <span class="TPkeyword2">var </span>buttons:<span class="TPkeyword2">Array</span>;

private <span class="TPkeyword2">function </span>initForm<span class="TPbracket">()</span>:<span class="TPkeyword1">void</span><span class="TPbracket">{</span>

 <span class="TPcomment">//Add each of the buttons passed to the form in to the action bar</span>
 buttons.forEach<span class="TPbracket">(</span><span class="TPkeyword2">function</span><span class="TPbracket">(</span>element:<span class="TPoperator">*</span>, <span class="TPkeyword5">index</span>:int, arr:<span class="TPkeyword2">Array</span><span class="TPbracket">)</span>:<span class="TPkeyword1">void</span><span class="TPbracket">{</span>
  bar.addChild<span class="TPbracket">(</span>element<span class="TPbracket">)</span>;
 <span class="TPbracket">})</span>;

 <span class="TPcomment">//Add the main form container in to the right place.</span>
 formContainer.addChild<span class="TPbracket">(</span><span class="TPkeyword5">content</span><span class="TPbracket">)</span>;
<span class="TPbracket">}</span>
</pre>
<p>That should give you an idea how it works. Notice the three public bindable properties, which we used to define the main elements of the form. </p>
<h4>How To Open a Form</h4>
<p>From some place in our script, say in the double-click event of the view, all we have to do in order to open a document using one of our forms is run code like this:</p><pre class="code2">import <span class="TPkeyword2">forms</span>.<span class="TPoperator">*</span>;

private <span class="TPkeyword2">function </span>openDocumentInTab<span class="TPbracket">(</span><span class="TPkeyword5">id</span>:<span class="TPkeyword2">String</span><span class="TPbracket">)</span>:<span class="TPkeyword1">void</span><span class="TPbracket">{</span>
 <span class="TPkeyword2">var </span><span class="TPkeyword5">form</span>:Contact <span class="TPoperator">= </span><span class="TPkeyword2">new </span>Contact<span class="TPbracket">()</span>;
 <span class="TPkeyword5">form</span>.isNewDoc <span class="TPoperator">= </span><span class="TPkeyword1">false</span>;
 <span class="TPkeyword5">form</span>.documentUnid  <span class="TPoperator">= </span><span class="TPkeyword5">id</span>;
 
 <span class="TPcomment">//Add it to the tabbed navigator</span>
 tabs.addChild<span class="TPbracket">(</span><span class="TPkeyword5">form</span><span class="TPbracket">)</span>;
<span class="TPbracket">}</span>
</pre>
<p>Because we've told it to import everything in the src/forms/ folder we can then create objects of the type "Content" (or "Company") as if they were built-in components. Thus we have access to the public properties of the objects, such as isNewDoc and documentUnid.</p>
<p>Obviously there's more to it than this, but hopefully you'll get the idea? The concept it quite simple and works quite well. If you need to alter the behaviour of the underlying Form element then you can do and the change will apply to all "forms" built on it.</p>
<h4>What Next?</h4>
<p>I think I've had enough of this Flex App Basics series of articles and I'm sure you probably have. I'm not even sure they've followed enough of a pattern to call them a series anything. They're more like random jottings that don't really piece together too well. Hopefully they'll have given you ideas on different methodologies though.</p>
<p>On Monday I'll package all the Contact Manager code together and upload it here. While it's nowhere near being a finished app it's good enough for you to rip apart and learn from. If I get the time to I'll try to keep adding on top of it.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20100205-0345?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20100205-0345</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20100205-0345</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20100205-0345?Open#comments</comments>
			<slash:comments>3</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20100205-0345</wfw:commentRss>
		</item>


	</channel>
</rss> 
