How To Create A FlexBook Component That Includes Content Pulled From A Database
Introduction
I'm using the FlexBook component developed by Ely Greenfield (see reference 1 below) to create a yearbook as part of my organization's annual report. Rather than hard-code every page of the FlexBook in the mxml code, I wanted to pull the content for the pages from a database. Then I would be able to update the FlexBook's pages by just changing the database rather than having to change the Flex mxml, recompile the application, and upload the new swf.
You can view a prototype of the yearbook here (right click on the Flex application to view the source).
References:
- Ely Greenfields' blog, Quietly Scheming, FlexBook
- Ely Greenfields' blog, Quietly Scheming, FlexBook updated, source now available
- Renaun Erickson's blog, myFeedz in a FlexBook (read the comments posted)
Yearbook Backend
For the prototype, the backend is just an Access database with one table. In that table I store the image name (including its path) and its caption. There is a YearbookService ColdFusion Component that has a function (getAllImages) to pull all the records from the table and return the results as a query. Both the CFC and the Access database are included in the source files.
Setting Up The FlexBook
Reference 2 above has the FlexBook source code. After you download the source code (which includes several examples of how to use the FlexBook) and import the project into Flex Builder, you should see a directory names qs. Under this directory are other directories that contain the source code for the FlexBook component. I copied this directory into my yearbook project. I'm also using the LetterPage custom component that was in the FlexBook source code.
Be sure to study one of the FlexBook example's application tag for how Ely declared several different name spaces. Note that the default name space for all the mxml doesn't include the mx prefix. So if you use Ely's application tag, you don't start your mxml components with mx (for example mx:Text), but rather just use the component name (Text).
Below is the code that creates a FlexBook in my application.
<!--create the FlexBook component-->
<controls:FlexBook id="book" y="47" width="730" height="530" horizontalCenter="0"
animateCurrentPageIndex="true"
animatePagesOnTurn="false"
activeGrabArea="page"
edgeAndCornerSize="150"
itemSize="halfPage"
mouseDown="hideInstructions()"
content="{pages}"
>
<!--create the cover page in the book-->
<controls:cover>
<VBox horizontalAlign="center">
<Spacer height="150" />
<Text id="cover" horizontalCenter="0" condenseWhite="true" textAlign="center"
fontSize="32" fontFamily="Verdana" color="#000000">
<htmlText>
<![CDATA[
<b>STFM's 2007<br>Year Book</b>
]]>
</htmlText>
</Text>
<Spacer height="30" />
<Image source="@Embed('assets/stfm/logo.gif')" />
</VBox>
</controls:cover>
<!--create the back cover page-->
<controls:backCover>
<l:LetterPage text="STFM" backgroundColor="#000000" color="#FFFFFF" />
</controls:backCover>
</controls:FlexBook>
In one of the examples on Ely's blog (see reference 1) you can change some of the FlexBook's attributes to see what effect different attribute values will have.
Notice that I've bound the content attribute to the pages variable. As I'll discuss later, the pages variable is an array that is storing custom components. Each element of the pages array will become a page of content in my FlexBook.
I create a cover page with some text and our logo. I also have a simple back cover page that uses Ely's LetterPage component to display our name.
Populate The FlexBook's Pages With Dynamic Data
When the yearbook application is first created the function initVars is called. In this function I create the pages array and call the getAllImages method of my YearbookService CFC. The function below handles the result returned by the CFC (assuming no errors).
public function resultGetAllImages(event:ResultEvent):void {
//Result returned by the CFC is a query
//place the query results into an
//ArrayCollection
var imagesAryCol:ArrayCollection = event.result as ArrayCollection;
//loop through the imagesAryCol
//Note that I need to process two records
//at a time (there are two images per ImagePage
//(page of the FlexBook)
for (var i:int = 0; i < imagesAryCol.length-1; i=i+2) {
//create a ImagePage object for each record
//returned by the CFC function
var page:ImagePage = new ImagePage();
//use the column values of two records
//to assign values to the ImagePage's
//attributes
page.image1 = imagesAryCol[i].imageName;
page.caption1 = imagesAryCol[i].caption;
page.image2 = imagesAryCol[i+1].imageName;
page.caption2 = imagesAryCol[i+1].caption;
pages.push(page);
}//end for loop
//refresh the content of the book
book.content = pages;
} //end function getAllImages
I cast the query result returned by the CFC to an ArrayCollection (each record in the query result is then an object stored in the ArrayCollection). I then loop over each element of the ArrayCollection. Note that my for statement allows me to process two elements each time through the loop.
Inside the for loop, I create an ImagePage object. ImagePage is a simple custom component I wrote (see the source code) that displays two images and their text caption inside a VBox. The ImagePage component has four attributes and I use the column value from a record to provide the value for each attribute. Since each ImagePage holds two images and two captions, I'm processing two records each time through the for loop.
After setting the ImagePage's values, I add that ImagePage object to my pages array. After the for loop is finished processing every element stored in the ArrayCollection, I just need to reset my book's content attribute to the pages array. My FlexBook now has a cover page, a content page (rendered by the ImagePage custom component) for each ImagePage object I placed into the pages array, and a back cover page.
ImagePage Component Note
If you view the source code for my ImagePage component you'll see that I'm using the mx:Image tag's source attribute to load the image at run time. I could have used the @Embed preface but did not. The Embed preface makes the images part of the Flex SWF file, which if I have many images as part of my yearbook, will significantly increase the swf's size and increase the download time the user experiences. However, by not using the Embed preface and loading the images at runtime, there can be a very slight delay in the images loading after a page is turned in the yearbook. For more information about loading images in a Flex application see: http://livedocs.adobe.com/flex/2/docs/00000544.html.
If anyone has a good method for loading images ahead of time, while keeping the swf file size small, please post a comment with a link to an example.
Summary
If you study Ely's FlexBook examples, you'll see that there are many different ways to create the pages of a FlexBook. You can hard code each page and its content, you can use a custom itemRenderer, or you can use an array of components for the content. In this application I've used the later method, so that I can update the content pages by merely changing the records in the backend database. The downside of this approach is that each of my content pages have the same layout. If you want to vary the layout of your content pages, you may need to use a different method.
I want to thank Ely Greenfield for so generously making the FlexBook component source code available. The FlexBook component is powerful and flexible. It can be used in a variety of applications and most importantly is just fun to use.
I hope that this blog post has shown you one way to use the Flexbook component and inspired you to try using FlexBook in your own application.


