ObjectBuilder

(This tag must be placed inside an <EventHandlers> tag, <MessageHandlers> tag or <Injectors> tag)

When placed inside an EventHandlers block, and the list of handlers is executed, it will create an object of the class specified in the "generator" attribute. You can pass arguments to the constructor of this class that come from a variety of sources, such as the event itself, a server result object, or any other value. Unless you specify cache="false", this object instance will be "cached" and not instantiated again when using the MethodInvoker or PropertyInjectors

Example:

<ObjectBuilder generator="ClassNameToInstantiate"
   constructorArguments="{['argument1','argument2']}" />

The above example would be the same as doing the following in ActionScript code:

var myObject:ClassNameToInstantiate = new ClassNameToInstantiate('argument1', 'argument2');

Attributes

generator

required

The generator attribute specifies what class should be instantiated.
Suppose you have a class called "MyClass" in the package com.yourdomain.business. You can specify a complete path to com.yourdomain.business.MyClass:

<ObjectBuilder generator="com.yourdomain.business.MyClass" />

Generally you may want to use a binding to specify the class name. Assuming you have an import statement like this in your Event Map:

import com.yourdomain.business.MyClass;

or simply:

import com.yourdomain.business.*;

You can then instantiate your worker using bindings:

<ObjectBuilder generator="{MyClass}" />

The advantage of using this syntax is that if you are using Flex Builder, you can press the command key (Mac) or the Ctrl key (Windows) and click on the generator class (MyWorker in the example) and it will take you to the class definition.

constructorArguments

Array

If the constructor of you class requires arguments, you can pass them via the "constructorArguments" attribute. Suppose your constructor has the following signature:

public function MyClass(name:String, value:Number)

then instantiating the class as follows will work:

<ObjectBuilder generator="{MyClass}"
   constructorArguments="{['Tom', 36]}"/>

Note that the constructorArguments attribute expects an array. Besides passing literal values, you can pass values coming from the event that triggered the handlers list:

<ObjectBuilder
   generator="{MyClass}"
   arguments="{[event.userName, event.age]}"/>

This assumes that the event contained a userName property and an age. See Passing Arguments for more information.

cache

either "global", "inherit", "local" or "none"

The cache attribute lets you specify whether this newly created object should be kept live so that the next time an instance of this class is requested, this already created object is returned instead. The instance can be requested by a MethodInvoker or a PropertyInjector.

For example, you may want to have a MethodInvoker use an already instantiated instance created by an ObjectBuilder.  Since the default value for this attribute is "inherit", it will do that by default. On the other hand, if you wanted to have two different instances, then you must set this attribute to "none".

The value "inherit" will globally cache the object in a normal EventMap and it will locallly cache the object in a LocalEventMap. If you want to cache globally in a LocalEventMap so that the object can be accessed from other event maps, set the value to "global".

<EventHandlers type="myEventType">
   <ObjectBuilder generator="{MyClass}" />
</EventHandlers>

<EventHandlers type="myOtherEventType">
   <MethodInvoker generator="{MyClass}" method="doWork" />
</EventHandlers>

Inner tags

Properties


You can add properties to your instantiated object by using the Properties tag inside the ObjectBuilder tag. These properties must be public.

Suppose you are creating an instance of a ShippingCalculator class. This class has a property called weightFactor and flatFee. In order to set those two properties, you can use the <Properties> inner tag. As attributes of the Properties tag, you can specify the names of your properties and set the values of those properties by setting the value of those attributes as follows:

<ObjectBuilder generator="{ShippingCalculator}">

   <Properties weightFactor="0.5" flatFee="3" />
   
</ObjectBuilder>

Besides specifying literal values, you can assign values coming from the event that triggered the sequence:

<ObjectBuilder generator="{ShippingCalculator}">

   <Properties weightFactor="{event.factor}" flatFee="{event.fee}" />
   
</ObjectBuilder>

This assumes that the original event contained a factor property and a fee property.

Other sources can include service results or faults, values returned by a MethodInvoker, etc.

