ZUI – Zing User Interface Overview

ZUI or the Zing User Interface is a set of tools for rapidly building user interfaces. ZUI is integrated with the Zing data model so as to facilitate building client server user interfaces.

The initial ZUI implementation is based on JQuery and will build HTML-based user interfaces. ZUI interfaces adopt the Angular one-page web model where there is a single download of the user interface and then movement between pages is performed entirely within the browser. This makes for much more efficient user interfaces than those requiring frequent HTML refresh.

In architecture, however, ZUI more resembles React and React Native. There are important differences. React Native use HTML embedded in the code which ZUI does not. ZUI uses standard TypeScript constructors to build up user interfaces.

There are several parts to understanding ZUI. They are:

Rendering the UI

ZUI does, however, have a rendering style architecture where every component has a renderJQ():JQuery method that returns a JQuery object. This is the basic architectural technique for building a user interface. It is intended that in the future new rendering methods will be added to the base component library. For example a renderReactN() method might render the component into React Native objects (thus making ZUI more platform independent).

The user interface is rendered whenever ZUI.notify() is called. This will happen automatically whenever new data is received from the server. Whenever your code responds to a user input, it should call ZUI.notify() to render the user interface and reflect any changes that have been made.

The ZUI class

The ZUI user interface architecture revolves around the the ZUI class. All components, including Page, inherit from the ZUI class. In the following sections we will show how the ZUI class is used throughout to create new user interfaces using a few simple ideas.

Page management

Page management in ZUI shares the Angular model where there is one download with page switching happening locally in the browser. Page management is built around four classes: PageManager, Page, PageState and ZUI.

PageManager class

The PageManager class controls everything about page management. It begins with the constructor, as shown below:

let dataSource = create a new data source
ZUI.pageManager = new PageManager(dataSource,new LoginPage(),"#content");

When creating a new PageManager you give it the DataSource where it will find its data, the page object to be used as the home page and a selector to identify where in the page HTML DOM tree the PageManager should place its user interface. A more complete example is shown in setting up a client in the example application. Placing the newly created PageManager into ZUI.pageManager lets the system know that this page manager will be controlling all pages. It will also display the home page that was provided.

Page Navigation

Page navigation is handled by two static methods defined on PageManager.

PageManager.GOTO(pageName:string,newState?:PageState)

This method will transition to the page named by pageName and will give it the state information in newState. If newState is missing the state information from the current state will be passed on. The GOTO method does not push the current page onto the browser’s back button stack. The back button will go to the previous page rather than the current page.

PageManager.PUSHTO(pageName:string,newState?:PageState)

The PUSHTO method behaves the same as the GOTO method except that the current page is pushed onto the back button stack so that pushing the back button will return to the current page.

User Management

PageManager.getUserManager():UserManager

This static method will return the current user manager. The user manager controls who is logged in.

PageManager.getUser():DataObj

This will return the data object that describes the currently logged in user.

Page Registration

PageManager.registerPageFactory(pageName:string, factory:(state:PageState)=>Page)

The registerPageFactory method is the mechanism for telling the page manager about the various pages that are defined in the user interface. The pageName is a string that names the page. This name can be used in GOTO and PUSHTO to identify the destination page. The factory is a function that will be called to create the page. A function is used so that the page can be dynamically calculated based on the state. The function also allows the page data not to be instantiated until it is actually needed.

Page class

The Page class is quite simple. It extends ZUI and uses the ZUI mechanisms for building out a user interface. The next section will describe how to build out user interfaces in ZUI.

The constructor takes the PageState that describes the data needed by the page.

It is very important that after declaring a Page class, the registerPageFactory method is used. For example:

class BookPage extends Page {
    constructor(pageState:PageState){
        super(pageState);
        this.content = new DivUI([
             new TextUI(pageState.bookTitle),
             new TextUI(pageState.bookAuthor)
        ]);
    }
}
PageManager.registerPageFactory("bookPage",
     (newState:PageState)=>{
          return new BookPage(newState);
     });

New ZUI components

New ZUI components are created either as a subclass of ZUI or one of its subclasses. There are basically two techniques for creating a new ZUI component. The first is to build a structure of existing components and assign them to this.content. The second is to implement your own renderJQ method and build the user interface out of JQuery parts. The first is the preferred method because it is more portable but sometimes you just need something custom in which case the second method is available.

Using existing components

Suppose that we want a component that displays the name of a student based on a Student data object. Using existing components exclusively means that your code is independent of the underlying platform. The following StudentNameView class will do just that.

1)  class StudentNameView extends ZUI {
2)      constructor(studentKey:string){
3)           this.content = new DivUI([
4)               new TextUI(()=>{
5)                    let st = Student.cGET(studentKey);
6)                    if (st){
7)                         return st.getFirstName();
8)                    } else {
9)                         return "<i>first name</i>";
10)                   }
11)              }).style("StudentNameView-first"), 
12)              new TextUI(()=>{
13)                   let st = Student.cGET(studentKey);
14)                   if (st){
15)                        return st.getLastName();
16)                   } else {
17)                        return "<i>last name</i>";
18)                   }
19)              }).style("StudentNameView-last") 
20)          ]).style("StudentNameView");
21)     }
22) }

Using existing components, most of the user interface lives in the constructor of the class. Line 3 creates a new DivUI and assigns it to this.content. This is the basic mechanism for expressing the content of the view class. The DivUI has two subcomponents. One for the first name (line 4) and one for the last name (line 12). These are both TextUI components and used in the same way. Instead of taking a plain piece of text as a parameter they take a function that dynamically retrieves the text from a Student object.

This StudentNameView class has a renderJQ() method. It is inherited from ZUI and functions by using the renderJQ() methods of the library components.

Custom renderJQ() method

