Dec 6 2006

Cairngorm for MG/ColdFusion Developers (Part 3, getInstance)

Posted by Joe Rinehart at 3:32 PM
3 comments
- Categories: Flex and ColdFusion

Sorry for the delay in the series - work happened! I haven't forgotten it - I've created the entire Contact Manager application and it ColdFusion backend (just using Memory/XML for storage), but I just haven't had time to blog the process. Introduction

In the last post in this series, we created a ModelLocator implementation, populated an ArrayCollection inside of it with some data, and used data binding to display aspects of the data in a few different views.

I also left in a few mistakes that threw me off track when I first dealt with data binding and ModelLocators nearly six months ago (thanks to Simeon Bateman for patiently explaining things to me!). In this series I wanted to not just show how to do things, but a bit of the learning process that leads to the "right"-ish way of doing things.

In this part, I had intended to get into ValueObjects and use them to segue back to data binding and problems in our ContactModelLocator, but after reading the comments on Part 2, it'd probably be best to approach the problems first.

Our goals are as follows:

1. Identify the problems with our ContactModelLocator 2. Learn what the heck that "getInstance()" method is for 3. Correct our earlier ContactModelLocator and our views

Prerequisites:

1. Complete the first two portions in this series, and you'll be able to code along.

ContactModelLocator: Binding goes boom!

Right now, we've got binding implementing according to how I originally (mis)understood its workings. Some of you noticed "won't be able to detect changes" compiler warnings. We'll now walk through exactly what this means.

At the moment, we've got two Objects representing Contacts stored in our ContactModelLocator, inside of an ArrayCollection. We've got a datagrid bound to that collection, so if we change the first name of a contact, the datagrid should change, right?

Let's find out. Replace the two <view:.../> tags in CG4CF.mxml with the following code:

<mx:VBox>
   <view:ContactGrid />
   <mx:HBox>
      <mx:TextInput id="newFirstname" />
      <mx:Button id="Ok" click="ContactModelLocator.contacts[0].firstname = newFirstname.text" />
   </mx:HBox>
</mx:VBox>

Ok, so we tossed the little "count" thingie, and added a text input / button. We then told the button that, when it's clicked, it should change the first name of the first contact in the ModelLocator to the contents of the text input. Note: unless you're writing a series of tutorials on Flex concepts, this is a really bad way to do something like this.

Now, if we run our app, fill in the text box, and click the button...nothing happens. Resort the grid, though, and you'll see the first name change. Curious, eh?

The datagrid wasn't able to detect the changes to its data provider (ContactModelLocator.contacts). When it was resorted, it rebound, and the changes were picked up.

So, yeah, that's a problem with our ContactModelLocator.

Making ModelLocator members Bindable

If you dig through the Flex docs, you'll read a bit about the [Bindable] metadata declaration. At first glance, it sounds like adding it to our ContactModelLocator class, right above the "public class ContactModelLocator" line, will make the members of the class bindable.

So, let's do that. Our class declaration now looks like this:

[Bindable]
public class ContactModelLocator implements ModelLocator

Compile, run....no change.

How does binding work, anyhow? I'm no expert in the specific Flex mechanics, but I do know the observer pattern: its where one instance of an object is "suscribed" to messages from another instance.

Doesn't that sound like it should work here?

Well, remember what we said about static variables in the last part? They don't belong to an instance: they belong to a class. And our "contacts" member is static.

Let's remove that "static" bit and see if the "won't detect changes" message goes away:

public var contacts : ArrayCollection = new ArrayCollection();

Cool, no warning! Granted, that's ignoring the total failures we just caused by removing the ContactModelLocator.contacts ArrayCollection that our grid and button reference.

That's easy to fix though: because "contacts" now belongs to an instance of ContactModelLocator, and not the class itself, we just need to change the bindings to refer to an instance of ContactModelLocator.

But how do we make sure they're referring to the same instance?

That getInstance() bit

ActionScript 3.0 doesn't allow for a true Singleton implementation, which is kind of what we need. In its place, a workaround commonly called "getInstance()" has become popular. The reasons and mechanics are outside of scope, but this is all you need to know:

Whenever you reference a ModelLocator, ServiceLocator, or ViewLocator, use getInstance() to get an instance, not "new"!

When you use the static getInstance() function, the class is playing a bit of a trick to make sure you always get the same instance, acting as a Singleton.

Fixing our ContactModelLocator

First, we need to change our initialize() function in the ContactModelLocator to no longer be static:

public function initialize():void {

Now, in all of the places where we've used "ContactModelLocator.", we need to change to "ContactModelLocator.getInstance().", so that we're working with the singleton instance.

First, our onCreationComplete function:

private function onCreationComplete():void
{
ContactModelLocator.getInstance().initialize();
}

Then, our little firstname updating button:

<mx:Button id="Ok" click="ContactModelLocator.getInstance().contacts[0].firstname = newFirstname.text" />

And, last, in our ContactGrid view:

<mx:DataGrid width="100%" height="100%" dataProvider="{ContactModelLocator.contacts}" />

Ok, compile, run...still no help! What's wrong?

Conclusion

To find out, you'll have to tune in next time, when we learn about ValueObjects and the role they play in our application! This time, we've identified some core problems with our ContactModelLocator, seen how to use getInstance() to allow bindings to the singleton instance of our ModelLocator (if there's one thing to learn in this post, that's it!), and corrected our app to use the singleton.

Next time, we'll create a simple ValueObject to represent a contact, make it bindable, and get our grid working!

Comments

John Allen

John Allen wrote on 12/07/06 1:54 PM

totally awesome, totally awesome
Mike Rankin

Mike Rankin wrote on 12/08/06 1:28 PM

I don't mean to rush you or anything, but... eh... NEXT INSTALLMENT PLEASE!

Seriously, these articles have been fantastic. Once you get them finished, I think it would be a good thing for labs to reference them for new adopters.
Mike Weiland

Mike Weiland wrote on 12/11/06 2:23 PM

There's a problem in the example code in the Contact Grid view you have us change the code to:

&lt;mx:DataGrid width=&quot;100%&quot; height=&quot;100%&quot; dataProvider=&quot;{ContactModelLocator.contacts}&quot; /&gt;

Should be:

&lt;mx:DataGrid width=&quot;100%&quot; height=&quot;100%&quot; dataProvider=&quot;{ContactModelLocator.getInstance().contacts}&quot; /&gt;

Write your comment



(it will not be displayed)