Home > Articles > Web Development

This chapter is from the book

Understanding the Structure

Now, you have a clearer understanding of what the component life cycle consists of. The next step is to understand how it actually moves through these phases and what each of the component methods actually does. After you have a clear understanding of that, you can quickly create the core structure of your component without thinking about it. However, to avoid running before you can walk, you need to know what you can harness within each method, so you can place your component code in the correct place to avoid potential pitfalls.

The obvious place to start is the component constructor. Although not strictly part of the component life cycle, it is the initialization point of all classes—including components—by invoking the class's constructor via the new keyword. Don't skip the constructor section, though; you need to adhere to some specific rules that differ from a standard ActionScript class. That said, let's see what those differences are.

Component Constructor

The component constructor is slightly different from normal class constructors because you don't create it with any parameters. There are a few good reasons for this. First, your component is more likely to be declared in MXML, where you cannot pass parameters into the constructor of your component. Second, if another component instantiates your component purely by interface or by base class type (UIComponent, for example), there is no way for you to cleanly set those parameters. Although you could provide these parameters and simply give them all default values, as the following example shows, this is a poor design approach that's better handled within the constructor and associated component methods via the components properties and accessors/mutators (getter/setters).

private var _property1:Number;
private var _property2:Number;

// An incorrect implementation of the property
// variables within the component constructor.
public function MyComponent(property1:Number=100, property2:Number=100)
{
  _property1 = property1;
  _property2 = property2;
}

// The correct approach
public function MyComponent()
{
  _property1 = 100;
  _property2 = 100;
}

As you can see, the second example sets the values of two variables when the component is instantiated. This is the preferred method by which you would assign internal values to your "startup" variables. Now this may sound all well and good for internal values, but what if you want to provide external values and have those applied to your component during the startup phases? Well, again, the Flex component framework provides us with a method to achieve this, commitProperties(), which is covered in a bit. For now, remember that the constructor is where you instantiate your variables with internally defined values.

After you have these values assigned, you need to know that they can be accessed. The Component framework takes care of this by dispatching a preinitialize event when the constructor finishes processing. It also does a few things in the background: It calculates any style values that have been assigned to your component and adds a reference to its parent. For example, if you placed this directly into your main MXML file, it would set this property to be a reference to Application. If it were placed inside a Panel component, it would be a reference to that.

Now, even though you should assign your variables needed for the initial startup of your component in the constructor, a few items shouldn't be declared here—namely anything that needs to be added to the DisplayList at startup. These objects need to be instantiated and added in a method called createChildren().

createChildren()

The createChildren() method is invoked directly after the constructor, after the component is added to the parent container via the addChild() method, be that implicitly by declaring the component in MXML within a parent tag or explicitly by using the parents addChild() method in ActionScript. Note that, in the case of container components that inherit from Group, you need to use the addElement() method instead of addChild(), because Group and its subclasses don't support addChild(). This is because they also support the adding and displaying of graphical elements that have been declared in FXG and those components that inherit from UIComponent or one of its subclasses. We look at FXG in Chapter 10, "Skinning and Styling."

The createChildren() method is where you should instantiate and add all your display objects required to get your component up and running with its default values and views. Like the constructor, the createChildren() method is invoked only once during the entire life cycle of your component, so it is ideally suited for the creation and adding of display objects that persist because you need to worry about this process only once. A good approach to take with the createChildren() method is if you have to create drawn objects as part of the initialization process of your component, separate these into individual methods and just invoke these during the createChildren() phase. This has two benefits: It keeps your code clean, and it enables a level of separation, so if you want to draw or process something again after the initial setup phases, you can easily access these methods.

There are a few exceptions to this rule of using createChildren() to add all your display objects to your component. What if you need to add something to the display that exists only if the user clicks or rolls over a bit of your component? Do you create those elements here or is there an alternative? I tend to create only those display items needed and use additional methods to create items when required and add them to the DisplayList at that point; but, as I said, you can do that or instantiate the objects in createChildren(), but don't add them to the DisplayList until you need them. Either way, keep in mind that you need to take the stack index of your children into consideration to ensure that they aren't obscured or masked from input when they finally get added; each item added to a particular DisplayList is effectively placed above the previous one (like a stack of magazines, the oldest being at the bottom and the most recent addition at the top).

commitProperties()

The commitProperties() method deals with any updates or alterations to the values within your component that have been influenced by external means; for example, setting the value of an attribute in a component's MXML tag like this:

