Broadcastable / Receivable¶
A broadcastable and receivable are observable
values that allow you to share data and state between two different areas of your application. They are based off of observables
and work by proxying their values over a namespace bus.
As you might have guessed, it has two parts:
-
receivable: observable that receives its value from a broadcastable.
-
broadcastable: observable that broadcasts, or syncs its value/state with other areas of your application.
// setup observable that broadcasts its value var broadcast = fw.observable('Hello').broadcast('broadcast', 'myNamespace'); // receive the value being broadcast var receive = fw.observable().receive('broadcast', 'myNamespace'); // The receive observable is now held in sync with the broadcast observable // receive() === 'Hello'
Note
Please be aware that there are some notes on instantiation order, and other context/use specific details that are covered in the usage notes section.
Receivable¶
Receivables are observables
that subscribe to and listen for a broadcastable
on a namespace
, keeping its value in sync. They are created by calling .receive(varName, namespace)
on an observable
:
// create the broadcastable that sends/sync with the receivable var myName = fw.observable('Smith').broadcast('myName', 'someNamespace'); // The receivable which receives/syncs with the broadcastable var receivedName = fw.observable().receive('myName', 'someNamespace'); // receivedName() === 'Smith' // write to myName (the broadcastable) which then updates receivedName myName('Jack'); // receivedName() === 'Jack'
Tip
You can also pass a namespace instance as the second parameter:
var someNamespace = fw.namespace('someNamespace'); var myName = fw.observable().receive('myName', someNamespace);
Writing To Receivables¶
Receivables can be written to just like any other observable property. Depending on the configuration of its corresponding broadcastable, there are two scenarios to be aware of:
-
The corresponding broadcastable is not configured to be writable or not instantiated
If you write to a receivable when its broadcastable is not configured to be writable, or no corresponding broadcastable is instantiated, then the value is not changed. This is because it updates only when the corresponding broadcastable sends it a new value.
If no broadcastable is listening, or it is not configured to be writable, then the value is not written to it (and thus does not propogate back to any receivables).
-
The corresponding broadcastable is configured to be writable
If you write to a receivable whos broadcastable is configured to be writable, then the value is written to the broadcastable and propogates out to any corresponding receivables (including the one that was written to).
For more information see: Writable Broadcastables
Broadcastable¶
Broadcastables are created by calling .broadcast(varName, namespace)
on an observable
. The varName
is the name which the other side will listen for, and the namespace
is the bus to broadcast the value over.
var myName = fw.observable('test').broadcast('myName', 'someNamespace');
There are two primary modes a broadcastable can be created in:
-
Sync is one way, from broadcastable to receivable. Values written to the broadcastable propogate to its receivables but not vice-versa.
-
Sync can occur in both directions. Values written to a receivable propogate back to its corresponding broadcastable.
Tip
-
Just like with receivables, you can also pass a namespace instance as the second parameter:
var someNamespace = fw.namespace('someNamespace'); var myName = fw.observable().broadcast('myName', someNamespace);
-
If you are creating a
broadcastable
from within the context of a viewModel, dataModel, or router then you can also pass it the instance and it will use itsnamespace
:function MyViewModel () { var self = fw.viewModel.boot(this, { namespace: 'MyViewModel' }); this.myName = fw.observable().broadcast('myName', self); }
This can be especially helpful when you have several properties to broadcast and want to keep your code DRY.
Read-Only Broadcastable¶
By default a broadcastable is read-only. This means that it broadcasts only (syncing is one-way, from broadcastable to receivable). A receivable cannot write back to a read-only broadcastable.
var myName = fw.observable('test').broadcast('myName', 'someNamespace');
As a general rule is its best to use the same variable name when broadcasting. This helps avoid confusion when wiring up any corresponding receivables for it.
Writable Broadcastables¶
You can optionally pass a 3rd boolean parameter to .broadcast
which (if true) makes it a writable broadcastable
. A writable broadcastable
will allow any receivable
to write back to it.
For example, we can create a writable broadcastable like so:
var myName = fw.observable('Smith').broadcast('myName', 'Person', true);
We can then create a receivable which receives/syncs with the broadcastable:
var receivedName = fw.observable().receive('myName', 'Person'); // receivedName() === 'Smith'
receivedName
is instantiated and is set to the current value of myName
(its corresponding broadcastable).
If we then write to receivedName
(the receivable), the value is written to the broadcastable which then propogates back to the receivable receivedName.
receivedName('Jack'); // myName() === 'Jack' // receivedName() === 'Jack'
Usage Notes¶
There are a few special conditions where the behavior of a broadcastable and receivable need to be defined:
Instantiation Order¶
When a receivable
is instantiated it will request the current value from its corresponding broadcastable
. This means it does not matter the order in which you instantiate them.
Creating the broadcastable first:
var myName = fw.observable('Jerry').broadcast('myName', 'someNamespace'); var receivedName = fw.observable().receive('myName', 'someNamespace'); // receivedName() === 'Jerry'
The above example shows that when a receivable
is instantiated it retrieves the current value from its corresponding broadcastable
(if it exists).
Creating the receivable first:
var receivedName = fw.observable().receive('myName', 'someNamespace'); // receivedName() === undefined var myName = fw.observable('Jerry').broadcast('myName', 'someNamespace'); // receivedName() === 'Jerry'
The example above shows when myName
is initialized it then updates receivedName
even though the myName
is created after receivedName
. This is because upon instantiation, a broadcastable
broadcasts the current value which any listening receivables
then is updated with.
Multiple Broadcastables¶
If you create multiple broadcastable
instances that all point to the same variable, then:
- Each
broadcastable
retains their local value, regardless of what the other broadcastables are set as. - When instantiated,
receivables
will receive all of the current broadcastable values, but only retain the last. - The
receivables
will have the most recently broadcasted value.
Using Non-Primitive Types¶
Non-primitive types (such as Arrays
or Objects
) are passed by reference. This means that if you broadcast/receive one of these pass-by-reference values, each side will have a direct reference.
For example:
var thisThing = fw.observable().broadcast('thisThing', 'someNamespace'); var thatThing = fw.observable().receive('thisThing', 'someNamespace'); // log any changes to thatThing to the console thatThing.subscribe(function(newThings) { console.log(newThings); }); // modify thisThing which then propogates to thatThing, which then gets logged thisThing(['this array', 'of values', 'is not', 'a primitive']); // console logs: ['this array', 'of values', 'is not', 'a primitive']
If you then modify the observables value directly, the other side will not be notified of the changed:
var thisThingData = thisThing(); // modify thisThing, but directly. thisThingData.push('it was modified'); // console does not log anything // thatThing() === ['this array', 'of values', 'is not', 'a primitive', 'it was modified']
Above you can see that when we modify the underlying reference to the thisThing
(which happens to be an Array
) the value of thatThing
is also updated...but nothing is logged to the console because we modified the underlying reference to the value...which Footwork doesn't know about.
This may be OK for your application, as long as you are aware of its consequences. You can avoid this issue by only using broadcast
and receive
with primitive values.
Lifecycle¶
Both receivables and broadcastables make a subscription to the designated namespace
bus, because of this they need to be disposed of before they can be garbage collected (and freed from memory).
To dispose of them, you call the dispose
method:
var receivedName = fw.observable().receive('myName', 'someNamespace'); // ... some time later receivedName.dispose();