Custom Components and The HtmlJsDataDisplay

Custom Components allow you to make a one-off custom component from inside an Apogee Workspace. Internally they use the HtmlJsDataDisplay data display.

A Custom Component is a control that allows the user to create his own custom component, complete with a display defined with HTML and javascript.

For a tutorial on this type of component, click here.

In Apogee, it is also possible to make your own component in javascript and import it into the program, and to share it with others. This component is coded in javascript and must be imported into the workspace as a module. For documentation on these, go to Creating Reusable Components.

The Custom Components

You can define a custom component by writing your own user interface code in HTML and javascript right in the workspace. There are two types of custom components:

  • Custom Component - This is a simple custom component that has a programmable display. This is convenient for things like output elements like charts or for action elements like a dialog.

  • Custom Data Component - This is similar to the custom component except it stores a data value. Interacting with the programmable display shows the save bar. Pressing save updates the stored value in the component. This is convenient for creating an object like a plain data table but with a custom display.

Both of these components use the HtmlJsDataDisplay. You don't need to know about data displays to write custom components. This is mentioned here only because this page also serves as a reference for people using this data display for writing reusable components.

UI Generator

For our these components, and for the HtmlJsDataDisplay, we use a UI Generator object. This is how we define the user interface for our display. It contains the functions below (all of which are optional). This can be used as a reference for the tutorials on the different custom components that follow.

An important note is that when we code this object in a custom component, in the view marked uiGenerator(), we can not access the other tables as we can from most of the other code views. That is because this code is not part of our model. This is just UI code.

We can however pass data into the UI. This will be done with the setData method on this object. The data passed to this function will be the output of the function we define in the view called input code, which is the equivalent to the formula in a plain data table.

/** This sample class gives the format of the UI Generator object
* that is used to construct the main display for a custom control.
* The user should return an object with these functions to implement
* the functionality of the display. All these methods are optional and
* any can safely be omitted. As such, a class does not need to be
* created, any object be passed in. If the class is used, an
* instance should be returned from the uiGenerator view.
* In the methods listed, outputElement is the HTML element that
* contains the form. The admin argument is an object that contains
* some utilities. See the documentation for a definition. */
var SampleUiGeneratorClass = class {
/** This can have whatever you want in it. They user will
* return an an instance rather than the class so this is
* called by the user himself, if he even shooses to use
* a class. OPTIONAL */
constructor() {
}
/** This is called when the instance is first compiled into
* the control. Note the output element will exist but it
* may not be showing. The method onLoad will be called when
* the outputElement is loaded into the page. OPTIONAL */
init(outputElement,admin) {
}
/** This method is called when the HTML element (outputElement)
* is loaded onto the page. OPTIONAL */
onLoad(outputElement,admin) {
}
/** This method is called when the HTML element is unloaded
* from the page OPTIONAL */
onUnload(outputElement,admin) {
}
/** This method is the way of passing data into the component.
* The code here can NOT access the other tables because this code
* is not part of our model. This object is just UI code.
* The data passed is the value returned
* from the user input function when the value updates. OPTIONAL */
setData(data,outputElement,admin) {
}
/** This method is used when save is pressed on the coponents save toolbar,
* if applicable. It retreives an data from the control, such as if this is
* an edit table. OPTIONAL */
getData(outputElement,admin) {
}
/** This method is called id the output element resizes.
* OPTIONAL */
onResize(outputElement,admin) {
}
/** This method is called before the window is closed. It should
* return apogeeapp.app.ViewMode.CLOSE_OK if it is OK to close
* this windows. If this function is omitted, it will be assumed
* it is OK to close. An alternate return value is
* apogeeapp.app.ViewMode.UNSAVED_DATA. OPTIONAL */
isCloseOk(outputElement,admin) {
return apogeeapp.app.ViewMode.CLOSE_OK;
}
/** This method is called when the control is being destroyed.
* It allows the user to do any needed cleanup. OPTIONAL. */
destroy(outputElement,admin) {
}
}

Output Element

In the functions above, one argument passed is the outputElement. This is an HTML DOM element in which we should place content for the display.

Admin Object

Another argument passed in the functions above is the admin object. This contains the following utility functions, to be used in the user defined functions above.

var admin = {
/** Returns an instance of the messenger. */
getMessenger();
/** Puts the component in edit mode, bringing up the save bar. */
startEditMode();
/** Ends edit mode for the component, removing the save bar. */
endEditMode();
}

Constructor

The constructor is not actually used by Apogee. In fact, this doesn't have to be a class at all. It can just be a javascript object with the necessary functions included in it. It is however convenient to write it as a class. The class itself is not passed into Apogee but rather an instance of it is passed. So in this case the constructor will just be called by the user himself.

