1. Trang chủ
  2. » Công Nghệ Thông Tin

advanced Flex Application Development Building Rich Media X phần 6 ppt

51 153 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Advanced Flex Application Development Building Rich Media X phần 6 ppt
Trường học Almer/Blank
Chuyên ngành Advanced Flex Application Development
Thể loại Bài thuyết trình
Năm xuất bản 2007
Thành phố Unknown
Định dạng
Số trang 51
Dung lượng 12,47 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Here’s the code for the ServiceUtil class: package com.almerblank.utils{ import flash.net.Responder; import flash.system.Security; public class ServiceUtil{ private static var _instance:

Trang 1

Figure 9-3 Admin policies templates screen

Figure 9-4 Partner e-mail templates screen

Trang 2

Figure 9-5 Partner e-mail blast screen

In the beginning there was ActionScript

Armed with these screens, I could then open up Flex and start setting up the actual modules Before Iactually started designing and coding though, I took some time to set up my development environ-ment with the core classes that I like to use My application skeletons always include a series of

Singleton classes that allow me to manage an application’sfunctionality much more easily than without

Singleton classes, in this context, refers to ActionScript

classes that are coded using the Singleton design pattern,which as I mentioned in Chapter 7 is used to restrict instan-tiation of a class to one object This is accomplished via aclass method, usually getInstance(), that creates a newinstance of the class if one does not exist If an instancealready exists, it simply returns a reference to that class.This approach is very useful when you need only oneinstance of a class to coordinate actions across an entiresystem or application

By having a set of templates already prepared, I save myself

a considerable amount of time by not having to type thecode for these from scratch on every project Figure 9-6shows a listing of the classes I include at the start of aproject

Figure 9-6 Custom application skeleton

Trang 3

Overview of the ServiceUtil class

As you can see, the com.almerblank.mvc package is the most extensive because this is where themajority of the heavy lifting takes place It represents my modified version of the Model-View-Controller (MVC) concept Before I describe the classes in this package, I’d like to take a moment todescribe the ServiceUtil class At Almer/Blank, we do a lot of remoting with AMFPHP (www.amf-php.org) In the early stages, we had to ensure we always had the proper dependencies (ActionScriptfiles) included and had to do a bunch of new Gateway() and new Responder() definitions throughoutthe application Enter ServiceUtil I created this class to simplify remoting by minimizing my codeinvestments to only two method calls Here’s the code for the ServiceUtil class:

package com.almerblank.utils{

import flash.net.Responder;

import flash.system.Security;

public class ServiceUtil{

private static var _instance:ServiceUtil;

public var gatewayURL:String;

private var _gateway:RemotingConnection;

private var _response:Responder;

public function ServiceUtil(enforcer:SingletonEnforcer){}

public static function getInstance():ServiceUtil{

if(ServiceUtil._instance == null){

ServiceUtil._instance = new ServiceUtil(new SingletonEnforcer);

}return ServiceUtil._instance;

}//set your gateway URL

public function init(url:String,security:Boolean,policy:String):➥

void{

if(security){

flash.system.Security.loadPolicyFile(policy);

}gatewayURL = url;

}//call your AMFPHP service

public function call(service:String, resultHandler:Function,➥

faultHandler:Function, params):void{

_response = new Responder(resultHandler, faultHandler);

_gateway = new RemotingConnection(gatewayURL);

_gateway.call(service, _response, params);

}}}/**

Trang 4

* Helper class for remoting

*/

import flash.net.NetConnection;

import flash.net.ObjectEncoding;

class RemotingConnection extends NetConnection{

public function RemotingConnection(sURL:String){

objectEncoding = ObjectEncoding.AMF0;

if(sURL){

connect(sURL);

}}public function AppendToGatewayUrl(s:String):void{

//

}}class SingletonEnforcer{}

Using this approach, I only have to worry about one public variable, gatewayURL, and two public ods, init() and call() You’ll see exactly how this is implemented shortly Now, let’s continue ourexploration by examining the interfaces

meth-ActionScript interfaces

If you are unfamiliar with ActionScript interfaces, they are classes that declare all the public methods

that a particular class must define A class that implements an interface must include the methods that

are declared in the interface Think of an interface as a set of rules that the implementing class mustfollow This strict adherence helps to ensure the implementing class does what it’s supposed to do.The use of interfaces also has other benefits as well First, you can document interfaces so that theyare also included in any API documentation that you or your team utilizes Second, the use of an inter-face will cause the compiler to check whether or not the implementing class actually defines therequired methods This is crucial on any large project, as you may inadvertently forget to code amethod or code a method’s signature improperly and without an interface; the compiler may notcomplain, but if it does, the complaint may be a vague one, which inhibits an easy solution With aninterface, the compiler will tell you exactly what you did wrong And interfaces are not just limited tomethods, because you can include variables as well

Here’s the code for the IGlobal interface:

package com.almerblank.mvc.command{

public interface IGlobal{

function init():void;

}}Both the IGlobal and IService interfaces each have only one method in the beginning of any proj-ect, init() You’ll learn more about what init() actually does as we explore the Controller class

Trang 5

Class exploration

The following sections explore various ActionScript classes that I used for the communications controlpanel

The Controller class

The Controller class is the “traffic controller” for the application It receives requests from out the application and passes those requests on to the appropriate classes for processing Here’s theinitial code for the Controller class:

public class Controller extends EventDispatcher{

private static var _instance:Controller;

private var _model:Model = Model.getInstance();

private var _globals:Globals = Globals.getInstance();

private var _svc:Services = Services.getInstance();

private var _app:Object;

public function Controller(enforcer:SingletonEnforcer){}

public static function getInstance():Controller{

if(Controller._instance == null){

Controller._instance = ➥

new Controller(new SingletonEnforcer());

}return Controller._instance;

}

Trang 6

public function initApp():void{

_globals.init();

_svc.init();

}}}class SingletonEnforcer{}

In the early stages, Controller starts off with just initApp() This is where any application tion takes place First, the init() methods of all the Singletons used in the application are called.From there, the sky’s the limit So, if a login needs to be processed or a LocalSharedObject (LSO)needs to be checked at startup, this is the place to do it Figure 9-7 shows how this method gets called

initializa-Figure 9-7 Application initialization

The Model class

The Model class is like a dictionary of all application variables or properties Think of it as a data fer object (DTO) or value object (VO) It acts as a storehouse for application properties, allowing them

trans-to easily be retrieved from a central location In other words, you define it once and use it anywhere.Here’s the initial code for the Model class:

public class Model{

private static var _instance:Model;

public function Model(enforcer:SingletonEnforcer){}

Trang 7

public static function getInstance():Model{

if(Model._instance == null){

Model._instance = new Model(new SingletonEnforcer());

}return Model._instance;

}}}class SingletonEnforcer{}

