Hi Matt!

 Thanks for the reply.  Trying to look at the code again, I relized that
I've got to stop using gmail to read this list :-P  It does nasty things to
code listings.

 Anyway, I think I just explained things backwards as I was thinking more
about the final outcome.
 given:
   foo.addEventListener(.... // Add a listener to current foo object
   foo = bar              // reference assignment, foo is now an alias to
the bar object
   foo.setter = 'blah'  // This calls function set setter on the BAR
object, events are dispatched by BAR

 This means that the listener in line 1 never sees the event, as it was
generated by BAR.

 Clearing up the initializers on [Bindable] declarations, seemed to fix the
databinding not tracking properly,
 So THANKS! for that tip - its not something I would have guessed
(especially since it doesn't get flagged as
 error).

 I think you could have objects track assingment of themselves by having an
'AddressOf' hook internally...
   foo = bar translates to :  foo = bar.AddressOf
 This would let 'bar' keep track of when its being aliased.  I believe some
script languages such as Python and
  Ruby do it this way.  Might be overkill, but it can come in handy at
times...

 Thanks Again for the Help!
 Steve


On 12/7/06, Matt Chotin <[EMAIL PROTECTED]> wrote:

   Assuming your setters are all normal bindings should listen based on
how the references line up and should update to always accurately listen on
the objects referenced by the expression.  So however foo.bar evaluates,
that's what should be listened to.  If it's listening on the prior version
of _bar you have a bug somewhere.  It may be that the way you dispatch
events is not notifying the bindings correctly.



I'm pretty sure the initializer code won't work, though maybe we were able
to somehow fix that issue.



You have to remember that "foo" is a reference to some object, it is not
the object itself.  So when I say foo = bar, if foo was assigned to some
other object that other object will have no idea that foo no longer points
to it.  This isn't C++ with your own operator overloading for ref-counting
pointers J   The only object that can know that foo is being re-assigned
is that class that declares the foo variable.  That's the one where you can
have a setter, and it's in that setter that you know the re-assignment is
taking place.



We don't have a cheatsheet, that may be a good idea.



Matt


 ------------------------------

*From:* [EMAIL PROTECTED] ups.com [mailto:flexcoders@ yahoogroups.com] *On
Behalf Of *Steve Hindle
*Sent:* Wednesday, December 06, 2006 11:42 PM
*To:* flexcoders@yahoogroups.com
*Subject: *Re: [flexcoders] Databinding & eventlistner Toy, Unexplained
errors and insights... Adobe Please Confirm





Hi Matt,



  Thanks for the quick response!   I really appreciate the help and info.
I realize the dispatch in the ModelBase1.mxml  setter was a very crude
hack - I did it that way as 'this' in the mxml refers to the application
root tag, and not the component/object. Since I was binding the listeners to
'foo','bar','baz' - having the event fire on the application level didn't
get me anywhere... It just seemed like the quickest/easiest way to get the
'right' object to fire the event. I'll remember to steer clear of this in
the future...



>The above code says that any instance of Foo will dispatch an event

>called "barChanged" when that instance's bar references has been
reassigned.

That makes sense - but what happens to items that were databound or
listening to foo.bar ?  They continue to use the prior version/prior
object that was stored in _bar, don't they?  (ie. they don't get updated to
listen to the 'b:object' you passed in to 'set bar()' )  This was the real
crux of it for me - I use a 'temp' or 'currRec' in mxml's just for binding.
I have an accessor based property called 'model' on the mxml's that just
does currRec = blah.  It was _supposed_ to simplify my databinding, but I
think its causing a lot of subtle bugs.

>var myFoo:Foo = new Foo(); //note that this probably doesn't work in real
code,

>assigning in the initializer to a [Bindable] doesn't work



Really?!?! Acck - I do that allover the place!  This is gonna save me some
grief! Thanks!!



Also, having to listen explicitly sub-components(foo.bar)  was something I
hadn't considered.  I've prolly got a bunch of bugs related to that as well.


It would be kinda of nice if the object could 'listen to itself' - so if
you said foo = bar, the 'foo' object/class could throw an event directly,
instead of having the

calling component do it (ie - my setter hack in ModelBase1.mxml was due to
trying to catch explicit assignments.  If foo just thru the event for me, I
could have gotten rid of the getter/setter all together and still been
notified of assignments.)



