< PreviousChapter 6: Solution120Figure 6.3: User Interface Ontology DiagramCompositeUser InterfaceWidget+ContainerControlVisualControllerModelViewComponentBackingObjectparentchild0..1*parent0..1User InterfaceFragment ClassificationUser InterfaceCompositionUser InterfaceComponent RolesUser InterfaceComponent Treewrapperbacking0..10..10..10..1TriadStoryboardWireframeAreaInteraction++starts atends atUser InterfaceSpecificationFragmentcorresponds torepresentsimplemented byimplemented by**PanelDialogends atchild***User Interface Ontology1216.3.3 User Interface Fragment ClassificationThe conceptual items of the User Interface Composition can be sub-classed into the classifications Composite and Widget. Those, in turn, can be sub-classed into Panel, Dialog, Container, Control, and Visual. The corresponding taxonomy excerpt is: ■Composite: High-level Fragment; either an orchestrating Panel or interact-ing Dialog. ■Widget: Mid-level Fragment; either an orchestrating Container, an interact-ing Control or a non-interacting Visual. ■Panel: Composite, mainly orchestrating multiple contained Fragments. ■Dialog: Composite, mainly interacting with the user through contained Widgets. ■Container: Active Widget, mainly logically grouping other Fragments. ■Control: Active Widget, mainly interacting with the user through input mechanisms like keyboard, mouse, touch-screen, and many more. ■Visual: Passive Widget, just showing content textually and/or graphically.6.3.4 User Interface Component TreeAssuming that the implementation follows the Component-Orientation paradigm and a Component Tree structure, a hierarchical structure of Components and their corresponding, separated Backing Objects results. The corresponding taxonomy excerpt is: ■Component: Object-oriented grouping of data and behavior, wrapping a Backing Object; usually in the form of generic functionality provided by a framework. ■Backing Object: Object-oriented grouping of data and behavior, backing a Component; usually in the form of domain-specific functionality provided by the application.6.3.5 User Interface Component RolesIndependent of any used particular Model-View-Separation pattern, one usually always has the three roles Model, View, and Controller for individ-ual Components in the implementation. The resulting triad of Compo-nents maps onto the Composite and Widget classes. The corresponding taxonomy excerpt is: ■Model: Passive/data-only Component, dedicated to host (and perform logical operations on) values (parameters, commands, states, data Model-View-Controller is a particular pattern of three distinct Compo-nents, but Model, View and Controller are just logical roles of those Components.Chapter 6: Solution122and events) to serve a View. This primarily deals with the UI aspect group Data (see page 117). ■View: Active/code-driven Component, dedicated to displaying and inter-acting with a view mask, based on a bidirectional binding to values in a Model. This primarily deals with the UI aspect group Mask (see page 117). ■Controller: Active Component, dedicated to performing presentation pro-visioning to a Model and presentation actioning from a Model. This primar-ily deals with the UI aspect group Connectivity (see page 118).6.4 Hierarchical Component StructureBad programmers worry about the code. Good programmers worry about data structures and their relationships. — Linus TorvaldsComponents and their orchestration in the Component Tree, apparently, are the central concepts in the Hierarchical User Interface Component Architecture (HUI-CA). Each Component follows the Object-Orientation paradigm and belongs as an element to the data structure Component Tree.6.4.1 Component Object and Backing ObjectLogically, the User Interface implementation consists of code spread onto a set of Component classes, in the sense of code structures in Object-Oriented Program-ming (OOP). The drawback of this approach is that those Component classes usu-ally have to inherit from certain abstract framework classes of the Component Sys-tem to have the common Component API methods at hand and to be reasonably managed by the Component System.On nearly all technology platforms, since over a decade, this OOP in-heritance-based approach is strongly frowned upon [Gamma et al. 1994] [Lowe 2015], because it effectively prevents the use of multiple frame-works in parallel. It is often stated in practice with the motto “Composi-tion/Delegation over Inheritance.” Hence, the modern approach is to use The distinction between Component Object and Backing Object allows more flexibility in the im-plementation of the un-derlying OOP classes.Figure 6.4: Component and Backing ObjectComponentBackingObjectcs(obj)comp.obj()Hierarchical Component Structure123bare OOP classes at compile-time and then, to control the Components during run time, either meta-programming-based annotations or on-the-fly wrapper objects are used. As the Java Script language, in contrast to Java and until ECMAScript 2018, does not have annotations, the latter approach is chosen for HUICA by default.In HUICA, the Components during compile time are bare OOP classes initially. Once instantiated into Components during run time, they technically become so-called Backing Objects and are being bidirectionally linked one-to-one to a cor-responding automatically instantiated Component Object (see Figure 6.4 on page 122). These Component Objects are of an OOP class of the Component Systems, providing the Component API.To call methods on this pair of objects, a central Component System API func-tion cs() (see also Component Lookup function below) and a Component Object API method obj() is used:To call method.......on Component Object comp...on Backing Object thisown foo()comp.obj().foo()this.foo()Component API bar()comp.bar()cs(this).bar()Usually, the access via the Component Object comp is never seen in practice, as all the application code is at the Backing Object level (where this reasonably points to in all coded methods). Hence, the this.foo() and cs(this).bar() are the only programming constructs one usually can see in practice.For every Backing Object, there is exactly one Component Object. However, for each Component Object, there can be zero or one Backing Object because one can also create Components for bare namespace and grouping purposes.The concept of the function cs(), which logically on-the-fly wraps the Backing Object in order to access the Component API on them, is derived from the jQuery $() function, which on-the-fly wraps DOM elements in order to access the jQuery API on them.6.4.2 Component TreeEach Component Object is one-to-one tied to (zero or) one corresponding Backing Object. While the Backing Objects are just logically and never technically linked to each other, the Component Objects are technically linked to each other in a hierar-chical data structure: the Component Tree (see Figure 6.5 on page 124).The reason for a tree simply is the fact that every kind of User Inter-face inherently follows a tree structure: the optically and hierarchically structured dialogs. Hence, it is self-evident and reasonable to model the Components hierarchically, too. This realization is central to HUICA, including all its implications like Hierarchical Component States (Section 6.5 on page 74), Hierarchical Component Communication (Section 6.6 on page 135), Hi-The Component Tree is the primary concept in the Hierarchical User In-terface Component Ar-chitecture.Chapter 6: Solution124erarchical Dialog Composition (Section 6.7 on page 147) and Hierarchical Dialog Architecture (Section 6.8 on page 150).The Component Tree starts with a namespace-only root Component. From there, new Components can be created by calling cs.create(parent, name, cl-assOrObject) where parent is an existing parent Component Object, name is the Component path element name (see Subsection 6.4.3 on page 125) of the new Component and classOrObject is either the class of the Backing Object to instan-tiate or the already pre-instantiated Backing Object.As an example, assuming that cs.root is a reference to the root Component, comp = cs.create(cs.root, “foo”, Foo) would instantiate the class Foo and insert it into the Component Tree under name foo, directly below the root Com-ponent. A subsequent call to cs.destroy(comp) or alternatively in the OOP-style comp.destroy() would remove the Component (and all its potential child Components) again. Additional child Components can be created accordingly.A Component Tree in the traditional visualization of Computer Sci-ence is usually drawn with the root Component at the top and child Components towards the bottom. In the context of HUICA, it is recom-mended to draw the Component Tree in the natural visualization, with the root Component at the bottom and with child Components towards the top.The reason for this is that users of any system are logically always located at the top of any visualization. If the user then “looks” from the top onto the dialogs, represented by the Components in the Component Tree below the user, this pro-jection is a natural one if the root Component is at the bottom. Because optically the user first sees the leaf dialogs, then the surrounding dialogs and finally the outermost root dialog (see Figure 6.6 on page 125). Component Trees are usually visualized in HUICA in the “natural” di-rection, i.e., with the root Component at the bot-tom.Figure 6.5: Component TreefoorootbarquuxfoorootbarquuxTraditional Component TreeNatural Component TreeHierarchical Component Structure1256.4.3 Component Path and Component Lookups Just as the jQuery $() function also can search for DOM elements via a CSS selector expression, in HUICA the cs() function can also search for a Component via a path query expression. The path query expression syn-tax somewhat resembles the Unix filesystem path Glob Pattern syntax and adds the possibility for traversing entire sub-trees. The correspond-ing Parsing Expression Grammar (PEG) for the path query expression is:Path::=PathAbsolute | PathRelativePathAbsolute::=“/” PathRelativePathRelative::=PathElement (“/” PathElement)*The Component Path Lookups are inspired by Unix filesystem paths and the jQuery DOM selec-tors.Figure 6.6: User Interface Optical View ProjectionrootfoorootbarquuxfooquuxbarComponent TreeUser InterfaceOptical ViewUserChapter 6: Solution126PathElement::=“” | “*” | /^[^\/]+$/Based on these path expressions, the cs() function finally has the following par-ticular signatures for looking up a Component Object:cs(path: PathAbsolute): Componentcs(base: BackingObject | Component, path: PathRelative): Componentcs(base: BackingObject | Component): ComponentThe first signature allows to global lookup a Component, based on an absolute path. The second signature allows to lookup a Component, relative to an existing Component (either specified directly or via its Backing Object). The third signature allows the previous on-the-fly mapping from a Backing Object to its Component or the convenient passing-through of a Component.In any case, even in case of the use of wildcards in the path query expression, the result has to be a single Component. Matching more than a single Component should raise an exception. With this, the following practically needed lookups are now possible in practice:cs(“/foo/bar”) /* absolute */ cs(“//foo”) /* absolute full-tree */ cs(comp, “foo/bar”) /* relative */ cs(comp, “*/foo”) /* wildcard */ cs(comp, “//foo”) /* sub-tree */ cs(comp, “..”) /* parent */ cs(comp, “..//foo”) /* parent sub-tree */ cs(comp) /* just passing-through */ cs(this) /* via backing object */6.5 Hierarchical Component StatesAll the good things you want to do in your life have to be started in the next few hours, days or weeks. — Tom DeMarcoA User Interface (UI) Dialog inherently is stateful, because of its life-cycle of being created, prepared, rendered, becoming visible, or becoming active. When imple-menting a UI Dialog as a Component, this Component has to be stateful and has to have a life-cycle, too. In HUICA the twist is that although each Component has its own state, the state transitions are hierarchically or-chestrated by the Component System.The hierarchical man-agement of Component States is one of the key concepts and major fea-tures in HUICA!Hierarchical Component States1276.5.1 Component StatesComponents can be in various particular states. The current state can be deter-mined and changed with the Component API method of signature state(state?: StateName): StateName. Components can be triggered to change their state with a method call state(StateName). During those state transitions, optionally enter and leave methods in the Backing Object can be called accordingly.The particular existing states and their corresponding enter and leave meth-ods are arbitrary. The Component System just provides a reasonable default, which can be defined with cs.transition(state: StateName, enter: MethodName, leave: MethodName). A state S1 is considered “lower” than state S2 if the state defining call cs.transition(“S1”, ...) is executed before the state defining call cs.transition(“S2”, ...) during run time of the application.Once a Component entered the target state from a lower previous state, the optional Backing Object enter method is called. Once a Component left the target state towards a lower following state, the optional Backing Object leave method is called. A reasonable default set of state transitions is (given in the order of lowest to highest state):cs.transition(“created”, “create”, “destroy”) cs.transition(“configured”, “setup”, “teardown”) cs.transition(“prepared”, “prepare”, “cleanup”) cs.transition(“materialized”, “render”, “release”) cs.transition(“visible”, “show”, “hide”) cs.transition(“ready”, “enable”, “disable”)The rationale for those particular default states is: ■created: this state is the state after the initial cs.create() and left only on the final cs.destroy(). Hence, it is treated specially inside the Compo-nent System and the corresponding enter/leave methods are not usable by the Component itself. ■configured: this state allows the Component to configure itself, usually by loading and parsing configuration parameters in the setup method and unloading them again in the teardown method. ■prepared: this state allows the Component to prepare itself, usually by al-locating resources in the prepare method and freeing them again in the cleanup method. ■materialized: this state allows the Component to materialize it-self, usually by rendering (but still not making visible) its DOM fragment and activating the data binding in the render method and freeing the DOM fragment again in the release method. ■visible: this state allows the Component to make itself visible, usually by attaching its (until this time still floating) DOM fragment into The state which attaches the DOM fragment into the global DOM (here the “visible” state) is the most prominent and recog-nized state in practice.Chapter 6: Solution128the global DOM or by making the (at this time already attached) DOM frag-ment optically visible (compare the CSS display property) in the show method and reversing the operating in the hide method accordingly. At this state, the DOM fragment usually contains deactivated control ele-ments. Interface Effects usually are also started in this state. ■ready: this state allows the Component to make itself ready for interaction, usually by activating all control elements in the DOM fragment in the en-able method and deactivating all control elements again in the disable method. This state should be not reached before all Interface Effects were finished. It is even acceptable that some widgets are intentionally techni-cally disabled in this state because of their logical state.At the bottom of the state transition hierarchy, there is always an additional state named dead. This state is entirely internal to the Component System and exists just to get rid of special cases in the state transition handling. If appropriate, except for the dead state, the application can define its own custom set of arbitrary states.6.5.2 Component Life-CycleOver the time of their existence, Components transition between states. This leads to a particular life-cycle, defined by the time, the states and the particular state transitions.One can trigger a state transition on a Component at any time, at any source state and to any target state. If the source and target states are the same, nothing happens, of course. If they are different, the Component will go to the target state, after it first went through all potentially existing intermediate states.Being able to trigger ar-bitrary state transitions is the killer feature pro-vided by the Component System.Figure 6.7: Example Component Life-CycleconfiguredpreparedvisiblematerializedcreateddeadpreparerendershowcleanupreleasehidereadydisableenabletimeHierarchical Component States129For example, assuming the Component comp is at state configured, then a comp.state(“enabled”) will attempt to call, in sequence, the enter methods prepare(), render(), show() and enable(). A subsequent comp.state(“configured”) will attempt to call, in sequence, the leave methods dis-able(), hide(), release() and cleanup(). This example then will lead to the par-ticular life-cycle in Figure 6.7 on page 128.6.5.3 Component State Transition InvariantsDuring run time of a User Interface, arbitrary state transitions can be triggered on arbitrary components and at an arbitrary time. To ensure that, despite those state changes local to a single Component, the Component Tree as a whole remains in an overall consistent state, at any time two invariants have to hold and be enforced by the Component System:This especially implies: before increasing the state of a target Component above the state of its parent Component, the parent Component first has to increase its state to the target state of the target Component. As the state transition on the parent Component might first have to trigger a state transition on its own parent Component, this state transition proce-dure is fully recursive.Enforcing the Compo-nent State Transition Invariants is the heart of the Component System and the key to a consist-ent UI rendering.Figure 6.8: Example Component Tree213456rootleafNext >