15 responses

  1. Would be nice to have a sourceCache property on the <properties> tag of <ObjectBuilder> since you can't directly reference globally instantiated objects and insert them into properties when using <ObjectBuilder> in a <LocalEventMap>. Of course there are ways around this, but they are less than elegant.
  2. I can't figure out which TAG to use in order to display TitleWindow when a specific event occurs. Tried this

       <EventHandlers type="{PopupLoginForm.SHOW}" debug="true">
       
          <InlineInvoker method="showLoginScreen();" />      
                
       </EventHandlers>

    with

    private function showLoginScreen()
    {
       var login:LoginScreen = LoginScreen(PopUpManager.createPopUp( this, LoginScreen , true));
    }

    declared in a script block. Did work.

    What do I do wrong, please?

    Thanks,
    Sergei.
  3. Correction to my post of February 14, 2010 at 7:04 PM said. It should read

    "Did not work" instead of "Did work"

    Sorry.
  4. It seems to be an error here in the article, "arguments" is written instead of "constructorArguments":

    <ObjectBuilder
    generator="{MyClass}"
    arguments="{[event.userName, event.age]}"/>

    Thanks,

    Rost
  5. what if i wanted to create two instances of the same class? how can i reference it for example in PropertyInjector and point a specific instance of the class?

    thanks,
    euge
  6. euge,

    First set cache=none when you create them. Create the first then use DataCopier to copy the lastReturn into data.instance1. Then create the second and DataCopier lastReturn to data.instance2.
  7. hi matt,

    great thanks!
  8. If you once run an ObjectBuilder the object instantiated will be cached and next time you use an PropertyInjector or something else, the instantiated object will be used instead of creating a new object.

    But in the following example it seems that mate tries to instantiate the object again:
    <EventHandlers type="{FlexEvent.PREINITIALIZE}">
       <ObjectBuilder
          generator="{TerminalPresentationModel}"
          constructorArguments="{scope.dispatcher}" />
    </EventHandlers>

    // ... few lines later

    <Injectors target="{TerminalPresentationModel}">
       <PropertyInjector targetKey="date" source="{DateManager}" sourceKey="currentDate" />
    </Injectors>

    I get the following error:
    - ERROR: Wrong number of arguments supplied when calling the constructor
    - TARGET: TerminalPresentationModel
    - TAG: PropertyInjector
    - METHOD: constructor
    - FILE: TerminalMainEventMap
    - NO ARGUMENTS SUPPLIED

    When I'm debugging I notice that the TerminalPresentationModel is instantiated (with the dispatcher as constructor argument). The error occurs after the TerminalPresentationModel is instantiated.

    Any hints what's wrong with my code?
  9. Try this instead of your Preinitialize handler:

    <Injectors target="{yourUI}>
    <ObjectBuilder generator="{TerminalPresentationModel}"
    constructorArguments="{scope.dispatcher}"
    registerTarget="{true}"/>
    <PropertyInjector targetKey="{model}"
    source="{lastReturn}"
    sourceKey="modelSource"/>
    </Injectors>


    However, I typically don't add the dispatcher in the constructor, but rather inject into the presentation model in the injectors so that you can use the presentation model with different "yourUI" instances if you cache it locally.
  10. I am using Java REST service as a back end service. I need to create the url dynamically ie I need to pass a parameter value from Event like event.userId

    I am using <HttpInvoker instace"some method to get the HttpService">

    How to pass a parameter to above instance method. Please give me some sample code.

    Thansk
    Sri
  11. @Mark:
    Thank you very much, this was the problem :)
    Your hint about don't adding the dispatcher in the constructor is perhaps a better solution, but in my case not necessary.

    @Sri:
    http://mate.asfusion.com/page/documentation/tags/services/httpserviceinvoker
    Look at the request inner tag. This should solve your issue.
  12. @heiring and @mark
    I've encountered the same thing recently and my workaround is actually derived from Mark's suggestion (don't use constructor to pass the dispatcher reference). I'm not understanding 100% why but this works for me:

    1. Remove the constructor with dispatcher argument in TerminalPresentationModel
    2. Create a setter for mateDispatcher in TerminalPresentationModel
    3. Use this configuration

    <EventHandlers type="{ FlexEvent.PREINITIALIZE }">
       <!-- It is important to know that registerTarget has to be TRUE in order to reuse in Injectors
          and all the properties will be set later in Injectors -->
       <ObjectBuilder generator="{ TerminalPresentationModel }" registerTarget="{true}"/>
    </EventHandlers>

    <Injectors target="{TerminalPresentationModel}">
       <!-- set globalDispatcher reference here. You can add additional property injectors to make it like DI -->
       <PropertyInjector targetKey="mateDispatcher" source="{scope.dispatcher}" />
    <PropertyInjector targetKey="date" source="{DateManager}" sourceKey="currentDate" />
    </Injectors>

    <Injectors target="{SomeView}">
       <!-- reference here -->
       <PropertyInjector targetKey="presenter" source="{TerminalPresentationModel}" />
    </Injectors>
  13. Documentation says:
    "For example, you may want to have a MethodInvoker use an already instantiated instance created by an ObjectBuilder. Since the default value for this attribute is "inherit", it will do that by default."

    However:

    <mate:EventHandlers type="{FlexEvent.PREINITIALIZE}">
    <mate:ObjectBuilder generator="{ModuleControl}"/>
    </mate:EventHandlers>
          
          
    <mate:EventHandlers type="{ModuleControlEvent.LOAD}">
    <mate:MethodInvoker arguments="{[currentEvent.sModuleName]}" generator="{ModuleControl}" method="load"/>
    </mate:EventHandlers>
          
    <mate:EventHandlers type="{ModuleControlEvent.READY}">
    <mate:EventAnnouncer generator="{ModuleControlEvent}" type="{ModuleControlEvent.LOAD}"/>
    </mate:EventHandlers>

    package shared.controllers
    {
       
       import com.asfusion.mate.core.GlobalDispatcher;
       import shared.events.ModuleControlEvent;

       public class ModuleControl
       {
          public function ModuleControl()
          {
             trace("Hi there!");
             var oGlobal:GlobalDispatcher = new GlobalDispatcher();
             oGlobal.dispatchEvent(new ModuleControlEvent(ModuleControlEvent.READY));
          }
          
          public function load(sModuleName:String=""):void{
             trace("LOAD THE: "+sModuleName);
          }
       }
    }

    Creates an infinite loop because the methodInvoker creates a new instance rather than using cache.
  14. Couple things I would do:
    1. I would turn on debugger in tags.
    2. Would put a breakpoint in the ModuleControl constructor and then you can use the FlashBuilder debugger to look at the stack trace. Sometimes this will lead you to something else at the root, vs. what you think. If you expect global cache (which I beilieve is your scenario), you would only hit it once. If more times, then you get the stack trace each time to see where it started.
    3. I would comment out the dispatchEvent line, how many times do you hit the contstructor then?
  15. Is there any way to create an Object using the ObjectBuilder with an id property that is bound to another value?

    The following does not work since "id" is reserved:

    <ObjectBuilder generator="{Object}" cache="none">
       <Properties id="{event.list}" />
    </ObjectBuilder>

    This does not work either since event.list should be bound:

    <RemoteObjectInvoker instance="{jobService}" method="DeleteJob" arguments="{{id:event.list}}">

    Is there any other way to send an "id" to a server API?

Comments now closed