There seems to be a bunch of restrictions on DataBinding - is there a
'cheatsheet' listing them? I've seen one about Collections not automatically
firing if an entire element of the underlying array was replaced.  And I
think there's one hiding in the short-circuit evaluation used with setters
(no store/event if value is already = new value).  Are there any more
'subtle' ones hiding out there ?



Thanks Again!














On 12/6/06, *Matt Chotin* < [EMAIL PROTECTED]> wrote:

I'm having trouble following some of your logic, but I think you're
somewhat off track.  I didn't run your code but in looking at your
ModelBase1.mxml the setter for baz is very wrong.  Your setter should not
be telling the sub object to dispatch an event, the setter always dispatches
an event on the class itself.  When you write [Bindable] metadata you are
creating a contract that the watchers will then interpret.



Class Foo extends EventDispatcher

{



  Private var _bar:Object;

  [Bindable(event="barChanged")]

Public function get bar:Object{ return _bar; }



Public function set bar(b:Object):void

{

  _bar = b;

  dispatchEvent(new Event("barChanged"));

}

}



The above code says that any instance of Foo will dispatch an event called
"barChanged" when that instance's bar references has been reassigned.



[Bindable]

var myFoo:Foo = new Foo(); //note that this probably doesn't work in real
code, assigning in the initializer to a [Bindable] doesn't work



<mx:Binding source="myFoo" destination="myOtherFoo" />



The only time that the above binding will fire is when I do something like
myFoo = someOtherFoo.  That binding should not fire if I do myFoo.bar =
someOtherBar.



Now if I have a binding like this:



<mx:Binding source="myFoo.bar" destination="myOtherFoo.bar" />



The binding will fire under two conditions:

1)        when myFoo is re-assigned to something else, myFoo =
someOtherFoo

2)        when the bar instance on myFoo is re-assigned: myFoo.bar =
someValue.

a.        Var mySecondFoo = myFoo; mySecondFoo.bar = someValue.  Note here
that mySecondFoo == myFoo.  So a change to the bar property is a change to
the same object, and therefore the bindings will fire.



I hope this helps clarify a little.  To re-iterate, when you write a
setter, for the purpose of binding you should never dispatch on the
sub-object, the only way you should see dispatchEvent is either on its own
or this.dispatchEvent.  Never this.<anything>.dispatchEvent.



Matt


 ------------------------------

*From:* [EMAIL PROTECTED] ups.com [mailto: flexcoders@ yahoogroups.com]
*On Behalf Of *Steve Hindle
*Sent:* Wednesday, December 06, 2006 9:50 PM
*To:* [EMAIL PROTECTED] ups.com
*Subject: *[flexcoders] Databinding & eventlistner Toy, Unexplained errors
and insights... Adobe Please Confirm



Ok - I think I'm making some headway now. Here's hoping this helps
someone else with similar problems.

For ages I've been plagued by 'odd' databinding problems. Things
would work most of the time - then fail for 'no reason'...

Playing around tonight, I noticed some behaviors that I think explain
a bunch of the problems I've been having. Perhaps others have had the
same problems, so here it is in a nutshell: It appears eventlisteners
and databinding bind to the object - and _not_ the identifier/label.
This holds true even for accessor based properties.

This means that having a 'currentRec', that gets reset to point to
each object in a list for instance, can be very dangerous. for
instance, if you have
var currRec:MyRecord
and you use it in mxml databindings, the bindings get applied to the
object currRec reference when the component was initialized! If you
later move another record into currRec, your databindings still point
to the FIRST object.

Could someone from Adobe confirm this?

Below is a little toy I wrote to test this stuff. The trace output
supports the statement above. Notice how after the assignment: baz =
bar, we start getting
trace output from the 'bar' eventlisteners. The 'baz' eventlisteners
no longer fire.
Play around with some of the commented out statements, and see how the
output changes - I was really surprised that statement ordering in the
setter made a difference!

Btw, If anyone can tell me why the PropertyChange handler doesn't
fire, I'd appreciate it! I thought 'PropertyChange' was the default
event used by databinding?

Trace output:
Assign 1 model to another
BAZ:Model Changed Event Generated! Event type: ModelChanged

Assign to accessorType
BAR:Model Changed Event Generated! Event type: ModelChanged

