Skip to content

Example: Observing Property Values

richardszalay edited this page May 17, 2011 · 1 revision

The intent of this example is to create an IObservable that contains the values of a property as they change over time.

Our class looks like this:

public class BankAccount extends EventDispatcher
{
    [Bindable]
    public function var balance : Number;
}

Our requirements is to have a sequence that contains the values of the balance property, and has the following specifications:

  • It should emit new values when the property changes
  • It should start with the current value

Flex supports property change notification using the PropertyChangeEvent event, so let’s start with that:

var propertyValues : IObservable = Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE);

There are a few problems with just using the event alone:

  • It contains all property change events, not just the “balance” property
  • It contains PropertyChangeEvent values, not Number values
  • It doesn’t contain the current value (it won’t emit a value until the property changes)

Let’s start by filtering the events using filter:

var propertyValues : IObservable = Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE)
    .filter(function(ev : PropertyChangeEvent) : Boolean { return ev.property == "balance"; });

Then we can use map to project the value of the property, replacing the event value:

var propertyValues : IObservable = Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE)
    .filter(function(ev : PropertyChangeEvent) : Boolean { return ev.property == "balance"; })
    .map(funtion(ev : PropertyChangeEvent) : Number { return ev.newValue; });

Finally, we will return the current value of the property using startWith:

var propertyValues : IObservable = Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE)
    .filter(function(ev : PropertyChangeEvent) : Boolean { return ev.property == "balance"; })
    .map(funtion(ev : PropertyChangeEvent) : Number { return ev.newValue; })
    .startWith(account.balance);

Final Code

Now we can put it all together:

var account : Account = new Account();
account.balance = 50;

getAccountBalanceValues(account).subscribe(function(balance : Number) : void
{
    trace("The balance is now " + balance);
});

account.balance = 100;
account.balance = 0;
account.balance = 25;

private function getAccountBalanceValues(account : Account) : IObservable
{
    return Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE)
        .filter(function(ev : PropertyChangeEvent) : Boolean { return ev.property == "balance"; })
        .map(funtion(ev : PropertyChangeEvent) : Number { return ev.newValue; })
        .startWith(account.balance);
}

The trace output of this application is:

The balance is now 50
The balance is now 100
The balance is now 0
The balance is now 25

Fixing the “current value”

In the above example, we used startWith to include the current value at the start of the stream. The problem with passing the value straight in is that it is evaluated when getAccountBalanceValues is called, not when the IObservable is subscribed to. This is fine when they occur consequtively, but not when the IObservable is stored as subscribed to later.

To avoid this bug, let’s use defer to resolve the value when the subsciption occurs. We can still use startWith as it converts it’s argument to a sequence using toObservable, which means we can pass it an IObservable and it will start with that:

private function getAccountBalanceValues(account : Account) : IObservable
{
    return Observable.fromEvent(account, PropertyChangeEvent.PROPERTY_CHANGE)
        .filter(function(ev : PropertyChangeEvent) : Boolean { return ev.property == "balance"; })
        .map(funtion(ev : PropertyChangeEvent) : Number { return ev.newValue; })
        .startWith(Observable.defer(function():IObservable
        {
            return Observable.value(account.balance);
        });
}

Using the property values

Here are some ways you could use the IObservable of property values that we’ve created:

  • Combine two different property values using combineLatest to be notified when either property changes
  • Use filter to have a sequence that notifies only when the account balance goes below 0