I am working on an application where I need to basically have 2 separate screens and I need to be able to switch between them. I am a total noob to Flex & Flash and I have not found anything on how to do this in my 2 days of Googling. It really shouldn't be this hard to do! :)
Here is what I have tried. I created a Flex Project and added 2 MXML Component files called Test1 & Test2 and each simply have a button in them which have a label of Button1 & Button2 so I can see if the correct component is showing. Below is the button code in the Test1 MXML Component file:
<s:Button id="btn1" label="Button1" click="currentState = 'State2'">
In the MXML Application file I have 2 States called State1 & State2. I have added the following to this file as well:
<local:Test1 includeIn="State1" x="0" y="0"></local:Test1>
<local:Test2 includeIn="State2" x="0" y="200"></local:Test2>
I have also added the states to all 3 files:
<s:states>
<s:State name="State1"/>
<s:State name="State2"/>
</s:states>
When I run the application, I see Test1 as I would expect since State1 is the first state listed. When I click the button in Test1 (labeled Button1), I would expect it to now show the Test2 MXML component (since I am changing the currentState to 'State2', but it does not. Test1 is still displayed with Button1 showing.
Can someone shed some light as to what I am doing wrong? Or propose a better (or proper) way of doing this?
Thanks in advance!
Sounds like the states in your Application are not connected to the states in your components. Just giving them the same name doesn't connect them in any way. Anyway, if you're just trying to hide/switch components in your main Application you really only need the states in there.
One option would be to just access the Application's currentState property directly from within the components:
<s:Button id="btn1" label="Button1" click="FlexGlobals.topLevelApplication.currentState = 'State2'"/>
That works, but it's not recommended because it strongly couples the component with the parent Application and in a large project will lead to a mess if you have to refactor (or work with other people).
Another option, is to have each component dispatch an event to let the parent know it should change:
<fx:Metadata>
[Event(name="changeParentState", type="flash.events.Event")]
</fx:Metadata>
<s:Button id="btn1" label="Button1" click="dispatchEvent(new Event('changeParentState'));"/>
Then, the parent would decide which state it should switch to:
<local:Test1 includeIn="State1" changeParentState="currentState='State2'" />
<local:Test2 includeIn="State2" changeParentState="currentState='State1'" />
This way is more generic and just better OOP. However, if you'd want your components to be able to trigger more than just a single state, you might have to do something more complex. This should give you a sense of where to go at least.
Related
At my project I've a DataGrid component that holds some information and, at the final column I need to put a floating tool box.
The idea is when the user hit on the icon's tool box, it appears above a floating tool box like a menu holding some icons to perform different actions.
So far, I've solved the column's ItemRenderer to show the dispatcher tool box icon but to show the upper floating tool box is something that I still can't solve. I've tried to make a custom component ( it's a Canvas that holds a HBox that contains the action's icons ) to hold the tool box's icons and showing it by a Menu control (mx.control.Menu) like this:
private function createAndShow():void {
var myCustomMenu:CustomContextMenu = new CustomContextMenu();
var myMenu:Menu = Menu.createMenu(this, myCustomMenu, false);
myMenu.show(btnToolBox.x + 10, btnToolBox.y + 10);
}
But this approach shows a weird container at the left and top of the page instead of show it at the given point i.e. (btnToolBox.x + 10, btnToolBox.y + 10).
So, can anybody help my out with this?
I was searching through the Internet looking for some examples but, I can't found anything that can help me to accomplish this situation so, if anyone knows a way to solve this or at least can point me somehow, any suggestions would be gratefully appreciated.
Finally, after a long research I found an answer. I created a Custom Component that uses a PopUpAnchor and solution looks like this
<s:PopUpAnchor id="hDropDown" x="{btnToolBox.x-options1.width+30}" y="{btnToolBox.y-6}" includeInLayout="false"
showEffect="{showEffects}" hideEffect="{hideEffects}">
<s:BorderContainer minHeight="0" id="options1" mouseDownOutside="{hDropDown.displayPopUp=false}"
cornerRadius="2" borderWeight="1" borderColor="gray" backgroundColor="black">
<s:layout>
<s:HorizontalLayout gap="5" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" />
</s:layout>
</s:BorderContainer>
</s:PopUpAnchor>
When using Flex with Spark, I have a simple chat window with a TextInput to enter your message and a send Button.
TextInput
Starts out as ""
Should be set to "" last in the function that handles the message sending
Should also be set to "" as a response for event="myOtherEvent"
Button
Should only be enabled when the TextInput's text.length > 0
At first I thought it was pretty clean to skip binding the text being entered into the TextInput to anything in my model and let that logic for button enabling/disabling stay in the view.
I still feel that it's a pretty nice approach except for the fact that it isn't a complete solution as it does not clear the TextInput.text as a response to receiving event="myOtherEvent".
The MXML for that partial solutions is:
<s:TextInput id="chatText" width="100%" height="32" />
<s:Button
label="Send"
enabled="{chatText.text.length > 0}"
click='{model.send(chatText.text); chatText.text=""}'
/>
If it wasn't for my event response requirement, how do you feel about that solution?
There is some logic in the Button, but just basic setting and checking. I know that it's a good idea to separate logic and presentation, but I thought this was a nice balance.
A complete solution I can think of would be to:
Have a two way binding of chatText.text and a property in my model
And in the set method for that property, I would dispatchEvent(new Event("updateButton")
A function in the same model class would bind to that event. That function would also be read in enabled="{model.thatFunction()}" of the Button. The function would return chatTextStringPropertyInModel.length > 0 and thus (by jumping through some hoops) would see to that the send-Button is enabled when there is text available for sending.
The model.send(chatText.text) can set chatTextStringPropertyInModel="" after sending and as that property is two-way bound with chatText.text the change would be reflected in the UI too.
My questions:
How much logic is all right to have in the view?
How should I solve this? What is most elegant and maintainable?
Maybe I'm confused but I don't see an issue in your solution. You just need to add an event handler for your other event
<fx:Script>
<![CDATA[
//clear text and disable button on other event
private function onMyOtherEvent(event:Event):void
{
chatText.text = "";
}
]]>
</fx:Script>
<s:TextInput id="chatText" width="100%" height="32" />
<s:Button
label="Send"
enabled="{chatText.text.length > 0}"
click='{model.send(chatText.text); chatText.text=""}'/>
Also I don't think there's anything wrong with a view component handling it's own view logic...
I'm not sure if this is possble, but I would like to add an event listener on a sub component from MXML. Something like this:
My Component
<s:Group>
<s:Button id="myBtn" label="click me" />
</s:Group>
Main Application
<local:MyComponent>
<local:myBtn click="doSomething()" />
</local:MyComponent>
I know that I can do this in code, I just want to know if it's possible to do this in MXML. If it's possible, what is the correct syntax?
It is possible, but requires a lot of setup.
First add event metadata to MyComponent:
<s:Group>
<fx:Metadata>
[Event(name="click", type="flash.events.MouseEvent")]
</fx:Metadata>
<s:Button id="myBtn" label="click me" />
</s:Group>
In theory you should make sure that your component also dispatches the click event; however since click will bubble by default you do not need to do anything else for that event.
Now, your main component will show the event in MXML code hinting, and the compiler will not complain:
<local:MyComponent click="doSomething()>
</local:MyComponent>
Generally, I would not recommend trying to drill down into a component in order to place listeners on events dispatched by a component's children. It is a break of encapsulation. The parent component should not know implementation details of the children.
i can do this:
<s:Button id="Btn" enabled.State1="false" />
But the following code is giving me an error.
private function enableDisable():void{
Btn.enabled.State1="false"; //Error: Access of undefined property State1
}
how to code enabled.State1 in ActionScript?
Thanks
I know this is not what you want to hear, but here it goes anyway: why do you want to do that? The whole purpose of the states is that you wouldn't have to write tons of ActionScript to do the same thing.
Why you can't do it like that
By writing Btn.enabled.State1 in ActionScript you're essentially saying: give me the property called 'State1' of the Boolean instance called 'enabled'. Obviously that won't work because a Boolean doesn't have such a property. You're confusing the MXML dot (.) notation - used for assigning values to properties based on states - with the ActionScript dot notation - used for reading/writing properties.
A solution or as close as it gets
Since it's the very nature of this feature that you would use it in MXML, you can't do exactly what you're asking for in ActionScript. The next best thing would be to listen for StateChangeEvent en set the Button's 'enabled' property according to the new state name.
addEventListener(StateChangeEvent.CURRENT_STATE_CHANGE, onStateChange);
private function onStateChange(event:StateChangeEvent):void {
switch (event.newState) {
case "wrong": Btn.enabled = false; break;
case "correct": Btn.enabled = true; break;
}
}
(I'm using the same states as in James' answer)
I think you may be using states in the wrong context. For instance, you have component which contains a user input with a button next to it. The button is only enabled when the correct word is input. You would define two states for the component, perhaps correct and wrong.
<s:states>
<s:State name="wrong" />
<s:State name="correct" />
<s:states>
You would then, similar to what you've done above, set individual properties for the buttons depending on the state:
<s:Button id="Btn" enabled.wrong="false" enabled.correct="true" />
By default, the state of the component would be wrong. After handling user input and checking if the correct word is entered, the state of the component would be changed to correct.
Normally the state-specific properties of components are set at compile time and the state of the component itself changed at runtime.
Here is an overview of states in Flex 4.6
In my code I have defined the following:
<s:Image id="test" x="50" y="50" width="30" height="30" click="onClick_clickHandler(event)" smooth="true" smoothingQuality="high" source="#Embed('icons/myImage_60_off.png')"/>
What I want is to be able to change the source of the image every time the user clicks on the image - similar to the way favourites work on a browser.
I have no idea how to change the source of the image from my code.
Thank you
I had a hidden datagrid with my solution, because it started off with a visible one.
The image looked like this:
<mx:Image top="153" left="10" right="10" bottom="5" source="{dgpick.selectedItem.ImageFile}" />
Every time someone clicks on the image, I would increase the selected index of the datagrid, and the labels will display the corresponding data, as they are also bound to its data.
The image can also have a link to an XML file that you can load like this:
<s:HTTPService id="Config"
url="config.xml"
result="resultHandler(event)"/>
private function resultHandler(event:ResultEvent):void
{
ImagesURL = event.result.images.ImagesURL[iCounter];
}
Every time someone clicks on the image, you can increase the counter, and so forth.
Hope this gives you some ideas.
There are 20 ways to do this.
If you need more code, add a comment, with what you need.
I finally did it!
public var image_loader:Loader; // define a new loader
image_loader = new Loader(); // create the new loader at the desired location (in my case in the initialization method of the page
image_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded); //add a listener and a function to assign to the listener
depending on where the command has to be executed
image_loader.load(new URLRequest('location of the image')); //this will load the image dynamically
function imageLoaded(event:Event):void {image_Id.source = image_loader;} //where image_Id is the id of the s:Image tag to be modified
also, I had to remove the source from the s:Image tag previously posted
The rest is just logic of the way the application needs to implement the functionality, so it's pretty much left to the developer's desires