Assign to var based property - Why doesn't the 'PropertyChange' handler
fire?
call ModelBase.set()
BAR:Model Changed Event Generated! Event type: ModelChanged

Test completed baz.accessorType: Steve
BAR:Model Changed Event Generated! Event type: ModelChanged

==================================================
ModelBase1.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=" http://www.adobe.com/2006/mxml";
layout="absolute" creationComplete="setup()">
<mx:Script>
<![CDATA[
import ModelBase;
import DerivedClass;
import mx.binding.utils.BindingUtils ;
import flash.events.*;

private var _baz:DerivedClass = new DerivedClass();

[Bindable (event="ModelChanged")]
public function get baz():DerivedClass { return _baz; }
public function set baz(v:DerivedClass):void {
// Notice you get different handlers depending on which dispatch you use
// also, the 'this.baz' is needed because 'this.dispatch' or
'dispatch' would
// dispatch on the root tag (this = mx:Application)
this.baz.dispatchEvent ( new Event("ModelChanged"));
_baz = v;
//this.baz.dispatchEvent( new Event("ModelChanged"));
}

[Bindable (event="ModelChanged")]
private var foo:DerivedClass = new DerivedClass();
[Bindable (event="ModelChanged")]
private var bar:DerivedClass = new DerivedClass();

public function setup():void {

foo.addEventListener('ModelChanged', foomodelChanged);
foo.addEventListener ('PropertyChange', foopropertyChanged);
bar.addEventListener('ModelChanged', barmodelChanged);
bar.addEventListener('PropertyChange', barpropertyChanged);
baz.addEventListener('ModelChanged', modelChanged);
baz.addEventListener ('PropertyChange', propertyChanged);

trace("Assign 1 model to another");
baz = bar;

trace("\nAssign to accessorType");
baz.accessorType = "Steve";

trace("\nAssign to var based property - Why doesn't the
'PropertyChange' handler fire?");
baz.varType = 'Whee!';

trace("call ModelBase.set()");
baz.set(foo);

trace("\nTest completed baz.accessorType: " + baz.accessorType);
}

public function changeit():void {
//bar.accessorType = 'Changed';
baz.accessorType = 'Changed';
}

public function propertyChanged(e:Event):void {
trace("BAZ:Binding detected a change!");
}
public function modelChanged(e:Event):void {
trace("BAZ:Model Changed Event Generated! Event type: " + e.type);
}

public function barpropertyChanged(e:Event):void {
trace("BAR:Model Changed Event Generated! Event type: " + e.type);
}
public function barmodelChanged(e:Event):void {
trace("BAR:Model Changed Event Generated! Event type: " + e.type );
}

public function foopropertyChanged(e:Event):void {
trace("FOO:Model Changed Event Generated! Event type: " + e.type );
}
public function foomodelChanged(e:Event):void {
trace("FOO:Model Changed Event Generated! Event type: " + e.type );
}

]]>
</mx:Script>
<!-- I'd have expected this to work, but it doesn't as the object
referenced
by baz changes -->
<!--mx:Button id='b1' label="{baz.accessorType}" x="53" y="109"/>
<mx:Button id='b2' label="{baz.varType}" click="changeit()" x="53"
y="130" / -->

<!-- This works since bar always refers to the same object -->
<mx:Button id='b1' label="{bar.accessorType}" x="53" y="109"/>
<mx:Button id='b2' label="{ bar.varType}" click="changeit()" x="53"
y="130" />
</mx:Application>

==================================================
DerivedClass.as:
package {
import flash.events.Event ;
public class DerivedClass extends ModelBase {

private var _aType:String = "Start";

// How do we bind to a custom event in mxml ?
//[Bindable (event="ModelChanged")] - doesn't work for ModelBase1.mxml
[Bindable]
public var varType:String = "variable Based";

[Bindable (event="ModelChanged")]
public function set accessorType(v:String):void {
_aType = v;
dispatchEvent( new Event("ModelChanged"));
}
public function get accessorType():String { return _aType; }

public function DerivedClass() {
//
}

}
}

==================================================
ModelBase.as
package {
import flash.utils.*;
import flash.events.*;

public class ModelBase extends EventDispatcher {
public var dummy1:String = 'String';

public function ModelBase() { }

public function set(o:ModelBase):void {
this.dummy1 = o.dummy1 + " blah";
dispatchEvent( new Event('ModelChanged'));
}
}
}



Reply via email to