<mx:Button label="Hello World" />

Here, the button's default value for its label property of Button has been replaced with my Hello World (I know, not original). What actually happens is that the value gets set within the Button component, which then triggers a request to the component framework to recommit the properties of the button with the new value. To do this, the button invokes its invalidateProperties() method, flagging that it needs to be updated in the next render phase because something has changed. When the next render phase happens, the commitProperties() method is executed and the value of the label property is set to Hello World.

If any of the display objects have also been altered or resized, additional invalidation calls may be required, so each one is flagged and they are all batched up to be invoked in turn within subsequent render phases. In the previous example, Hello World is longer than Button, so after the Button has assigned the new label value, it also calls the invalidateSize() method because the component needs to make sure it is still the correct size. The invalidateSize() method in turn calls the measure() and/or updateDisplayList() method.

measure()

The measure() method is slightly different from most of the other methods within the components life cycle. By this, I mean that it is invoked only if the width and height of your component are not provided with an actual value. Therefore, if you provide a width and height for your component when you place it in your application, measure() is completely ignored by Flex, even if you invoke the invalidateSize() method because Flex doesn't need to work out what size your component is because it is already defined.

For those of you who want to know what it actually does, here is the deal: When the measure() method is invoked, it checks to see whether the explicitHeight and explicitWidth properties return NaN (Not a Number); if they do, it then executes whatever code it contains to calculate the actual width and height of your component. Now, you may be rereading the previous sentence at this point; where did this explicitHeight and explicitWidth stuff come from? Well, the Flex framework has an extended selection of height and width properties, and all of them perform slightly different roles. To help clarify what each does, they are all listed in Table 3.1.

Table 3.1. Various Size Properties Within the Flex Framework

Type

Description

Notes

explicitHeight

explicitWidth

explicitMaxHeight

explicitMaxWidth

explicitMinHeight

explicitMinWidth

explicitHeight

explicitWidth

The explicit sizes are used by the components' containers, not the component, and they relate to various values, such as the actual, minimum, or maximum size of that component.

Used by the parent container to calculate both the size and position of your component within itself.

The component's container uses these values to calculate its size. If the parent of your component is a container-based component, these values are unlikely to be factored.

Also, all these values are based on the component's own coordinate system and are affected by scaling when applied in relation to their parent. Therefore, dimensions may not return identical values from the respective coordinate system.

maxHeight

maxWidth

minHeight

minWidth

Used by the parent container to calculate both the size and position of your component within itself.

The max and min size values are similar in nature to the explicit values.

These values are also affected by scaling when applied in relation to their parent and, as such, may return different values as calculated by their respective coordinate systems.

percentHeight

percentWidth

Enable you to define a percentage value (0–100) for your component.

If you set the height, width, explicitHeight, or explicitWidth, these values are reset and ignored.

measuredHeight

measuredWidth

measuredMinHeight

meaasuredMinWidth

The properties you use to define the default sizes for your component. They are the only properties to do with sizing you should use in your measure() method.

You do not need to set any of these externally because they are automatically set via the width and height properties.

height

width

The properties that you use to dictate the size of your component externally.

When either is set, it causes a resize event to be dispatched, which eventually results in your component recalculating its dimensions, if applicable.

Although you can place a percentage value in the MXML width (width="100%"), you cannot do this in ActionScript. You need to use the percentHeight or percentWidth if you want to achieve the same result.

As you can see, only a couple of them are actually used by your component as a basis for size calculation by the component itself. The others are part of the framework and, for the most part, happily trot alongside your component. I will point out the percentHeight and percentWidth attributes, however, because these are not exposed within MXML. This is because the width and height attributes of MXML are clever enough to work out the data type when it is applied and, therefore, enable you to set a percentage value for that property. After all, every attribute in MXML is basically a string. ActionScript, on the other hand, isn't as forgiving, so if you want to set percentage values for your components in ActionScript, you need to use the percentHeight and percentWidth properties as required. I know that was a bit of a slog, but you'd have come across all those different sizing options at some point if you hadn't already, so it was worth getting it out of the way where it has some relevance.

