views:

846

answers:

6

Hi

I am trying to create a component which can receive an additional value from mxml code:

public class Board extends Canvas
{
 public var boardSize:Number;
 private var cellWidth:Number;
 public function Board()
 {
  super();

 }
}

How can I pass boardSize value from the mxml code. e.g.

<MyComp:Board x="22" y="25" width="600" height="600" boardSize="19">

But it doesn't work. When I am trying to debug the code I see that boardSize variable is NaN inside the class

A: 

try this:

<MyComp:Board x="22" y="25" width="600" height="600" boardSize="{Number(19)}">

or this:

<mx:Script>
<![CDATA[
public var boardSize:Number = 19;
]]>
</mx:Script>

<MyComp:Board x="22" y="25" width="600" height="600" boardSize="{boardSize}">

The problem may be that you are passing it as a string to the component, it doesn't realize that it is a number.

Ryan Guill
Just tried both of them. Didn't help :(
Oleg Tarasenko
+1  A: 

When are you testing this? If you test the code by putting a breakpoint in the constructor, boardSize should be null. Just like your code, instantiating by MXML requires that the constructor is called before any of the member variables are set.

For an example, try this app using the TestObject class. When you run it in debug mode, the trace will tell you that n and s aren't set, but when you see the app, the two labels are clearly bound after the trace has happened.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*">
 <mx:TraceTarget />
 <local:TestObject id="o" n="19" s="19" />
 <mx:Label text="Number Field: {o.n}" />
 <mx:Label text="String Field: {o.s}" />
</mx:Application>


package
{
 public class TestObject
 {
  [Bindable]
  public var n:Number;
  [Bindable]
  public var s:String; 

  public function TestObject()
  {
   trace("Initialized test object ...", n, s);
  }
 }
}
Dan Monego
thats a good point.
Ryan Guill
Yes I putted breakpoint there. That's why it didn't work? Will it help if I set breakpoint near mxml tag before?
Oleg Tarasenko
Probably not. If you want to run code as soon as these variables are set, you should look into using getter and setter methods to handle them.
Dan Monego
So, as far as I understand the variable is not initialized in constructor, and it's pointless to call a method from constructor to do something if it needs the variable? When to call it in this case?
Oleg Tarasenko
Either from the setter of the method you're working with, or on display objects, using the creationComplete handler.
Dan Monego
Just realized that I need to user variables I noted above not in constructor, but on creation complete
Oleg Tarasenko
+1  A: 

Just to elaborate on Dan Monego answer and comments. Change the class to look like the class below. Then you can set a break point on the set function.

public class Board extends Canvas
{
        private var _boardSize:Number;

        public function get boardSize():Number{
           return _boardSize;
        }


        public function set boardSize(val:Number):Number{
           _boardSize = val;
        }

        private var cellWidth:Number;
        public function Board()
        {
                super();

        }
}
Shua
A: 

Hi, I think to get mxml code completion you need to add the [Inspectable] metatag above the property that you want.

[Inspectable(defaultValue="", type="Number", name="boardSize", format="Number")]
public var boardSize:Number;

Try this and let me know how you got on. Loads more info on this kind of component development here

The inspectable tag gives you some control over how the autocomplete works (Limiting a field to a few inputs, for example), but all public properties show up in autocomplete by default.
Dan Monego
A: 

If you look at your constructor it has no arguments hence you need the properties, or public vars, to set the value. If you wanted to create your custom component with arguments you would have to create a different constructor as listed below. Don't think it is is possible to call the constructor with args from mxml but with actionScript you could have optional parameters that would set the values...

public class Board extends Canvas
{
        public var boardSize:Number;
        private var cellWidth:Number;
        public function Board(boardSize:Number = 0, cellWidth:Number = 0)
        {
                super();
                this.boardSize = boardSize;
                this.cellWidth = cellWidth;


        }
}
AndrewB
A: 

The place where you most probably want to have access to this property would be during setup of the component and creation of its children - as "creationComplete" should be already after you've done that :) For this, override the createChildren() method. Additionally you probably also want to create a setter for it (or at least bind to it) to catch changes.

Example:

package {
  import flash.events.Event;

  public class Board extends Canvas {

  /** clever ASDoc comment goes here */
  [Bindable("boardSizeChanged")]
  [Inspectable(category="General", defaultValue="")]
  var _boardSize: Number;
  public function get boardSize(): Number {
      return _boardSize;
  }
  public function set boardSize(value: Number): void {
      if (_boardSize == value) return;
      _boardSize = value;
      // do something when boardSize changes, e.g. update child components
      trace("Boardsize is now: " + value);
      dispatchEvent(new Event("boardSizeChanged"));
  }

  public function Board() {
      super();
  }

  override protected function createChildren(): void {
      super.createChildren();
      // create your own children, using the boardSize property (this uses the getter function)
      trace("Boardsize: " + boardSize);
  }
}}

Please note that in this example, set boardSize() is called prior to createChildren - so if you use boardSize to change your children you should make sure that they actually already exists in this setter.

Zefiro