« This is my first Flex project. | Main | Effects are hard to get just right. »
April 25, 2005
Are components really a good substitute for pages?
One of the things I get hung up on is that HTML-based sites are built up from pages, whereas Flex sites are one big glob of goo as far as the outside world is concerned. Some people see breaking up sites into pages as a bad thing (e.g., it forces you to jump through hoops to ensure that the proper data has been fetched on each page, etc.), whereas I see breaking up sites into pages as a good thing (in general, I agree with the REST folks, although I'm not religious about it).
For my project, I envisioned the following "pages" to start with:
- welcome
- new user
- album list + album detail
In the Flex world, the easiest way to split things up like this is to use a ViewStack. Coming from the HTML world, I find the idea that all the pages of your app are just lying around waiting to be displayed a bit unnerving, but I think it's just something I need to get used to.
At this point, my app looks something like this:
<Application> <Script> all script for all pages goes here... </Script> <ViewStack id="vs"> <Panel title="welcome"> lots of stuff... </Panel> <Panel title="new user"> lots of stuff... </Panel> <Panel title="album list"> lots of stuff... </Panel> </ViewStack> </Application>
While the visual elements for all the "pages" of my app are split up in the ViewStack, the logic is all stored in one big block.
In Flex-land, the way to separate this out is to create components, so I tried that. At this point, my app looks like this:
<Application> <ViewStack id="vs"> <local:WelcomePanel id="welcomePanel" /> <local:NewUserPanel /> <local:AlbumInfoPanel /> </ViewStack> </Application>
Hooray for encapsulation! It looks pretty, right?
Well, now what happens when I want to hook these pages up? Let's say there's a button on the welcome panel that takes you to the new user page. Before, the code might have looked like:
<Button label="Create account" click="vs.selectedIndex = 1" />
Now, the same code would look like this:
<Button label="Create account" click="Application.application.vs.selectedIndex = 1" />
Ack! That looks like a severe violation of encapsulation! How does the component know that the application has a viewstack called "vs", let alone what index to set?
Another possible solution is to add the handler to the application, not the button, like so:
<Script>
<![CDATA[
function appInit()
{
welcomePanel.newAccountButton.addEventListener("click", this);
}
function handleEvent(event: Object)
{
if (event.type == "click" && event.target == welcomePanel.newAccountButton)
{
vs.selectedIndex = 1;
}
}
]]>
</Script>
But this is a violation of encapsulation in the other direction. How does the application know that the welcome panel has a button named "newAccountButton"?
The only "clean" solution is to provide a formal contract between the components and the application. This could be done by having the component declare an event (newUserClicked) or having the application create API functions which the components can depend on. The problem is that all of these solutions feel like overkill, when all I want is the equivalent of "go to this page".
In HTML, this is solved by the <a> tag, which essentially says "set the application state as described by 'xxx.html'." One could say that this violates "encapsulation" in that the page needs to know that xxx.html exists, but that's part of the architecture of the web. URIs are meant to be meaningful, persistent, and represent states that you are guaranteed to be able to get back to.
I don't have a perfect answer to this yet, but it's something I've been thinking about a lot.
Posted by sho at April 25, 2005 10:08 AM