in main.mxml :
public function init():void
{
PopUpManager.createPopUp(this,login,true);
}
<mx:ViewStack id="vs" label="content" >
<local:FrontPanel id="Fpanel" />
<local:SlavePanel id="Spanel" />
<local:AdminPanel id="Mpanel" />
</mx:ViewStack>
In Login.mxml :
public function authenticateRH(event:ResultEvent):void
{
var replyMsg:String=event.result as String;
switch(replyMsg)
{
case "Master" :
here i want invoke Mpanel from Main.mxml
break;
case "Slave" :
here i want invoke Spanel from Main.mxml
break;
case "fail" :
Alert.show("Login Incorrect!!");
return;
}
}
How can i selectchild of main.mxml's Viewstack from login.mxml ?
I would use events (and interfaces), that way your forms don't need to know as much about one and other. It also gives you more control on what happens after your popup closes (IMHO).
Here is a little example (if I can get the code to paste correctly). This example does not use an interface that would help decouple things. I also use dynamic events that make prototyping faster but you will want to define a formal event.
simulate your main:
import mx.events.DynamicEvent;
import mx.managers.PopUpManager;
private function btnClick(e:Event):void
{
vs.selectedIndex = 0;
var myLogin:myPopup = new myPopup();
myLogin.addEventListener
(
"WAS_CLOSED",
function(e:DynamicEvent):void { vs.selectedIndex = e.byButton; }
);
PopUpManager.addPopUp(myLogin, this, true);
PopUpManager.centerPopUp(myLogin);
}
simulate your login:
import mx.events.DynamicEvent;
import mx.managers.PopUpManager;
private var eClose:DynamicEvent = new DynamicEvent("WAS_CLOSED");
private function closeMe(byButton:int):void
{
eClose.byButton = byButton;
dispatchEvent(eClose);
PopUpManager.removePopUp(this);
}
<mx:Button x="122" y="250" label="Thing 1" click="closeMe(1);" />
<mx:Button x="195" y="250" label="Thing 2" click="closeMe(2);" />
public function init():void
{
var popup:IFlexDisplayObject = PopUpManager.createPopUp(this, Login, true);
popup.addEventListener(Event.SELECT, onSelect);
}
private function onSelect(e:CustomEvent):void
{
if(e.item == CustomEvent.MASTER)
{
vs.selectedItem = Mpanel;
}
else if(e.item == CustomEvent.SLAVE)
{
vs.selectedItem = Spanel;
}
//remove popup
PopUpManager.removePopUp(IFlexDisplayObject(e.target));
}
//Login.mxml :
public function authenticateRH(event:ResultEvent):void
{
var replyMsg:String=event.result as String;
switch(replyMsg)
{
case "Master" :
dispatchEvent(new CustomEvent(CustomEvent.MASTER));
break;
case "Slave" :
dispatchEvent(new CustomEvent(CustomEvent.MASTER));
break;
case "fail" :
//remove popup
PopUpManager.removePopUp(this);
Alert.show("Login Incorrect!!");
return;
}
}
//CustomEvent.as
public class CustomEvent extends Event
{
public static const MASTER:String = "master";
public static const SLAVE:String = "slave";
public var item:String;
public function CustomEvent(item:String)
{
super(Event.SELECT);
this.item = item;
}
}
I'm assuming your problem is that you don't have a reference to your ViewStack while in your authenticateRH method. One way you can fix this is to give your login popup some more information after you create it:
public function init():void
{
var loginPopup:login = PopUpManager.createPopUp(this,login,true) as login;
loginPopup.setUsefulInformation(referenceToViewStack);
}
You'd have to add the setUsefulInformation() method to login.mxml. It's up to you what information you want to pass to solve the problem.
first, i believe you're creating your pop-up incorrectly. you seem to have the first two parameters switched. should be:
PopUpManager.createPopUp(login,this,true);
docs: http://livedocs.adobe.com/flex/3/langref/mx/managers/PopUpManager.html#addPopUp%28%29
second, you need to either explicitly give your Login class a reference to your Main class or if your Login class is on the same display chain as your Main class you can use a custom event with Bubbles set to true.
Related
I want to change Property Of Custom Component Popup From Main.mxml (Main Application)
My Custom Component is ->
public class PropertyPanel extends Panel
.....
in it I have
public function minimisePanel(e:MouseEvent):void{
effResize.heightTo = previousHeight;
effResize.widthTo = 200;
this.x = parentApplication.width - 320;
effResize.play([this]);
}
In Main Application I Called It As -->
private function AddPropertiesPanel():void{
var PropWindow:IFlexDisplayObject;
PropWindow = PopUpManager.createPopUp(this, Property_Panel, false);
/*Property_Panel is Property_Panel.mxml*/
}
And In Main Application I Want to -->
public function setCurrObj(event:TransformEvent):void{
/*Some Magical Stuff Required Here*/
}
Instead of storing the popup as a variable local to the function; store it as an instance variable on the Main.mxml class:
public var PropWindow:IFlexDisplayObject;
The AddPropertiesPanel() method will change like this:
private function AddPropertiesPanel():void{
PropWindow = PopUpManager.createPopUp(this, Property_Panel, false);
/*Property_Panel is Property_Panel.mxml*/
}
And then you can easily access properties on the panel instance in other methods:
public function setCurrObj(event:TransformEvent):void{
PropWindow.someProperty = someValue
}
//Base.as
public class Base
{
private var _foo:String;
[Bindable]
public function set foo(value:String):void
{
_foo = value;
}
public function get foo():String
{
return _foo;
}
/*
Many many setter/getter, methods, events
*/
}
//Control.as
public class MyControl extends Group
{
public function MyControl()
{
}
}
//Window.as
public class MyWindow extends spark.components.Window
{
public function MyWindow()
{
}
}
//Module
public class MyModule extends spark.modules.Module
{
public function MyModule()
{
}
}
I want to expose (friendly) Base properties, methods and events on the other classes. Something like this:
var window:MyWindow = new MyWindow();
window.foo = 'Hello World!';
var module:MyModule = new MyModule();
module.foo = 'bar';
<namespace:MyControl foo="Hello World!"/>
I don't want define all the properties in each class because they are many and the same for all of them.
Ideally would define something like:
public class MyControl extends Group, Base
{
public function MyControl()
{
}
}
(I know it can't be done.)
Thanks!
UPDATE:
Thanks again!
Maybe this clarify more my need... On business layer I have a variable called processID (and businessID, operationID, localityID, etc.) what be passed to Window from Menu, and Window passes it to Module. On Module Container, I have a CustomComponent what query database using this variable as parameter. This applied for all (almost) Components on Module. These variables are defined as level business layer, then I define a Class to store and manage these variables (and some related methods operating with these variables using business logic), so I can make a standalone class (or library) for every environment to reusing my common components. The idea is... insert a new CustomComponent and set these variables via mxml, like this:
<custom:MyCustomComponent id="zzz" processID="{processID}" businessID="{businessID}"/>
Module has the business logic for set (o not) any of the variables.
Otherwise, I would have to implement different logic for the CustomComponent (and Module) for read parent's variables and define these variables only in MyWindow (using composite pattern).
You can get your answer from following link -
http://flexinonroids.wordpress.com/2009/05/27/flex-3-dynamically-loading-components-at-runtime/
http://thecomcor.blogspot.in/2007/11/adobe-flex-dynamically-loading-classes.html
Or you can follow below approach -
1) Create an Interface as base
2) Extend your class with interface
3) Load class at runtime with SWFLoader.loaderContext.applicationDomain.getDefinition method
Thanks,
Varun
You can place your classes that require friendly access in the same package as your Base class, and define private fields without any access modifier( it is equivalent to internal modifier).
Otherwise, you can define your namespace like that:
namespace my_internal;
and then define class members like that:
my_internal var _foo:String;
after that, those members will be hidden for all code, except for code that contains
use namespace my_internal;
You can read more here:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9e.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f91
However, using 'friend access' can be an evidence of bad design, so if I were you I'd think twice before defining namespaces.
Update:
pseudo-superclass 1:
package proxy
{
public class Simple1
{
public var x:int;
public var y:int;
}
}
pseudo-superclass 2:
package proxy
{
import mx.controls.Alert;
public class Simple2
{
public var name:String = 'noname';
public function doAlert():void{
Alert.show(name);
}
//not normal method to replace 'this' with proxy
Simple2.prototype.doCrossClass = function doCrossClass():void{
Alert.show(''+(Number(this['x'])+Number(this['y'])));
}
}
}
Code for testing the result (looks as what you are expecting?):
var mega:Mega = new Mega();
mega.x = 100;
mega.y = 200;
mega.name = 'Multiple inheritance';
mega.doAlert();
mega.doCrossClass(); //300
And now pseudo-subclass with multiple inheritance:
package proxy
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public dynamic class Mega extends Proxy
{
public function Mega()
{
super();
}
public var superArray:Array = [new Simple1(), new Simple2()];
flash_proxy override function getProperty(name:*):*{
for each(var superClass:Object in superArray){
if( name in superClass){
return superClass[name];
}
}
throw new Error('no such property');
}
flash_proxy override function setProperty(name:*, value:*):void{
for each(var superClass:Object in superArray){
if( name in superClass){
superClass[name] = value;
return;
}
}
throw new Error('no such property');
}
flash_proxy override function callProperty(name:*, ...args):*{
for each(var superClass:Object in superArray){
if( name in superClass){
var f:Function = superClass[name] as Function;
return f.apply(this, args);
}
}
throw new Error('no such function');
}
}
}
You can also want to use javascript-like class construction(i.e. just using simple Object and assigning properties and functions to it in any combinations you want).
I've created a component which is basically a meter (extends skinnable container). The skin to the component contains two rectangles (background and the actual meter). This component receives an arraycollection with the following fields value, max and min(the arraycollection I am passing is Bindable). When I initialize and pass the data to be used to draw It works great (keep in mind - only works the first time). I've created a button that changes the data array and shows the current meter value. Apparently the meter's value is changing everytime I modify the ArrayCollection I've set to be used for rendering.(dont want to say "dataProvider" because it is not a Flex dataprovider, just a variable )... here is the code...
public class meter extends SkinnableContainer {
[SkinPart( required = "true" )]
public var meter:spark.primitives.Rect;
[SkinPart( required = "true" )]
public var meterBackground:spark.primitives.Rect;
private var _dataProvider:ArrayCollection;
private var _renderDirty:Boolean = false;
public function Meter() {
super();
}
public function set dataProvider( value:Object ):void {
if ( value )
{
if(value is ArrayCollection)
{
_renderDirty = true;
_dataProvider = value as ArrayCollection;
}
if(_dataProvider)
{
_dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
}
invalidateDisplayList();//only happens the first time
}
}
private function collectionChanged(event:CollectionEvent):void
{
Alert.show("In collection Change");//this never goes off when I change the "dataProvider"
_renderDirty = true;
invalidateDisplayList();
}
private function propertyChanged(event:PropertyChangeEvent):void
{
Alert.show("In property Change");//this never goes off when I change the "dataProvider"
_renderDirty=true;
invalidateDisplayList();
}
public function get dataProvider():Object {
return _dataProvider;
}
override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void {
if(_dataProvider))
{
var span:Number = unscaledWidth / _dataProvider[0].max;
var meterWidth:Number = _dataProvider[0].value * span;
meter.width = meterWidth;
_renderDirty = false;
}
}
And this is the mxml code where I change the "value" field....
<fx:Script>
<![CDATA[
[Bindable]
private var meterData:ArrayCollection = new ArrayCollection([
{value:80, max:100, min:0}
]);
protected function mySlider_changeHandler(event:Event):void
{
meterData[0].value = Math.round(mySlider.value)*10;
}
protected function button1_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
var array:ArrayCollection = testMeter.dataProvider as ArrayCollection;
var value:Number = array[0].value as Number;
Alert.show(value.toString());
// testMeter.meter.width= Math.random()*100;
}
]]>//initial value in "meterData" does get drawn...but when it's changed with the slider..nothing happens..
<custom:Meter id="testMeter" dataProvider="{meterData}" />
<s:HSlider id="mySlider"
liveDragging="true"
dataTipPrecision="0"
change="mySlider_changeHandler(event)"
value="3"/>
<s:Button click="button1_clickHandler(event)"/>
Can you help me figure out what's going on??Thanks guys!!!
The issue is that you are not resetting the dataProvider, nor doing anything to fire off a collectionChange event. I'll deal with each one individually. Here is how you reset the dataProvider:
testMeter.dataProvider = newDataPRovider;
But, you aren't doing that. So the set method will never execute after the first initial set.
If you need the collectionChange event to fire, you need to change the collection. You aren't actually doing that. You are changing the object in the collection. This code does not change the collection:
meterData[0].value = Math.round(mySlider.value)*10;
It just changes a single property in one of the objects of the collection. Try something like this:
var newObject = meterData[0];
newObject['value'] = Math.round(mySlider.value)*10
meterData.addItem(newObject);
This code should fire off the colletionChange event, even though the code you have does not.
I have a few more thoughts, unrelated to your primary question. Be sure to removeEventListeners in the dataProvider set method, like this:
public function set dataProvider( value:Object ):void {
if ( value )
{
// remove the event listeners here before changing the value
// that should allow the object to have no extra 'references' that prevent it from being garbage collected
_dataProvider.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);
_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
if(value is ArrayCollection)
{
_renderDirty = true;
_dataProvider = value as ArrayCollection;
}
if(_dataProvider)
{
_dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
}
invalidateDisplayList();//only happens the first time
}
}
And you said:
(dont want to say "dataProvider" because it is not a Flex
dataprovider, just a variable )
A dataProvider on the Flex List, or DataGrid, or DataGroup is also "just a variable." I don't see how the "Flex Framework" implementation is all that different than what you've done, conceptually anyway. Since you're specifically expecting a min and max value perhaps you should use a Value Object with those explicit properties instead of an ArrayCollection.
I have a component called a TableDataViewer that contains the following pieces of data and their associated set functions:
[Bindable]
private var _dataSetLoader:DataSetLoader;
public function get dataSetLoader():DataSetLoader {return _dataSetLoader;}
public function set dataSetLoader(dataSetLoader:DataSetLoader):void {
trace("setting dSL");
_dataSetLoader = dataSetLoader;
}
[Bindable]
private var _table:Table = null;
public function set table(table:Table):void {
trace("setting table");
_table = table;
_dataSetLoader.load(_table.definition.id, "viewData", _table.definition.id);
}
This component is nested in another component as follows:
<ve:TableDataViewer width="100%" height="100%" paddingTop="10" dataSetLoader="{_openTable.dataSetLoader}"
table="{_openTable.table}"/>
Looking at the trace in the logs, the call to set table is coming before the call to set dataSetLoader. Which is a real shame because set table() needs dataSetLoader to already be set in order to call its load() function.
So my question is, is there a way to enforce an order on the calls to the set functions when declaring a component?
The Flex documentation mentions (somewhere, I can't find it al the moment) that the order of initialisation of properties set in MXML is undefined. That is, sometimes dataSetLoader might have its value set first and then table, or sometimes the other way round.
To get around this you can use the Flex invalidation methods like invalidateProperties() and invalidateDisplayList() to wait until all of your properties are set and then perform your processing all at once.
As an example, here is how you might handle your issue.
Note that we move the call to the _dataSetLoader.load(...) method to the commitProperties() method, when we know we have both the table and dataSetLoader values:
[Bindable]
private var _dataSetLoader:DataSetLoader;
private var dataSetLoaderChanged:Boolean = false;
public function get dataSetLoader():DataSetLoader {return _dataSetLoader;}
public function set dataSetLoader(dataSetLoader:DataSetLoader):void{
trace("setting dSL");
_dataSetLoader = dataSetLoader;
dataSetLoaderChanged = true;
invalidateProperties();
}
[Bindable]
private var _table:Table = null;
private var tableChanged:Boolean = false;
public function set table(table:Table):void {
trace("setting table");
_table = table;
tableChanged = true;
invalidateProperties();
}
override protected function commitProperties():void
{
super.commitProperties();
if (tableChanged || dataSetLoaderChanged)
{
if (_dataSetLoader != null)
{
_dataSetLoader.load(_table.definition.id, "viewData", _table.definition.id);
}
tableChanged = false;
dataSetLoaderChanged = false;
}
}
In a previous application that I had written, I had a Class that extended AdvancedDataGrid (ADG). It contained the following code:
package
{
public class CustomADG extends AdvancedDataGrid
{
....
// This function serves as the result handler for a webservice call that retrieves XML data.
private function webServiceResultHandler(event:ResultEvent):void
{
var resultXML:XML = new XML(event.result);
dataProvider = new HierarchicalData(resultXML.children);
}
....
public function setOpenNodes(maxDepth:int = 0):void
{
var dataCursor:IHierarchicalCollectionViewCursor = dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}
}
}
In this implementation, the function setOpenNodes() worked fine - it did exactly what I intended it to do - pass it a number, and open all nodes in the dataProvider at or below that level.
Now, I am creating a new ADG Class and want to reproduce this functionality:
package view
{
import mx.collections.IHierarchicalCollectionViewCursor;
public class ReportADG extends AdvancedDataGrid
{
public function ReportADG()
{
super();
}
public function setOpenNodes(maxDepth:int = 0):void
{
var dataCursor:IHierarchicalCollectionViewCursor =
dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}
}
}
The dataProvider is set in the parent component:
<view:ReportADG id="reportADG" dataProvider="{reportData}" />
reportData is set in another file:
reportData = new HierarchicalData(resultXML.children);
However, I am getting runtime errors:
TypeError: Error #1034: Type Coercion failed: cannot convert ListCollectionViewCursor#6f14031 to mx.collections.IHierarchicalCollectionViewCursor.
I've tried casting dataProvider as ICollectionView. I've tried then casting the ICollectionView as IHierarchicalCollectionView. I've tried all sorts of casting, but nothing seems to work. Why won't this work in this new implementation as it did in the past implementation? What do I need to do?
*** Update:
I started debugging this. I added an override setter to my ADG Class to see when dataProvider was being set:
override public function set dataProvider(value:Object):void
{
super.dataProvider = value;
}
I added a breakpoint to this setter and to my setOpenNodes() function. Sure enough, the dataProvider is being set BEFORE setOpenNodes() is called, and it is HierarchicalData. But, when the setOpenNodes() the debugger says that the dataProvider is a null ArrayCollection. It seems like this is the root issue.
I needed to call commitProperties before attempting to access the dataProvider property.
public function setOpenNodes(maxDepth:int = 0):void
{
super.commitProperties();
var dataCursor:IHierarchicalCollectionViewCursor =
dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}