Thanks for your code.
like you, i like ELY's Flexbook and have some projects with it. I just wanted to tell you i have found some minor bugs in the source (sometimes, the book hangs with cover and backcover). i have patch for these but i have another problem when using transparency (like anatomy). when you turn page, memory used is growing, growing .... So i need the way to solve this because if it's ok for a light application as anatomy, it's not for more advanced transparency application. I told this to ELY's forum but didn't received any reply.
Thanks for your code.
It’s really helping me. Now I'm using "Page Turning" feature of "FlexBook" externally by using a slider (STATE_AUTO_TURNING). But when my current page index is 0 and I set the turnToPage(3), then it's directly go to the page no. 3 -- It's OK. But I want to show as a book it first turn to page 1 then 2 and lastly 3.
So would you please solve that for me?
I have to create mx:Tile and mx:text control dynamically, meaning I will loop though a xml results returned from a web srvice and depending on results it should create a mx:tile and mx:text. Lets say there are 10 records returned it should creat 10 mx:tile tags and each tile should have one mx:text. Is there a way to creat controls dynamically ? Any help is appreciated.
Thanks
Siri
How can we set itemRenderer for flexbook component in actionscript...
I need to change the itemrenderer dynamically
thanks in advance
I would like to create a Flex book of pdf pages.
Do you have any advice?
Thank you.
Thank for your tutorial regarding Ely's FlexBook component. I greatly admire the component and really want to use it in my current project. However, I'm overwhelmed by the failures I've had over the last three days in trying to get it to work.
I'm a relative newcomer to Flex, but I have a programming background and have taken book training via the Adobe Flex 3 Training from the Source Book.
I think one issue might be that I am using Flex/Flex Builder 3 However, I am using the Flex 2.01 compiler within Flex Builder, since FlexBook is built in Flex 2.
I have followed your tutorial many several through. However, I suppose that your method--RemoteObject via ColdFusion--is different enough that I'm getting lost in translation.
I am trying to get my data into FlexBook using HTTPService via a RubyOnRails server that is delivering plain XML.
I can get both the FlexBook and the XML feed working in separate applications.
However, whenever I try to combine them in a single app--making my XML feed the source of the FlexBook's content, I run into the same issues:
(These seem like they could relate to FlexBuilder as much as the project itself. Does that seem possible?...)
1) I am replacing your methods of arriving at a "pages" array which serves as source of the FlexBook's content. Basically, I am replacing your <RemoteObject> with my <HTTPService>. In order to do this, I am changing the value of the creationComplete attribute of the <Application> from initVars() to feed.send()--the send method of my HTTPService.
I am also replacing your getAllImages function with my own feedResultHandler() function, which fires upon the ResultEvent of my HTTPService.
I feel fairly confident that everything is properly arranged and taken care of, from using your code comments and my knowledge of the HTTP Service.
And as you'll see below, the issues I'm having are very strange and not what you would expect if I had gotten those steps wrong.
to the your XML object with one called "pages" from my HTTPService, I save and refresh and see no changes to my SWF.
What actually happens is that the moment I actually replace your <RemoteObject> plumbing with me <HTTPService> plumbing, then save the file and launch to see what happens, NOTHING new happens.
In other words, it looks as if the file did not save, and that there has been no change at all to the SWF. Your data still loads in the SWF. and no complaints are made by the debugger or the browser.
2) In trying to correct this, I will then absolutely REMOVE all of your code. Then I will re-launch and the same thing happens. Nothing. No changes. It's as though Flex refuses to take this last update to the source file.
3) I then debug slowly, and at my breakpoints, the variables that appear show your old data--even though your code is no longer in the source!
4) The only difference from this result, is that occasionally I will get an error during debugging in the console which reads: TypeError, Cannot get reference from empty or null object. (I'm sorry, this is not the exact wording, but I'm unable to re-create the error at this moment.).
I also notice a very strange thing when this happens: The line numbers referred to in the errors, which also reference the related functions, are incorrect. Instead, I'm getting line numbers further down the page, way outside of those functions--as though the debugger is not seeing the new code that's been added to the source.
5) I then try everything I can think of in terms of re-importing the project files, clearing files from HTML-template and bin-debug folders, and importing the project files into entirely new projects, etc. Of course, clearing cache everywhere and restarting Flex and even my machine.
This never seems to work because I can never arrive at a new pure project which has all of the updated code in it. Instead, when I try to launch the application file, the file fails to launch, either with a simple error saying 'File failed to launch' or a message saying that the file cannot launch because a required file in bin-debug is missing.
Frustration to say the least.
I guess I would ask: Have you ever seen this issue with people trying to use the FlexBook with Flex 3/FlexBuilder 3?
And also, for you and for any of your readers, has anyone seen similar behavior with Flex 3/Flex Builder 3? That of a project not taking a key update to the source code.
I should say, I had no such problems stepping through all of the Flex 'Training from the Source' Manual, which entails many many changes to the same 3 application files in the same project.
THANK YOU FOR READING. ANY HELP IS GREATLY APPRECIATED.
James