Access object inside View from another View? - actionscript-3

applicationX.mxml :
<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160" >
<s:ViewNavigator label="Login" width="100%" height="100%" firstView="views.LoginView" />
<s:ViewNavigator label="Settings" width="100%" height="100%" firstView="views.SettingsView" />
</s:TabbedViewNavigatorApplication>
Settings.mxm ( Settings View ) :
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" >
<s:Label id="myLabel" />
</View>
How to access myLabel from Login view ?

you shouldn't. One view should never know what is inside another view. What you want is a model with a property like .loginStatus that can then be set by Login and seen by Settings. You can use one of many MVC styles to accomplish this. Do some googling on MVC patterns and Flex and see the different ways this is done. In the meantime, here is a quick for instance:
Settings.mxml:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" >
<fx:Script>
<![CDATA[
private var model:MyModel = MyModel.getInstance();
]]>
</fx:Script>
<s:Label id="myLabel" text="{model.loggedInStatus}" />
</View>
Login.mxml:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" >
<fx:Script>
<![CDATA[
private var model:MyModel = MyModel.getInstance();
private function loginSucceded():void{
model.loggedInStatus="Logged In";
}
]]>
</fx:Script>
</View>
MyModel.as
Singleton class with a property named .loggedInStatus. You can check this other answer AS3 singleton implementations for a discussion about various singleton patterns and why I use the one I use.
This is a VERY simple example. You wouldn't want to use a human readable string loggedInStatus to determine state or anything. but this is an example of how the model works and how views can display proper things based on the state of the model.

Set settings singleton. This means, you define a static variable pointing to the item itself (it is required to have only one instance of Settings.mxml)
add
public static var instance:Settings;
to settings.
add initializeEventListener to settings, and inside the function set the instance:
instance=this;
Than you can access Settings page anytime by getting the singleton, like:
Settings.instance.myLabel.text="success";

Related

Flashbuilder 4, Desktop Air Application - Embed and play Sound

I am a complete newbie to Flashbuilder, and I need to learn how to 1.) Embed a sound file (mp3) and 2.) be able to play said file.
I looked it up using Google and all the examples seem to be for AS3, I tried to adapt it as follows but cant get it to play (or should I say I can't hear anything?)
bgmusic is imported at the src level
Test.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.media.Sound;
[Embed(source="bgmusic.mp3")]
public var mySound:Class;
protected function button1_clickHandler(event:MouseEvent):void
{
var s:Sound = new mySound() as Sound;
s.play();
}
]]>
</fx:Script>
<s:Button label="Test" click="button1_clickHandler(event)" />
</s:WindowedApplication>

Is there a way in ActionScript 3.0 to basically make a tab either visible or invisible, depending on a bindable boolean variable?