We can build the same StudentNameView class using only the renderJQ() method. This is more flexible, but also more dependent on JQuery. The following is the same StudentNameView class functionality implemented ina different way. The purpose of the renderJQ() method is to return a JQuery object that has the desired functionality.

1)  class StudentNameView extends ZUI{
2)       studentKey:string;
3)       constructor(studentKey:string){
4)           this.studentKey=studentKey;
5)       }
6)       renderJQ():JQuery {
7)           let divui = $('<div class="StudentNameView"></div>');
8)           let student = Student.cGET(this.studentKey);
9)           if (student){
10)               let fnui = $('<div class="StudentNameView-first">'+
11)                    student.getFirstName()+'</div>');
12)               divui.append(fnui); 
13)               let lnui = $('<div class="StudentNameView-last">'+
14)                    student.getLastName()+'</div>');
15)               divui.append(lnui); 
16)          } else {
17)               divui.html("<i>student name</i>");
18)          }
19)          return divui;
20)      }
21) }

In this implementation all of the interesting code is in the renderJQ method. This is all standard JQuery code for building up a DOM tree that implements the StudentNameView class.

ZUI component library

Most of the time user interfaces are built from existing components. Here is a list of the components with links to their descriptions

  • BreakUI – creates a horizontal break in the layout
  • ButtonUI a basic clickable button
  • ClickWrapperUI – a component that makes any other component or set of components clickable.
  • ColorPickerUI – provides a simple color picker
  • DateTimeUI provides several forms of input for dates and times.
  • DivUI – collects together several components into a single group. This is the key structuring component for building complex user interfaces.
  • DoneIndicatorUI – a simple horizontal bar that indicates percent done.
  • DragDropWrapperUI – a wrapper that provides a drag and drop functionality to any ZUI component.
  • DropDownChoiceUI – presents a number of text choices in a drop-down button.
  • FileDropTargetUI – this wraps around a number of other components and supports dropping a file onto those components.
  • HTMLEditUI – a full HTML editor.
  • IconButtonUI – a button built from an image rather than text
  • IFrameUI – inserts an <iframe> that can contain another web page
  • ImageUI – an image or rotating list of images.
  • KeyListUI – most of the find methods in the data model return a list of keys. This component will present the objects represented by those keys in a sorted list.
  • Messages – a component that displays all messages from the system.
  • Modal – a component that takes over the window and requests a response from the user.
  • OpenCloseUI – a component with a header line that can open into more information and then close again.
  • SelectUI – a component that will dynamically choose among a set of other components to adapt the user interface to the underlying data.
  • SliderUI – implements a simple horizontal slider
  • StyleCheckUI – implements a generalized check box that chooses between two styles.
  • TableUI – implements an HTML table of various components
  • TabUI – implements a basic tabbed user interface
  • TextFieldUI – implements a text typin box.
  • TextUI – displays a fixed or dynamic string of HTML

SCSS styling and layout

All of the styling for Zing user interfaces is built around SCSS. There are several capabilities that just makes things easier than plain CSS. Of course, your build process must compile the SCSS files into CSS files. There is default styling for all of the components in the library. There is also a layout system for formatting user interfaces using a twelve column grid. To make this all work you should have your SCSS file import Zing’s SCSS declarations. This is done as follows.

@import <your local folder>/parameters.scss
@import <folder for your Zing source>/zui/zuiMain.scss

You should copy “zui/parameters.scss” into your own folder area. This file contains a set of SCSS parameters that control the styling of the ZUI components. You can either keep these parameters as you copied them or you can modify them to your own tastes. Mostly these parameters are colors and fonts. They must be imported before zuiMain.scss so that the values will be available.

Every component in the library has some default style classes that control the appearance of that component. You may augment these by declaring your own versions after your @import of the Zing styles. This mechanism is possible and useful for restyling your entire UI rather than using the standard ZUI look. However, there are two mechanisms that are better for providing local control for each component.

Every ZUI component has a .style(“classnames”) method that will accept one or more CSS class names. These are appended to the class specification after the default classes for that component. Using this mechanism puts all of your styling information out in the SCSS files, which is generally good practice.

In addition every ZUI component has a .css(” style information “) method. This accepts attributeName:attributeValue pairs separated by semicolons. In the JQuery implementation this information is placed in the HTML style attribute. This allows fine grained control of the style information but embeds style information in your code. This is generally only used when color or size information needs to be dynamically computed from data.

grid layout

The grid layout has twelve columns separated by gutters. At layout declaration uses the SCSS @include feature to insert layouts into your own SCSS files. By default we specify a component by its column width which gives it margins on the left and right of 1/2 of the gutter width. Here are some examples:

.MyStyle {
     @include layout(3);
}
.AButton {
     @include layout(0);
}

The .MyStyle class will occupy 3 columns (1/4 of the page width) with gutter space on each side. The .AButton class will ignore the grid and be as wide as necessary to accommodate its content.

The layout mixin has two additional parameters: gutters and offset. The gutters parameter can have the values “both”,”left”,”right”, or “none”. The default is “both”. This can remove the gutters when they are not wanted. For example, using “right” the left margin is eliminated so that component does not indent from its container. The offset parameter specifies how many empty columns should be to the left of the component. The default is 0. The following are some examples:

.FullNoIndent {
     @include layout(12,none);
}
.ColumnIndent {
     @include layout(11,left,1);
}

The .FullNoIndent style will take the entire page width (12 columns) with no gutters on the sides. The .ColumnIndent will skip the first column (offset = 1) and then occupy the remaining 11 columns and will have a margin only on the left (none on the right).

The layout mixin will stack components from the left to right going across the page. The layoutRight mixin works the same as layout except that it stacks components right to left.

The overlay() mixin will cause the component with that style to anchor on the upper left corner of its container over the top of whatever is already there.