If you place all your application variables in the variable declarations section, you can then accessthem using the variable defined in Figure 9-7 like so: model.someVariable The use of this class candefinitely help make your application more modular and scalable

The Globals class

The Globals class is where I house functionality that may not need to be modularized any further Forexample, if a particular functionality requires only two methods such as login() and logout(), I willinclude those methods in Globals rather than creating a separate Login class In other words, I keep

my “top-level” methods in this class Here’s the initial code for Globals:

public class Globals extends EventDispatcher implements IGlobal{

private static var _instance:Globals;

private var _model:Model;

private var _controller:Controller;

private var _app:Object;

public function Globals(enforcer:SingletonEnforcer){}

Trang 8

public static function getInstance():Globals{

if(Globals._instance == null){

Globals._instance = new Globals(new SingletonEnforcer());

}return Globals._instance;

}public function init():void{

_model = Model.getInstance();

_controller = Controller.getInstance();

_app = Application.application;

}}}class SingletonEnforcer{}

Now, let’s explore the init() method All it does is ensure that I have access to the Singletons that I’llneed to work with, as well as giving me access to the Application object Although you can probablyimplement this differently, I’ve found that this is the least error-prone implementation that suits mydevelopment needs

The Services class

The Services class is where all of the AMFPHP communication methods are housed Any service callsthat need to be made or service returns that need to be processed are handled here I usually set it up