Given all that preceding information, how do you actually set the size of your component? Well, you have a few options. First, you can obviously set the default width and height of your component. If you don't supply a value for either or both of these, they default to 0, effectively making your component invisible. I personally wouldn't use this as a mechanism to set the visibility of your component because you are likely to have child components that may size incorrectly if they are relying on their parents' dimensions. The second thing you can do is set the minimum width and height of your component; this has the benefit of letting Flex know that you don't want your component to go below a certain size—this is especially useful if you have a complex layout that doesn't degrade gracefully or just becomes completely unusable if it goes below a certain size.

override protected function measure():void
{
  measuredHeight = 200;
  measuredWidth = 200;
  measuredMinHeight = 25;
  measuredMinWidth = 100;
}

Be warned: This isn't set in stone, and there are certain instances in which external factors can override and even ignore this.

For the most part, you are likely to set these properties to the same value, as the following code shows. Either way, the main use of the measure() method is to set the default dimensions of your component so that another developer doesn't need to provide initial values just to get your component to display in his application.

The upshot is that these values are used when you drop a component from the Component Panel within Flex Builder onto the design view. The vast majority of the control components display at a default size for that specific component type as defined in their respective measure() methods.

override protected function measure():void
{
  measuredHeight = measuredMinHeight = 200;
  measuredWidth = measuredMinWidth = 100;
}

Now that we looked at resizing our component with measure(), let's look at updating those visual elements that may have been altered by resizing your component through external influences, such as if a developer supplies a specific width and/or height or via its children.

layoutChrome()

It is worth pointing out that layoutChrome() is a Halo-specific method. It has no use or equivalent within the Spark framework because of the life cycle Spark components have for their skins—something that you'll get a better understanding of in Chapter 10. With that in mind, I included it in this chapter for completeness; if you are interested in only developing components based on the Spark framework, you can skip this section. For those who want to work with the older Halo component framework (and some components are still provided only as Halo versions), read on.

The concept behind the layoutChrome() method is that container styling and visuals should be kept separate from content. For the most part, there isn't a huge need to have them separated because you can manage their updating together and everything is fine. However, if the container's autoLayout property is set to false, you could have a problem because when autoLayout is set to true, recalculation of the size and position of your component, and all its children, is performed whenever a child component's position or dimensions change. So, if the container changes size, or its contents do, they can both expand or contract to accommodate the changes if that is how they've been configured. That's why this is the default value in the vast majority of Flex container components.

If autoLayout is set to false, Flex updates only the measurement and/or the position when a child is added or removed—not too helpful for our border if it were clumped in with all the other layout code because it could cause unforeseen layout issues if it updated itself only when this happened.

Fortunately, Flex continues to invoke the layoutChrome() method even if autoLayout is set to false. So, as long as you put all your container's chrome within the layoutChrome() method, it updates correctly whenever the position or size changes, regardless of whether the rest of the content is set to update. But don't be tempted to throw all your visual element controls into a layoutChrome() method just for the sake of it. Most components don't require this because they update themselves and their children all at once; however, if you create a container-style component that may or may not be based on a template component, this process comes into its own.

updateDisplayList()

You've seen how to apply values to your component's properties and set the size, and for Halo-based components, you know how to manage the chrome of a container. The last core component method makes sure everything is cleaned up and ready to go because it is in charge of updating the display status of your component: updateDisplayList().

The updateDisplayList() method is the final step before your component actually appears within your application; it is responsible for setting any child component properties, sizing, and positioning elements as required. In addition, it is responsible for applying any graphical style elements, including any nonchrome skins. Also, at this point, the parent container calculates the component's final size, if it has been set externally and/or altered by its children, before setting its visibility to true.

For a method that does so much, it has a short description, I know. There is one more thing that you need to know about updateDisplayList(), and it may have already been sitting at the back of your mind while you were reading about measure(). If measure() is invoked only if you don't set the width and height, how do you set them after you have set your component's initial size? Well, it's probably no surprise that you do that here, within updateDisplayList(). Of all the core methods, updateDisplayList() is the only one that accepts parameters, as you can see in the following code line. But, like all the preceding methods, it shouldn't be directly invoked by you, the developer; this should be left to the Flex component framework via the relevant invalidation call.

updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void

These two values are passed to the component as part of the invalidateDisplayList() call and are set to the component's coordinate system, not the parents. The reason for this is that unscaledWidth and unscaledHeight values do not include any scaling, as the name implies, that may have been applied to the component. So, aim to use these values when performing update calculations that affect your component and its children, not width and height, as you might think as these do have the scaling modifiers applied.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020