Flex 4 XML Attribute Binding Not Working? - apache-flex

For some reason this seems to no longer work in flex 4, it used to work in flex 3...
[Bindable]
public var xmlTitle:String = "TEST";
[Bindable]
public var xmlData:XML = <Data title={xmlTitle}> ... </Data>;
I am setting the xmlTitle variable outside of the component:
<local:Comp xmlTitle="Some Title" />
I have tried with getter / setters...etc. It just does NOT update when changing, it shows the default value and never changes. Is this a known new bug in flex 4?

I can't say I've ever done that before, mostly because it's extremely bad form. If you want to create an xml, using the XML object in mxml to create it in the declarations:
<fx:Declarations>
<fx:XML id="xmlData" xmlns="">
<Data title="{xmlTitle}">
</Data>
</fx:XML>
</fx:Declarations>
Should work just fine.

Using {} with XML in ActionScript is not data binding. It takes current variable's value in the moment of forming XML. And doesn't change it later. So this behavior is absolutely expectable.

Related

What is the MXML syntax to assign properties of subcomponents in custom MXML Components?

I am working on a custom Flex 4 component which is an aggregation of two existing flex components. I would like to be able to specify my own custom properties for the component as well as access the existing public subcomponent properties via MXML. For instance I might want to adjust the font color or style for the label and text input.
A toy component which aggregates both a label and a text input:
<?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"
>
<fx:Script>
<![CDATA[
[Bindable] public var prompt:String = "default prompt";
[Bindable] public var input:String = "default inpput";
]]>
</fx:Script>
<s:VGroup>
<s:Label id="cLabel" text="{prompt}" />
<s:TextInput id="cTextInput" text="{input}" />
</s:VGroup>
</s:Group>
Then in my main application I would like to access the public interfaces of the sub-component via mxml without re-writing a pass-through binding for every one. Something like:
...
<local:myInput prompt="name" input="please enter name">
<local:cLabel color="0xffffff" />
<local:CTextInput fontStyle="bold" />
</local:myInput>
In actionscript one can do this easily for all public properties:
myInput.cLabel.color = "0xffffff";
But I am stumped on the syntax for MXML. This seems like it should be easy, but I have not found the answer yet. Any help greatly appreciated.
You can't daisy chain down an display hierarchy w/ the MXML tag/value. You can do it in ActionScript, as you specified, but even that would probably be considered a bad practice.
I'll point out that color on the Label and fontStyle on the TextInput are not properties. They are styles So, the code you have:
myInput.cLabel.color = "0xffffff";
Would most likely throw an error because color is not a property. You'd have to use code like this:
myInput.cLabel.setStyle('color',"0xffffff");
However, since styles are usually inherited by the children; I suspect at the top level component, you can set the style and it would immediately trickle through to the children automatically. So, you should just be able to do:
myInput.setStyle('color',"0xffffff");
Or in MXML:
<local:myInput prompt="name" input="please enter name" color="0xffffff" fontStyle="bold" >
</local:myInput>
And it should trickle on down. Things can get trickier if you want to set styles individually on child components.
But, back to your original question w/ regards to properties. To keep a component encapsulated, you should create properties that set on the children. Something like this:
private var _property : String;
public function get property():String{
return _property;
}
public function set property(value:String){
_property = value;
myChildComp.property = value;
}
It can suck if you need to do this for a lot of properties. If encapsulation of this component isn't a priority; just set them in ActionScript.

Disable warning only in one place

