ActionScript Interfaces with custom namespaces - apache-flex

Is there any way to get interface to play along with custom namespace? Example follows.
IHeaderRenderer.as:
public interface IHeaderRenderer{
function set header(value:IHeader):void;
function get header():IHeader;
}
HeaderRenderer.as
import fi.test.internalNamespace;
public class HeaderRenderer implements IHeaderRenderer{
internalNamespace function set header(value:IHeader):void{
// do something
}
internalNamespace function get header():IHeader{
// do something
}
}
This gives you the basic compiler error:
1044: Interface method get header in namespace fi.gridutils.headerrenderers:IHeaderRenderer not implemented by class fi.gridutils.headerrenderers.implementation:HeaderRenderer.
Why is this needed, you might ask. I'm developing a component, where the header accessors should not be directly visible to the components end user (developer), but if the developer wants to create his own Renderer he should know that they are needed. This is because the parent component will use these accessors to give the custom renderer the data it needs to render the header correctly.
Now to my mind there seems to be only three choices:
1) use public access control. This has the setback that the end developer will see accessors, which should not be directly accessed by him. Plus they add unnecessary clutter as they appear in auto-complete.
2) do not use interface. This means the end user has pretty poor options of developing the component further.
3) use the interface, but omit the accessors that use internalNamespace. Now the end developer will not know that he should add also header accessors to his custom headerrenderer class, which ends up in Flash Player giving following error to the developer in runtime:
Cannot create property internalNamespace/::header on fi.gridutils.headerrenderers.implementation.HeaderRenderer.
Sorry for all the blabbing. Any cunning ideas how this kind of situation could be handled?

In ActionScript, the interface methods need to be public. What good is an interface, if you can't guarantee the component using it can access the relevant interface methods?
that said, you can use the exclude metadata to prevent properties from showing up in code hinting.
Something like this:
[Exclude(name="header", kind="property")]
More info

Related

How to use Resource bundle(Client Bundle) in JSNI?