so that the class is only accessed through Controller; sometimes in the early stages of development,however, I’ll allow direct access to quickly build a working demo, and then lock it down once thegroundwork is in place Here’s the initial code for Services:

public class Services extends EventDispatcher implements IService{

private static var _instance:Services;

Trang 9

private var _model:Model;

private var _controller:Controller;

private var _amfphp:ServiceUtil;

public function Services(enforcer:SingletonEnforcer){}

public static function getInstance():Services{

if(Services._instance == null){

Services._instance = new Services(new SingletonEnforcer());

}return Services._instance;

}public function init():void{

} catch(error:*) {trace('service init error: '+error.message);

}}private function faultHandler(fault:Object):void{

Alert.show(fault.description, 'Service Error:');

CursorManager.removeBusyCursor();

}}}class SingletonEnforcer{}

Any other classes added to the com.almerblank.mvc.service package will be specific to the tion, such as the Communications class that the Communications management screen uses Once I’vegotten those class templates in place, I stop with class creation and set up the design elements beforebeginning creation of any specific classes

applica-MXML development

You’ve already seen part of the main MXML file in Figure 9-7, which showcased the script block thatsets everything in motion Now, it’s time to start creating the various screens specified in the wires Iuse the wire shown earlier in Figure 9-1 to outline in my mind what Flex components to use Figure 9-8shows what this outline would look like if I chose to physically diagram it

Trang 10

Figure 9-8 Mental MXML layout diagram

At the very top of Figure 9-1 is the title of the application and an access control button for logging inand out of the application That combination is also present on all the other screens, so I place thiscombo in an ApplicationControlBar component The main navigation in this wire is represented as aset of tabs, which screams for the TabNavigator component, as does the subnavigation of that screen.After reaching this point, I examine each subview and try to determine the optimal way to lay themout Once I’ve got the mental picture illustrated by Figure 9-8 in my head, I start implementing the lay-out Figures 9-9 and 9-10 show the design and code views in the early stages of the app

Figure 9-9 Early design view of my app

Trang 11

Figure 9-10 Early code view of my app

Time to organize

Having the core MXML in place, I can now start building the various modules required for theCommunications screen Before doing so, I create two new folders; views and components, to betterorganize my forthcoming creations The views folder holds all the modules that make up the content

of the main TabNavigator Instead of putting all that code in the main MXML file, I can break it downinto smaller chunks to make it easier to read and thus easier to maintain as it grows Figures 9-11through 9-13 show the Communications view and how it’s implemented in the main MXML file

Figure 9-11 Communications screen (design view)

Trang 12

Figure 9-12 Communications screen (code view)

Figure 9-13 Communications incorporated into the main MXML

Layout design

With the foundation laid, all that’s left to do is build out the components required for theCommunications screen: PodTemplates, PodMailer, and PodTerms Reexamining my mental diagram, Isee that it would be a lot better to use a “fluid” layout to build these components vs using a bunch ofcontainers within containers When you do the latter, you unnecessarily add weight to your app, sinceeach one of those container classes has to be compiled To achieve my fluid layout, I mentally grouprelated components and use data binding to “snap” them together The following code fromPodTemplates.mxml illustrates this approach:

<?xml version="1.0" encoding="utf-8"?>

<mx:Canvasxmlns:mx="http://www.adobe.com/2006/mxml">

<mx:Labelid="lblTemplates"

Trang 13

y="10"

width="300"/>

<mx:Buttonid="btnRevertTemplate"

x="{selectedTemplate.x+selectedTemplate.width+4}"

y="10"

label="Revert"/>

<mx:Buttonid="btnSaveTemplate"

x="{btnRevertTemplate.x+btnRevertTemplate.width+2}"

y="10"

label="Save"/>

<mx:Labelid="lblSubject"

x="{selectedTemplate.x}"

y="{selectedTemplate.y+selectedTemplate.height+8}"

width="400"/>

<mx:Labelid="lblBody"