In an MXML code
<fx:Script>
public var data:ArrayCollection = new ArrayCollection();
</fx:Script>
<s:DataGroup dataProvider="{data}" />
I'm getting a warning:
Data binding will not be able to detect assignments to "data"
I know that the data provider will be never changed in this case, and want to suppress this warning in this case, but I don't want to completely disable it, -show-binding-options=false in all project is not an option.
How to disable a warning only in one place? Disabling for the whole file is not so good, but acceptable.
How about just making your data variable bindable? Something like:
<fx:Script>
[Bindable]
public var data:ArrayCollection = new ArrayCollection();
</fx:Script>
<s:DataGroup dataProvider="{data}" />
Instead of using <fx:Script></fx:Script> you could use <fx:Declarations></fx:Declarations>. Any object declared in that MXML element is bindable implicitly. Here's how your code will look like then:
<fx:Declarations>
<s:ArrayCollection id="data" />
</fx:Declarations>
<s:DataGroup dataProvider="{data}" />
Additionally it becomes much more readable and there's no mix of ActionScript and MXML. Because your collection is declared as public it makes difference whether to use ActionScript with [Bindable] or using MXML.
BTW, a general recommendation for cleaner code is to separate ActionScript completely from MXML. For instance in my projects I create a separate ActionScript file for each MXML component in the form <NameOfComponent>Includes.as.

Initialize properties of custom component before creating children in flex

Say I have the following custom component:
<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">
<fx:Script>
<![CDATA[
[Bindable]
public var prop:String;
private function formatProp() : String {
return "Hello, " + prop;
}
]]>
</fx:Script>
<s:Label text="User: {prop}"/>
<s:Label text="Greeting: {formatProp()}"/>
</s:Group>
If I add it to my application like this:
<local:MyComponent prop="Hello"/>
The result looks like:
User: Mark
Greeting: Hello, null
It seems Flex is setting prop on my custom component after it has already initialized the child labels, so it's reliant on the property changed event to set the user label.
Is there an elegant way to make Flex wait for all of my component's properties to be set before initially evaluating bindings?
Note: I realize the formatProp function is trivial and could be included inline, but this is just a simplified example.
The "elegant way" would be to actually provide data binding, so that you can change your property also afterwards. Your initial idea looked good, working with the answer provided by Cornel. I just wanted to mention this as your actual question sounded more like that you know your data binding is not working and you just wanted to postpone the initial setting of the variable.
Btw, should you plan to create custom components in Actionscript (instead of mxml) you'll face the opposite problem: properties are set before you had a chance to actually create your children, so you may need to buffer them if they actually should influence some childs properties.
it is not related to component livecycle, more to binding rules. Your function "formatProp" should recieve the parameter "prop" as a parameter in order to be called when the prop is changed. Try this code:
private function formatProp(props:String) : String {
return "Hello, " + props;
}
<s:Label text="Greeting: {formatProp(prop)}"/>

How can I target a Flex 3 datagrid in MXML from Actionscript?