This is admittedly very similar to questions which have been asked before, but it is a little different. Everything I've seen about how to deal with making a tab either apply or not apply to a TabNavigator is, in my opinion, not good enough. The problems are that the ways to do this that I've seen in my research have issues with either adding a tab a split second after the user sees it missing, or removing a tab a split second after the user sees it present. Is there not some way to get around this problem? Thanks!
EDIT: For clarity, I mean, for other stuff like labels and images, we can basically get around this problem by setting their visibility equal to a bindable variable and not entering the screen they're in until that variable has been set. The equivalent to this approach is what I'm looking for. Thanks!
You can use a ChangeWatcher to trigger a function when a bindable variable changes. In that function, add or remove the tab based on the variable's value.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="onCreationComplete()"
xmlns:local="*">
<fx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
[Bindable]
public var showTab2:Boolean;
private var watcher:ChangeWatcher;
private function onCreationComplete():void
{
watcher=ChangeWatcher.watch(this, "showTab2", onValueChange);
}
private var _tab2:NavigatorContent;
public function get tab2():NavigatorContent
{
if (!_tab2)
{
_tab2=new NavigatorContent();
_tab2.label="tab2";
}
return _tab2;
}
private function onValueChange(event:Event):void
{
if (showTab2 && !tabNav.contains(tab2))
{
tabNav.addElement(_tab2);
}
else if (!showTab2 && tabNav.contains(tab2))
{
tabNav.removeChild(tab2);
}
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<mx:TabNavigator id="tabNav" width="300" height="300">
<s:NavigatorContent label="tab 1"/>
</mx:TabNavigator>
<s:Button label="Add/remove tab2" click="showTab2 = !showTab2"/>
</s:Application>

Disable alternatingItemColor property for AdvancedDataGrid if the rows are empty

I have a custom AdvancedDataGrid and we use alternatingItemColors property which shows two different colors for rows of the AdvancedDataGrid. Now sometimes the datagrid has 15 rows but only 5 would have data and we want only first 5 rows to display alternating colors and rest of the rows should only display one color. Has anyone done this in past and if someone can please explain how to do this would be really appreciated.
Thanks in advance.
You have to override the Datagrid and override drawRowBackground method, if the rowInd is more than the number of rows then set the default color. See the code snippet mentioned given below -
public class CustomDataGrid extends AdvancedDataGrid
{
protected override function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void{
var XMLdata:XML=rowNumberToData(dataIndex) as XML;
if(XMLdata!=null){
if(XMLdata.attribute(Constants.col) != undefined && XMLdata.attribute(Constants.col) != ""){
color=XMLdata.attribute(Constants.col);
}else{
color=0xFFFFFF;
}
}
super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}
}
Either set rowCount to the actual row count when you have fewer rows than the height allows for, or override drawRowBackground.
Try by using Item renderer for your ADG: -
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
private var dpHierarchy:ArrayCollection= new ArrayCollection([
{name:"A", region: "Arizona"},
{name:"B", region: "Arizona"},
{name:"C", region: "California"},
{name:"D", region: "California"}
]);
]]>
</fx:Script>
<mx:AdvancedDataGrid id="myADG"
width="500" height="500"
paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0"
dataProvider="{dpHierarchy}"
itemRenderer="DrawAlternateRowColor">
<mx:columns>
<mx:AdvancedDataGridColumn dataField="name" headerText="Name" />
<mx:AdvancedDataGridColumn dataField="region" headerText="Region" />
</mx:columns>
</mx:AdvancedDataGrid>
</s:Application>
//ItemRenderer name: - DrawAlternateRowColor -- you can use the same concept with you CADG.
<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true" alternatingItemColors="[#0000FF, #FF0000]"
width="100%" height="100%">
<s:Label id="lblData" verticalAlign="middle" text="{listData.label}" />
</s:MXAdvancedDataGridItemRenderer>

Flex custom component, best way to use it

I have not completly understood how custom components work...
Let's assume I have my Main.mxml application
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:local="*">
<fx:Script>
<![CDATA[
private var privateStr:String = "Stringa Private";
public var publicStr:String = "Stringa Public";
]]>
</fx:Script>
<local:AddUser height="100" width="500"/>
<s:Label id="lblText" x="120" y="120" width="418" height="115" text="!!!"/>
</s:WindowedApplication>
And the component AddUser.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="initialize_component()">
<fx:Script>
<![CDATA[
public var btnName:String = "Login";
private function initialize_component():void
{
login.label = btnName;
}
private function doLogin():void
{
//some stuff here
}
]]>
</fx:Script>
<s:TextInput id="txtuser" x="96" y="36"/>
<s:TextInput id="txtpass" x="96" y="66"/>
<s:Button id="login" x="96" y="96" width="128" click="doLogin()" />
</mx:VBox>
I would like that on the Button (login) click I get the publicStr/privateStr that are in the main.mxml...
Am I getting everything wrong? how can I use more components like they are all part of the same application and use the same variables/methods?
It seems like you're having issues with the idea of encapsulation. Child components shouldn't know about parent components, and View components shouldn't do real work, only request work from Controller components. In very simple projects, your top level component can contain the controller logic, but many people prefer to keep it separate even in small projects. How to do this is beyond the scope of this answer.
So, how should the parent and child properly communicate? Child components should expose properties that the parent (or Framework, if you're feeling ready to use a dependency injection framework) can populate with only the data the child components need.
Child components request work from the controller by generating events.
So, doLogin() would containe something like
dispatchEvent(new Event('doLogin'));
and the parent component would be listening for this Event. In its handler, you would perform the login. More than likely, your login would be asynchronous, so you'll need another handler to listen for the login data to come back. When the login data comes back, you will then set the properties on the login View based on the return.

pass data from popup to parent

I have a parent w/ a popup child. When parent loads, I have to call a function within the popup without showing the popup (thus, I load "pupLove" but don't include it in layout)....I then pass this data to the parent. When the user manually clicks another button to open the popup, the same function is called & data passed to the parent. However, I am not able to pass dg.length to the parent. I believe the root problem is that I am loading "pupLove" & thus the parents are getting confused.....I'm guessing if I get rid of "pupLove" I can pass the data correctly but will need to call the child's function at creationComplete of the parent....how do I do that?
Here's my parent:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
backgroundColor="green" width="50%" height="100%"
xmlns:local="*"
>
<fx:Script>
<![CDATA[
import pup;
import mx.managers.PopUpManager;
public function showPup(evt:MouseEvent):void {
var ttlWndw:pup = PopUpManager.createPopUp(this, pup, true) as pup;
PopUpManager.centerPopUp(ttlWndw);
}
]]>
</fx:Script>
<mx:VBox>
<local:pup id="pupLove" visible="false" includeInLayout="false" />
<s:Button click="showPup(event);" label="launch Pup" />
<mx:Text id="Ptest" color="black" text="from Parent:{pupLove.dg.length}" />
</mx:VBox>
</s:Application>
And a popup child called 'pup.mxml':
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300">
<fx:Script>
<![CDATA[
public function init():void{
// send php call
}
import mx.events.CloseEvent;
import mx.managers.PopUpManager;
private function removePup(evt:Event):void {
PopUpManager.removePopUp(this);
}
]]>
</fx:Script>
<fx:Declarations>
<s:ArrayCollection id="dg">
</s:ArrayCollection>
</fx:Declarations>
<s:TitleWindow width="100%" height="100%" close="removePup(event)">
<mx:VBox>
<mx:Text id="test" color="red" text="from Child:{dg.length}" />
<s:Button label="add Items" click="dg.addItem({id:'cat'})" />
</mx:VBox>
</s:TitleWindow>
</s:Group>
UPDATE: I guess my question can be more easily stated as: "is there a way to call a child's function from the parent without actually loading the child?"
Well, if in your child's function, you don't need to access any of its UI component, then you could instanciate the child in the parent view and call the method.
When you need to display the popup afterwhile, use the PopupManager addPopup method with this existing instance instead of using createPopup