x="{templateBody.x+templateBody.width+50}"

y="{templateBody.y}"

Trang 14

text=" Acceptable variables for this template"

fontWeight="bold"/>

<mx:Listid="templateVariables"

Trang 15

Figure 9-15 Admin e-mail blast screen

Figure 9-16 Admin terms screen

Trang 16

Incorporating the data

With the basic layout complete, I usually start bringing the data model into the picture I considerwhat kinds of data I need to bring in and build off of that For the Communications screen, I need twodata sets, one for the e-mail templates and one for the policies So, I add two ArrayCollections,templateDP and termsDP, to the Model class for just that purpose Now, I start thinking in terms ofeverything the first subview needs to make it tick While still in the Model class, I add two more vari-ables, templateObj of type Object and template of type ObjectProxy templateObj represents adummy object that I like to use when dealing with data-bound forms in Flex It consists of a set of keyswith empty values that I use in a form-reset operation to bring the form back to its default emptystate I’ll show you how shortly template is the object to which the form is actually tied I chose to useObjectProxy because, when developing this system, I found that for members of the Model class thiswas the easiest way for me to maintain my data bindings, since Flex complains about binding to a plainObject’s properties Speaking of bindings, it’s time to start binding the template to the proper con-trols in PodTemplate Here’s the updated code for PodTemplates.mxml with the additions in bold:

<?xml version="1.0" encoding="utf-8"?>

<mx:Canvasxmlns:mx="http://www.adobe.com/2006/mxml">

<! template selection >

<mx:Labelid="lblTemplates"

Trang 17

y="{txtVariables.y+txtVariables.height+4}"

borderStyle="none"

dataProvider="{parentApplication.model.template ➥ templateVariables}" />

In these updates, I’m binding the text properties of the TextInput and TextArea components, as well

as the dataProvider properties of the ComboBox and List components, to the appropriate properties

of the template ObjectProxy, and I’ve set what action I’d like to take place when a user updates part

Trang 18

of the template, as well as what happens when a user clicks the Revertor Savebuttons Now that Ihave a sense of how I want the data model to be tied to the design and what path I want the top-levelinteractions to follow, I can move on to adding these methods to the Controller class.

Remember, the Controller class is nothing more than a traffic controller, basically receiving eventsand passing them on to the appropriate service class Here are the additions for the Communicationsclass:

//////////////////////// communication methods \\\\\\\\\\\\\\\\\\\\\\\\

public function getTemplates():void{ _svc.getTemplates(); }

public function loadTemplate(event:Event):void➥

public function cancelEmailChanges(event:MouseEvent):void➥

{ _comm.cancelEmailChanges(event); }public function blastNetwork():void{ _comm.blastNetwork(); }public function getPolicies():void{ _svc.getPolicies(); }

public function updatePolicy(event:FocusEvent):void➥

Building the Communications class

Between Controller and ICommunication, I have a very clear picture of what methods I need to buildinto Communications Now, I have to think about exactly how these methods need to work On thesurface, I want and need them to do what their names indicate Keeping this in mind helps me stayfocused as I’m usually coding three-to-four files at once Here’s my first pass on the template-relatedmethods in Communications.as:

/////////////////////// email template methods \\\\\\\\\\\\\\\\\\\\\\\\