I have a datagrid defined in an mxml file (flex 3):
I am using an external class to connect to a sqlite database and generate some results (this is working and I can trace the results).
How can I target the datagrid generated in the mxml from the external class? I have tried:
Application.application.resultsGrid.dataProvider = results.data;
And get 'Error: Access of undefined property Application.' from the amxmlc compiler.
I've also tried:
[Bindable]
public var resultsGrid:DataGrid;
In the class properties.
Looks like I needed to include import mx.core.*; and it now works.
I don't really understand your answer. Am I not binding the dataprovider property by doing:
Application.application.resultsGrid.dataProvider = result.data; ?
I'm from a PHP background and familiar with OOP in that environment so the idioms in Flex are quite strange to me.
as brd664 says, what you are actually doing in
Application.application.resultsGrid.dataProvider = result.data;
is actually an assignment. It's just like assigning a value to variable as in
var a : uint = 1;
Binding gives you a little more structure and allows you to populate multiple components based on a single property update. There's a ton of other benefits from binding and probably too much to cover in this post.
Here is a quick and simple example of how binding works. Note that there is one property that is bindable... when you click the button it sets that property to the value of whatever is in the textInput. This update then causes the bindings to fire and updates anything that has been bound to that property. It's one of flex's biggest features (it's also used extensively in silverlight and wpf and probably a load of other technologies that i'm not aware of). Anyway... have a play with it and see if you can get your component to update from a binding.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">
<mx:Script>
<![CDATA[
private var _myData : String
[Bindable]
public function get myData() : String
{
return _myData;
}
public function set myData(value : String) : void
{
_myData = value;
}
private function clickHandler(event : MouseEvent) : void
{
myData = myTextInput.text;
}
]]>
</mx:Script>
<mx:VBox>
<mx:HBox>
<mx:Label text="{myData}" />
<mx:Label text="{myData}" />
<mx:Label text="{myData}" />
</mx:HBox>
<mx:TextInput id="myTextInput" text="TYPE HERE" />
<mx:Button label="CLICK TO BIND" click="clickHandler(event)" />
</mx:VBox>
</mx:Application>
Update: The phrasing of your question confused me :(
If you need to populate the datagrid with from you db, you really should be looking at binding the dataProvider property.

Access to elements defined in MXML from External AS

I have a MXML with a form, and inside it, two TextInputs. I hate having any piece of code inside the MXML file (I come from a JavaScript formation) so I use a
mx:Script source="external.as"
tag to include any code used in any MXML file. The problem is that if I have this code on the external.as file:
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = marketInput.text;
vo.segment = segmentInput.text;
vo.priceLow = priceLowInput.text;
vo.priceHigh = priceHighInput.text;
}
Where marketInput, segmentInput, priceLowInput and priceHighInput are TextInputs defined in the MXML file. When I try to complile I get a 1120: Access to undefined property XXXXX
I have tried adding this lines prior to the function:
public var marketInput:TextInput;
public var segmentInput:TextInput;
public var priceLowInput:TextInput;
public var priceHighInput:TextInput;
But instead I get a 1151:A conflict exists with definition XXXX in namespace internal which makes perfect sense.
Is there a way to do this without having to pass all the input references to the function as parameters of it?
You need to create a reference to an instance of the TextInputs' parent container, and then use that reference to accsess the TextInputs and their properties. I think we need some clarification on your file structure. How are you creating the instance of the parent container? I'm thinking this is what you need to do:
MyForm.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:TextInput id="marketInput" />
<mx:TextInput id="segmentInput" />
<mx:TextInput id="priceLowInput" />
<mx:TextInput id="priceHighInput" />
</mx:VBox>
SaveVOContainer.as:
package
{
public class SaveVoContainer extends Container
{
private var myForm:MyForm = new MyForm();
public function SaveVOContainer
{
this.addChild(myForm);
}
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = myForm.marketInput.text;
vo.segment = myForm.segmentInput.text;
vo.priceLow = myForm.priceLowInput.text;
vo.priceHigh = myForm.priceHighInput.text;
}
}
}
Doing a "code-behind" is painful in Flex. There is no concept of partial classes or the flexibility of prototypal inheritance as in Javascript. Google for "code-behind in flex" for many resources.
I think it's better you get used to the idea of embedding code in mxml. Use script tags avoiding inline code as much as possible. If you have to write a lot of code within MXML, perhaps you may want to re-factor the code into multiple custom components. Bonus points if they are reusable.
The canonical way to do code-behind in Flex is via inheritance. Here's a good explanation from the docs: http://learn.adobe.com/wiki/display/Flex/Code+Behind. In a nutshell:
Declare an ActionScript class to use as your base class.
Set the base class as the root container in your MXML file.
For any controls declared in your MXML file, you have to redeclare them as public members of the base class using the exact same name (exactly as you are doing above for your script block with source tag, only it works :-)
So, your ActionScript file:
package mypackage
{
import mx.controls.TextInput;
public class myClass extends WindowedApplication
{
public var marketInput:TextInput;
private function populateFromForm():void{
/* As above */
}
}
}
And the corresponding MXML file:
<?xml version="1.0" encoding="utf-8"?>
<custom:myClass xmlns:custom="mypackage.*"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<mx:TextInput id="marketInput"/>
</custom:myClass>
Et cetera for your other TextInput controls. And now your populateFromForm function should work.
It is kind of heinous to have to redeclare the same entities twice, but it's not quite the bag of hurt the earlier respondent made it out to be (although it's possible this changed in Flex 4 to make it less painful than it was).
import this in .AS:
import mx.core.Application;
in the .AS use this:
mx.core.Application.application.component.property = value;
mx.core.Application.application.myText.text = 'test';
do you have a script tag in your mxml file that points to your ActionScript file?
<mx:Script source='includes/foo.as' />

Resources