I am trying to use client bundle of css resource in JSNI but couldn't find any helpful code.
I want to know how to call css resource in JSNI and how exaclty to use client bundle in JSNI.
My Normal code:--
Resource.INSTANCE.button().ensureInjected();
Resource res = GWT.create(Resource.class);
button.setStyleName(res.button().gwtbutton());
How to write in jsni?
Here Resource----> is interface extend clientbundle.
gwtbutton ---> is css.
I have read in this link https://groups.google.com/forum/#!topic/google-web-toolkit/94v4tjCZiBo that we can use the CssResource through jsni. But I'm unclear about his usage its syntax.
There's no magic here (OK, there is a bit, but behind the scenes). Your Resource interface's implementation is automatically generated by GWT, so when you call res.button().gwtbutton(), it will return a proper name for the style you are referencing from a CSS file. This name could be obfuscated, with a prefix, etc. It doesn't matter to you - in the end it's just a string, a style's name. Just pass it as a String to your JSNI method:
public native void addStyle(String style) /*-{
// Add style to a DOM element
}-*/;
// Invoke like so:
addStyle(res.button().gwtbutton());
If you want to use your ClientBundle directly in JSNI code:
// First, get Resource instance from a static field
var resource = #package.Resource::INSTANCE;
// Then, get the CssResource with the style
var button = resource.#package.Resource::button()();
// Finally, get the style's name
var gwtButton = button.#package.Button::gwtButton()();
Substitute package.Resource for the proper qualified name (package + class name) of the Resource interface. Use the code completion offered by Google Plugin for Eclipse if needed. Do the same for the type returned by the button method (it's probably some interface extending CssResource).
If you are troubled by the syntax, please dig through the documentation or look into some tutorials on Java Native Interface - it shares a lot of similarities with JSNI and might be better documented.

Getting ahold of the Application to call its method

In my Flex 4.5 application I have a TitleWindow Settings.mxml, which is popped up by the PopUpManager.
Once the user has changed some settings, I not only need to save them to a SharedObject, but also to apply them to the main Application itself - so that the changes are visible to the user immediately.
For example I need to call its method hideApp(somevalue);
The spark.components.Application does not seem to have any static/singleton methods to get ahold of it.
So how do you do it?
And I also wonder how to declare, that an MXML file implements one or several interfaces?
package {
public interface Hiddable {
function hideApp(value:Number):void;
}
}
I'm asking this, because besides the main Application I have a SettingsTest.mxml Application in my project for "unit testing" that particular functionality.
Thank you! Alex
Yes it does:
FlexGlobals.topLevelApplication
though I would recommend you use events to avoid tight coupling.
As for the question about interfaces: use the attribute implements
<s:Component ... implements="IClassA,IClassB" ... />
About implementing of interfaces in MXML components see the following documentation.
What about passing changed data back from your pop up window to the application I recommend you to use Observer pattern with Flash event model something like the following:
var myWindow:MyWindow = MyWindow(PopUpManager.createPopUp(this, MyWindow));
myWindow.addEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
private function myWindow(event:MyWindowEvent):void
{
// Unsubscribing from events
var myWindow:MyWindow = MyWindow(event.currentTarget);
myWindow.removeEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
// Changed data is passing with custom event object
someData = event.someData;
}
And you should implement your custom event for that (MyWindowEvent in my pseudo code) and fire it from your TitleWindow component. You can read more about implementing custom event in documentation.

Setting the WebServiceWrapper endpointURI at run time

I'm in the middle of switching from Flex Builder 3 to Flash Builder 4, and one of the problems I have run into is that support for web services in 4 is substantially different. In both IDE's I am able to import a WSDL for my web service and it will generate the appropriate client classes for communicating with the service. The generated code in each is different.
In my Flex3 code I was able to access the endpointURI property of the mx.rpc.soap.AbstractWebService, but in the Flex4 code that is generated, the new class extends com.adobe.fiber.services.wrapper.WebServiceWrapper which does not have the endpointURI property.
My project has mulitple game servers and the player picks which server they want to play on. In the past if the player wanted server 1, I would set the endpoint URI to http://game1.server.com/service.asmx, and like wise if they wanted server 2 I would set the endpoint to http://game2.server.com/service.asmx.
What am I looking for to accomplish this in Flash Builder 4?
Short Answer:
var s:ClassThatExtendsWebServiceWrapper = new ClassThatExtendsWebServiceWrapper;
s.serviceControl.endpointURI = 'http://service.com/service.asmx';
Long Answer:
Well I finally found a solution. Adobe seems to have made this much harder than it should have been.
Web Service classes that are generated by Flash Builder 4 extend the com.adobe.fiber.services.wrapper.WebServiceWrapper. WebServiceWrapper has a property called serviceControl that can be used to control the service. The problem is that not all the members of serviceControl are accessible at the application code level. Lets assume that I have a web service called GameService. When I use the data tool to connect to the web service by providing a WSDL, Flash Builder will create two classes for me automcatically.
internal class _Super_GameService extends
com.adobe.fiber.services.wrapper.WebServiceWrapper
{ ... }
public class GameService extends _Super_GameService
{}
_Super_GameService contains all the automatically generated code to make calls to the web service. GameService contains no code itself, but unlike _Super_GameService, it is public. The idea here is that any enhancements that we need to make can be made to GameService, then later on if we need to update, _Super_GameService can be regenerated, but out changes to GameService will not be overwritten by the code generation tool.
Now this leads us to usage of these generated classes. Typically all I should have to do is create an instance of GameService and call a method on it. In this example DoSomethingAwesome is a method available on the web service.
var gs:GameService = new GameService();
var token:AsyncToken = gs.DoSomethingAwesome();
Now this will call the service using the URI of the service specified in the WSDL file. In my situation I wanted GameService to connect to a different URI. This should have been simple, but things fell apart.
My first problem was that viewing the documentation on WebServiceWrapper (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/com/adobe/fiber/services/wrapper/WebServiceWrapper.html) did not render properly in Firefox. So when I was reading the documentation I wasn't getting the full picture. This really needs to be fixed by Adobe.
Viewing the documentation in another browser helped me find out about the serviceControl property of WebServiceWrapper. serviceControl is declared as a mx.rpc.soap.AbstractWebService. AbstractWebService does have an endpointURI property which makes the following code valid.
var gs:GameService = new GameService();
gs.serviceControl.endpointURI = 'http://game1.service.com/GameService.asmx';
The other problem I had is that for some reason the endpointURI property of serviceControl does not appear in the Intellisense context menu. So since I didn't see serviceControl in the online documentation at first, and I didn't see endpointURI in intellisense, I didn't realize the property was there to be set.
If you look at the source for AbstractWebserivce, (http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/frameworks/projects/rpc/src/mx/rpc/soap/AbstractWebService.as) there doesn't seem to be an Exclude tag to explain why endpointURI does not appear in the Intellisense context menu. So I don't know what is going on there.
You should be able to override the endpointURI on the WebService. But I'm not sure where to do that with the generated code since I use <s:WebService/>.
This is the only way I could get it to work, in the generated stub for your service:
import com.adobe.fiber.core.model_internal;
Also:
/**
* Override super.init() to provide any initialization customization if needed.
*/
protected override function preInitializeService():void
{
_needWSDLLoad = false; // to prevent loading the default WSDL
super.preInitializeService();
// Initialization customization goes here
wsdl = "http://localhost/yourservice?wsdl";
_needWSDLLoad = true;
model_internal::loadWSDLIfNecessary();

Extending Flex FileReference class to contain another property

I want to extend the FileReference class of Flex to contain a custom property. I want to do this because AS3 doesn't let me pass arguments to functions through event listeners, which makes me feel sad, so I need this property to exist on the event target, so I can access it.
I also want to be able to cast extant FileReference objects to this class without any fuss. I have:
var fr:SmxFR = e.target as SmxFR
and I want that to work; right now it just returns null.
A blank, newly instantiated SmxFR object has the extended property in place, but all of its inherited properties and objects return Error: Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.
This is the class I am using, SmxFR.as:
package
{
import flash.net.FileReference;
public class SmxFR extends FileReference
{
public var housenum:String = "";
public function SmxFR()
{
super();
}
}
}
Kept it as straightforward as I could, really. Can someone please help me figure this out? Thanks.
Edit:
Per request, this is the instantiation which results in the aforementioned error in all inherited objects:
var fr:SmxFR = new SmxFR();
I get living handle property from that, and all other (that is, inherited) properties throw Error #2037.
So, maybe what I want to do is going to require overriding FileReferenceList? If the original objects must be instantiated to SxmFR, that's what I'll have to do, since I'm using FRL to allow the user to select multiple files at once. Are you guys sure there is no way to fast from a FileReference to my class?
You can totally pass objects via event listeners, it's just done in a specific way. I'd learn to do it correctly, rather than trying to extend a core library which could cause you problems later if you make a small mistake.
My solution: instead of extending FileReference, extend Event and add your properties to that.
var myEvent:MyExtendedEvent = new MyExtendedEvent();
myEvent.myCustomProperty = myValue;
dispatchEvent(myEvent);
Then in your handler you just write:
function myEventHandler(e:MyExtendedEvent):void {
trace(e.myCustomProperty);
}
Much more painless to go down this road! The added benefit is that if any other Flash Developer anywhere ever looks at your code they're not going to get hit in the face with a non-standard customized FileReference. :)
When e.target is instantiate as FileReference you can't cast it to SmxFR because it's not in the line of inheritance. In the other way you can a SmxFR Object to FileRefernce.
Extending FileReferenceList is not going to be helpful. FileReferenceList.browse() method creates an array of FileReference object when user selects multiple files - that happens internally (may be in its private methods) and you cannot change that behavior and force it to create SxmFR objects instead. Use custom events as Myk suggested.
This article talks about Sound objects, but may be that's applicable to FileReference objects too. May be you cannot reuse them. Post the code where you use the SmxFr class and get the said error.

React to change on a static property

I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.

Resources