Draft 0
Last modified on 2/16/99 by Mike Pinkerton
This is a concatenation of some relevant emails and newsgroup postings about command parameter. Hyatt and Rod are behind most of this, blame them :-).
Much of the XPToolkit is based on the observer concept. A widget's implementation observes its content model representation, the AOM node, for changes. The AOM node in turn observes a Command node, just another AOM node, to see if the command that the widget surfaces in the UI is enabled. The command node observes some property of an AppCore/Service to determine its enabled state. All of the above interactions have been discussed, hinted at, worked around, or otherwise assumed in previous XPToolkit papers and brownbag discussions. Obviously the observer concept is fundamental to XPToolkit, but it has so far been hidden inside of discussions about the command architecture.
Earlier proposals from the XPToolkit team encouraged the use of Command nodes to separate user interface elements from the application code which implemented behaviors. Unfortunately, command nodes became the dumping ground for just about every bit of functionality and glue related to tying together application core objects and UI elements, and as a result, became a big complicated mess.
Our new proposal introduces Broadcaster and Observer nodes in the AOM. These new nodes retain the original goal of Command nodes (that is, allowing multiple reusable UI elements to observe changes in app core state) but aren't as muddled down with extra baggage as Command nodes are. In fact, given a combination of event handlers on UI elements and Broadcaster/Observer nodes, and appCore reflection into JavaScript, Command nodes are no longer necessary.
The observer XUL construct is intended as a mechanism for communication between AOM nodes. It simply allows the AOM node observer relationships to be specified in XUL so that they can be automatically set up at XUL load time.
<broadcaster id="nodeIdentifier" />
Attribute |
Description |
---|---|
id |
a tag so that other XUL, such as the <observes> tag, can reference the broadcaster |
<observes element="nodeIdentifier" attribute="AttributeIdentifier" onchange="...some JavaScript" />
Attribute |
Description |
---|---|
element |
the id of the broadcaster being observed. |
attribute |
the attribute of the parent object which should be updated when that attribute changes on the broadcaster. |
onchange |
an event handler called when the attribute changes on the broadcaster. |
<window> ... <broadcasterset> <broadcaster id="cantSendMail"/> <broadcaster id="BrowserCore_LoadProgress" percentage=0 /> </broadcasterset> .... <titledbutton id="sendMailButton" onclick="mailCore.sendMessage(document.forms[0], document.forms[1], document.forms[2])"> <observes element="cantSendMail" attribute="disabled"> </button> <titledbutton id="someOtherButton" onclick="mailCore.sendMessage(document.forms[3], document.forms[4], document.forms[5]"> <observes element="cantSendMail" attribute="disabled"> </button> <progressmeter> <observes element="BrowserCore_LoadProgress" attribute="percentage" /> </progressmeter> ... </window>
In the above example, different form elements are passed in depending on which button you click. This is really how you'd like it to work. The AppCore has no knowledge of the UI; it simply gets some parameters and knows what to do. Any direct UI manipulation happens at the event-handling level.
In other words, you don't have to use Command nodes. We also have this notion of broadcaster nodes (with a command being a specific type of broadcaster). Assuming that the appCore is exposed somehow as a JS object with its methods reflected into JavaScript, and that it knows the IDs of the broadcaster or Command nodes it wants to poke on to do enabling/disabling, you could do the right thing by making the command invocation (in this case a sendMessage call) happen at the widget level, and then passing in the elements that are appropriate based on which widget you're dealing with.
I'm sure many of you are thinking, "Why is all the logic enabled/disabled logic seemingly backwards?" Recall that the DOM attribute for enabled/disabled state of an HTML4 <button> tag is "disabled," and as a result, the properties must be phrased in those terms. At some point in the future, we will try to add the ability to observe the negative of an attribute, but this mechanism in not currently present.
If one takes a step back and looks at the command architecture, you can see the observer relationship in it. The widget observes the command's enabled state. Since the command is actually fulfilled by appcore code, and the AppCore code decides when to enable or disable the command, it is really the AppCore that owns the command node. The command node is surfacing a capability of the AppCore to the widget via the AOM in much the same way that a broadcaster node surfaces a state of the AppCore to the widget.
The general observer mechanism implies an observed persistent state. The AppCore has a state, such as "LoadProgress" which is expressed via an AOM node with a progress percentage as a parameter. This is a persistent state of the AppCore which is then expressed through the AOM node.
Contrast this with a command node. A command node is a binding between the widget's AOM node and some code that fulfills the function of the widget. A command node is not a representation of persistent state (with the exception of the command's enabled state). Primarily, the command node is simply a transport path for getting something done in response to a user interacting with a widget.