Init

This function is called as soon as the UI generator object is passed into Apogee. It can be used to do any one time initialization needed. Note however that the outputElement may not be loaded on the page. If having the element loaded is a requirement, you can use the onLoad function.

onLoad

This method is called when the outputElement is loaded onto the web page. This should happen when the display is first created and it can happen subsequently if the outputElement is removed and reloaded onto the page, such as when you change views in the component.

OnUnload

This method is called when the outputElement is unloaded from the page.

setData

This method is called whenever there is new input data for the component.

In the Custom Component, the data passed is the return value of the function defined in the input code view. This function will be called each time any dependency in this function is called.

In the Custom Data Component, the data passed is the total value of the component, including both the return value of the input code view function and the stored data for this object. This is the same as the total value of the component, consisting of a JSON object with the following fields:

  • input - This is the return value of the function defined in the input code view.

  • data - This is the saved value associated with this component.

In other uses of the HtmlJsDataDisplay, the programmer passes callbacks in when instantiating the HtmlJsDataDisplay. See the HtmlJsDataDisplay constructor arguments listed below.

getData

This method is called when the end user presses Save in edit mode.

In the Custom Component, this is not applicable.

In the Custom Data Component, the function should return whatever data is desired for this table.

In other uses of the HtmlJsDataDisplay, the programmer passes callbacks in when instantiating the HtmlJsDataDisplay. See the callbacks listed for the HtmlJsDataDisplay constructor.

onResize

This method is called whenever the end user changes the size of the component.

isCloseOk

This method is called before the component is closed, to see if it is OK to close the component. The return values are:

  • apogeeapp.app.ViewMode.UNSAVED_DATA

  • apogeeapp.app.ViewMode.CLOSE_OK

destroy

This method is called when the display will be destroyed. This allows the user to cleanup and resources that should be destroyed.

HtmlJsDataDisplay Constructor

When using the HtmlJsDataDisplay in a reusable component, the constructor has the following signature. (Note this is not relevant for the Custom Component or the Custom Data Component.)

constructor(viewMode,callbacks,member,html,resource)

It has the following arguments:

  • viewMode - This is the instance of the view mode that shows this data display. It will be passed directly to the DataDisplay base class from which the HtmlJsDataDisplay inherits.

  • callbacks - These are the callbacks needed for the data display. It will be passed directly to the DataDisplay base class from which the HtmlJsDataDisplay inherits. The following callbacks should be included on this javascript object:

    • getData() - This gets the data that should be loaded into the display.

    • getEditOk() - This method returns true if edit mode is allowed for this component

    • saveData(formValue) - This is called with a the value from the display (form). It should save that value.

  • member - This is the member object associated with the component.

  • html - This is the HTML content with which to initialize display element content.

  • resource - This is the UI generator object for the display.

Additional Content

Besides the UI generator object, the custom components and the HtmlJsDataDisplay take some additional content.

HTML

The HTML is loaded into the output element on creation of the output element. Make sure any IDs in the HTML elements are unique on the page.

CSS

The CSS is used by the HTML content in the element, though it is simply added to the page in the header. As such, make sure the class names used in the CSS are unique on the page.

Input Code and Input Private

For the custom components, the programmer provides a function to give an input value to the component.

In the UI code, there is no access to the model and the other tables in it. We instead have an input function we can define. This can access the other tables. The result of this function is passed to the UI code in the setData function of the UI generator.

Destroy On Hide Option

When we create a custom component, or when we edit the properties of the component, there is an option Destroy on Hide. The default value is false.

If we set this to true, any time we hide the component display we will destroy it. It will be reconstructed when we show it again. This is done in most of the native components to save resources. However it may take a little extra care in defining the component. If the value is set to false, which is the default, than the display is only created once and then saved, so there is no need to worry about saving state between when the display is destroyed and recreated.

If you do want to tear down the display created when it is not shown, this flag can be set to true. It is typically not needed however.

HtmlJsDataDisplay Options

If we are using a HtmlJsDataDisplay in defining a reusable component, then the data display is part of a View Mode (which is what we are accessing in the view drop down). There are more options here for this same destroy concept.We have the following destroy options:

  • apogeeapp.app.ViewMode.DISPLAY_DESTROY_FLAG_NEVER - Do not destroy the view mode.

  • apogeeapp.app.ViewMode.DISPLAY_DESTROY_FLAG_INACTIVE - Destroy the display when the view mode is inactive.

  • apogeeapp.app.ViewMode.DISPLAY_DESTROY_FLAG_INACTIVE_AND_MINIMIZED - Destroy the display when the view mode is inactive or when the component window is minimized.