public function loadTemplate(event:Event):void{

_selectedIndex = event.target.selectedIndex;

if(_templateEdited == false){

Trang 19

_view = event.target.parentDocument;

var id:uint = event.target.selectedIndex;

var tmpObj:Object = _model.templateDP.getItemAt(id);

_originalTemplate = _model.templateDP.getItemAt(id);

_model.template = new ObjectProxy(tmpObj);

} else {

Alert.show('You have unsaved changes Do you wish to save your➥

changes?', 'WARNING:', 3, null, _editCloseHandler);

}}private function _editCloseHandler(event:CloseEvent):void{

var tmpObj:Object = _model.templateDP.getItemAt(_selectedIndex);

_model.template = new ObjectProxy(tmpObj);

}public function updateTemplateSubject(event:FocusEvent):void{

_model.template.templateSubject = event.target.text;

if(_templateEdited == false){

_templateEdited = true;

}}public function updateTemplateBody(event:FocusEvent):void{

_model.template.templateBody = event.target.text;

if(_templateEdited == false){

_templateEdited = true;

}}public function saveEmailChanges():void{

if(_model.template.emailTemplateId == 0){

Alert.show('You must first select a template to edit before ➥

performing this operation.', 'User Error:');

} else {

Trang 20

_templateEdited = false;

}}public function cancelEmailChanges(event:MouseEvent):void{

if(_model.template.emailTemplateId == 0){

Alert.show('You must first select a template to edit before ➥

performing this operation.', 'User Error:');

} else {var id:uint = _model.template.emailTemplateId-1;

_svc.reloadTemplate(id);

_templateEdited = false;

}}public function reloadTemplate(id:uint):void{

var tmpObj:Object = _model.templateDP.getItemAt(_selectedIndex);

_model.template = new ObjectProxy(tmpObj);

_app.commMgr.templatesPod.selectedTemplate.selectedIndex = ➥

_selectedIndex;

}Let’s start with loadTemplate() This method is called every time the user makes a selection on theComboBox I store the selectedIndex property of the ComboBox so that it can be easily referencedfrom the other methods After that, I check for whether the template has been edited with_templateEdited If it hasn’t, I want to continue with the template loading; otherwise, I want to alertthe user that he has unsaved changes that will be lost if he continues on without saving first Thisinteraction is where _editCloseHandler() and _continueTemplateLoad() come into play WithupdateTemplateSubject() and updateTemplateBody(), I want to set the appropriate property of thetemplate ObjectProxy equal to the text that’s currently entered in the related text components andthen ensure that _templateEdited gets updated correctly For saveEmailChanges() andcancelEmailChanges(), I want to first check whether or not the user has selected a template to edit

If the form is still in the default state, I want to notify the user that no action will be taken while theform is in this state If a template has been selected, I either want to edit the template,saveEmailChanges(), or reload the template, cancelEmailChanges() Finally, reloadTemplate()works in conjunction with both saveEmailChanges() and cancelEmailChanges() and is the last stop

in a cancel or edit operation With Communications now in good shape, I can move on to my final tination before compilation, Services

des-Building the Services class

I use the same process as before, using Controller, IService, and now Communications as my guide

as to what to add to Services After completing that review, I know that all I initially need are servicecalls to retrieve all the templates and to send an edited template back to the server Here’s my firstpass at the required code:

public function getTemplates():void{

amfphp.call(_commService+'.getTemplates', _getTemplatesHandler, _➥

faultHandler);

Trang 21

}private function _getTemplatesHandler(result:Array):void{

if(result[0] == 'DEBUG_OUTPUT'){

trace(result.toString());

CursorManager.removeBusyCursor();

} else {var templates:Array = result.filter(_truncTemplates);

_controller.setAC('templateDP', ➥

_controller.makeArrayCollection(templates));

CursorManager.removeBusyCursor();

}}

private function ➥

_truncTemplates(element:*, index:int, arr:Array):Boolean{

return element.templateName != 'TOTAL FAILURE!';

}public function editTemplate(obj:ObjectProxy):void{

var tmpObj:Object = _controller.convertObjProxy(obj);

_amfphp.call(_commService+'.editTemplate', _editTemplateHandler, ➥

_faultHandler, tmpObj);

CursorManager.setBusyCursor();

}private function _editTemplateHandler(result:Array):void{

if(result[0] == 'DEBUG_OUTPUT'){

trace(result.toString());

CursorManager.removeBusyCursor();

} else {var templates:Array = result.filter(_truncTemplates);

Trang 22

private function _reloadTemplateHandler(result:Array):void{

if(result[0] == 'DEBUG_OUTPUT'){

trace(result.toString());

CursorManager.removeBusyCursor();

} else {var templates:Array = result.filter(_truncTemplates);

As you can see, the public methods are very straightforward All they really do is call the specifiedAMFPHP service, pass along any additional parameters as required, and signal the user that something

is happening with CursorManager.setBusyCursor() It’s the result handlers that really determine whatwill be experienced via the interface when using the app If the e-mail templates can successfully beretrieved, _getTemplatesHandler() will populate templateDP with that data, but not before strippingout the noneditable template for a total system failure _editTemplateHandler() is very similar

in how it operates, only adding an alert prompt notifying the user that his edit was successful andalso reloading the edited template Finally, _reloadTemplateHandler() is exactly like_editTemplateHandler() except for the lack of an alert prompt to the user All that’s left to do now

is flesh out the Blast and Terms subviews using the same process that I did for the Templates subview.The MXML and ActionScript required for the Blast and Terms subviews is so similar to the code imple-mented for the Templates subview that elaboration of it here would just be redundant However, I willtake a moment to discuss how to integrate this portion of the Flex app with a simple but robust PHPe-mail system on the back end

Extending to the inbox

The RMX admin control panel provides both network and group admin users the ability to send bothplain text and HTML e-mails to the entire network via the Blast subview When an admin user pressesthe Sendbutton, his message is packaged in an Object containing the subject, message, and intendedaudience and is then sent via AMFPHP to the server, where the real magic happens Here’s the codethat processes that object:

function blastNetwork($arr){

foreach($arr[0] as $key=>$value){

$$key = $value;

}if($blastAudience == ''){

$return = array('Error, message not sent.', 'STATUS: Failure');

} else {if($blastAudience == 'All Members'){

$return = $this->sendMail($blastSubject, $blastBody, true);

Trang 23

} else {

$return = $this->sendMail($blastSubject, $blastBody, false);

}}return $return;

}//helper function for blastNetworkfunction sendMail($subject, $message, $toAllMembers){

set_time_limit(0);

if($toAllMembers){

$query = "SELECT * FROM tbl_members";

$return = array('Message sent to all members successfully.', ➥

'STATUS: Success');

} else {

$query = "SELECT * FROM tbl_partners";

$return = array('Message sent to all partners successfully.', ➥

}The first method, blastNetwork(), processes the object sent in from Flex using a for each loop.After that, it’s time to see to whom the message needs to be sent In either case, I pass the subject andmessage on to the private sendMail() method along with a Boolean to indicate whether to blastmembers or partners Once the torch is passed to sendMail(), I set the script timeout limit, and then

I grab the proper data based on the message audience After that step, I run the proper query, andthen use a while loop to step through the result set and send out the message To actually send themessage, I will integrate Daniel Käfer’s eMail class (www.danielkaefer.de) This nifty little class takescare of properly constructing a multipart e-mail in the MIME format, allowing me to send text andHTML messages by simply setting the $text and $html variables of the eMail class Once the messageshave been sent, the proper response will be returned to Flex and displayed to the user Having imple-mented that part of the communications system, it’s now time to compile the Flex app Figures 9-17through 9-19 illustrate the compiled result of the first passes on the Communications screens

Trang 24

Figure 9-17 Templates subview, first draft

Figure 9-18 Blast subview, first draft

Trang 25

Figure 9-19 Terms subview, first draft

Now the Communications interface is successfully wired to both the Flex code and the PHP back-endcode via remoting This milestone constitutes a solid code foundation that I can branch out from andrefactor in a nondestructive manner This workflow can be followed for the other managementscreens as well Once the entire app is complete, usability enhancements can easily be added, as well

as custom styling that will really bring the app to life The lessons that you learned in Chapter 5 on Flexstyling definitely become applicable at this point But for now, take a moment to catch your breathand regroup before forging on, because you deserve it

Summary

In this chapter, you learned about the process that I used to meet the user communications systemrequirements for the RMX You saw one approach to constructing the actual MXML layout by usingdata binding You also explored my simplified version of the Model-View-Controller design patternand how using such a process can help to increase your productivity Finally, I showed you the fruits ofall that labor by bringing those wireframes to life with the help of the Flex framework and PHP In thechapter that follows, you’ll learn about how we work with video in the RMX

Ngày đăng: 14/08/2014, 11:21