Skip to content

CodeCoupler UI Component

The Component class is the base class of all CodeCoupler components. All the following functionalities and rules affect all components.

Each component will have the following members:

Component Construction

prepare() and finalize()

These method names are used internally and should not be used. Otherwise the component will not work properly.

init():

All components use this method name for custom initialization code. It will always used when a component is created.

boot() and start():

These method names is only used for the initialization process of some components.

initialized

The property this.initialized return a promise which will be resolved if the component ist initialized or rejected if an error occurs while initializing.

This can be used for example in the init methods to define actions that will be executed after the initialization phase ends. You need to know that after the init method, additional steps may be carried out. In some cases it makes sense to wait for this first:

class MyComponent extends Component {
    async init() {
        this.initialized
        .then(() => {
            //This will be executed after this init method and after
            //all following steps until the component has been
            //completely initialized
        })
        .catch((e) => {
            //This will be executed if this init method ore some of
            //the following steps throws an error.
        });
    }
}

Common Getters

env

Prepared environment and options variables.

► Read more about environment of component

id

With this.id you can read the id of the current component.

settings

Access the system wide settings. Details explained in "System Wide Settings".

options, constructor.defaults

Over the property this.options the component get access to the options the user have defined in the start definition object for your component.

The options will be merged with the static property defaults of the component class and all static properties defaults of all prototypes of the component.

With this mechanism you can define the default values for all options and the user can set default values for each new instance.

1
2
3
4
5
6
class MyComponent extends SomeComponent {
    static defaults = {
        //Define here the default values of your expected options.
        //This place is also very suitable for documenting your options!
    };
}

If you set a new value to this.options it will be merged to the already existing ones!

Destroying

asyc destroy(), destroying, Event destroyed

Method to destroy the component. The base method will call all destroy methods of all child components. The function can be called multiple times, but is only executed once. To get the status if destroying is running, test if the property this.destroying returns true.

At the end of the destroying process the component will trigger the event destroyed. As argument the event will have the component instance.

The method blocks the component with the message from the system wide setting messages.COMMON.DESTROYING while executing. On finish the component will be blocked with the message messages.COMMON.DESTROYED with a special overlay style destroyed which is black and non-transparent. The method do not wait while the last blocking is ready.

The method can be overriden to add own functionality. In this case the super method should be called. You should ensure that no error will be thrown:

class MyComponent extends Component {
    async destroy() {
        try {
            //Implement your logic here
        } finally {
            await super.destroy();
            //At this point the "destroyed" event was fired
        }
    }
}

Widget Handling

widget(widgetStart)

Widget factory method to initialize an new widget instance.

► Read more about widgets

Application Handling

app(appStart)

Application factory method to initialize an new application instance.

► Read more about applications

apps

With this.apps you can access an object of all applications of this component. The id of the application is used as property name.

You get only running apps. If the id is not in this object the application was closed. This can be done by a user or programmatically. This is important to check because the application instance can continue to exist, even the application was closed.

frontedApp

Returns the instance of the fronted application. Can be null if component do not have an application started.

Event Management

on(event,handler), one(event,handler), off([event],[handler]),
trigger(event,argments)

Each component provide methods to bind and trigger functions based on event names.

  • on: Add a handler to an event.
  • one: Add a handler to an event that will be executed only one time.
  • off: Remove a handler to an event. If handler is omitted all, handler to this event will be removed. If event and handler are omitted, all handler for all events will be removed.
  • trigger: Trigger an event.

Hints

hint(text), hint(type, text), hint(type, text, element), $hintContainer

Show an hint with the given style in type. Depending on which component this method is used, the hint is displayed in the following places:

Component Hint Target
System In the current stage
Stage In the stage element
Application In the content area of the application
Widget In the widget element

The argument type specifies the hint style. Predefined are info, success and error. If ommited success will be taken. You can add or modify these styles in the system wide setting hints. Each property in this object represents one style. A style is a configuration object which will start a jPanel hint extension.

The signature including element is used internally. If you need to specify a custom element in which the hint should be displayed, you can use `element which can be defined as HTMLElement, jQuery object or selector string.

The getter $hintContainer is used internally. Do not override.

Visibility

visibility, $visibility, static visibilityValues,
static $visibilityValues(value), Event visibility

With this.visibility you get one of the value from the static object Component.visibilityValues:

  • Component.visibilityValues.HIDDEN
  • Component.visibilityValues.VISIBLE
  • Component.visibilityValues.PARTLY

A component can set its visibility status with this.visibility to one of these values. But as soon as you ask for the value, it does not mean that exactly the same will be returned.

If you ask for the value, further conditions are checked to determine whether a component is visible or not. A component is not visible for example if it is inside of an application panel that is minimized. Or it is also not visible if it is in an application panel behind another maximized panel. This is not only checked for the panel above, but also for the entire route down to the system.

The value PARTLY will returned if a component is in a panel behind other panel which are normalized. This means that the component can be partially visible or completely covered by other panels. This cannot be determined more precisely.

It is very important if you implement widgets which hold itself other widgets to maintain the visibility status of these widgets. For example if you implement a tabed display of multiple widgets, you should set the visibility of each one, depending on which tab is displayed.

Whenever the visibility of a component is supposedly changed, the event visibility is triggered. The event visibility will be triggered often without checking if the visibility have really changed. This is becuase the calculation of the visibility is much more intensive and is not used everytime. You can bind a callback to this event, check with this.visibility and get the new or old status.

The static function $visibilityValues(value) is just a helper which converts a visibility value to its name.

The getter this.$visibility returns the value that have been set for the component, not the calculated.

Resizing

Event resize

Whenever the resize event is triggered in a component, the component will trigger this event on all child components. An application will trigger this event if the panel is resized, normalized or maximaized. If you implement a component wich change its size you should trigger this event one time in your component. If you change sizes of child components you should trigger this event in the affected components one time.

Blocking

► Read more about blocking components

Blocking Methods

async block({type, text, handle, settings, sticky})

Block the the component with a message. Keep in mind that this is an async method. This is because the blocking layer will fade in and the method will resolve if the fading is finished.

Different signatures are available for this method:

  • block()
  • block(text):
  • block(text, {...})
  • block(handle, {...})
  • block(handle, text)
  • block(type, text)
  • block(type, text, {...})
type: String

If you set type to info or error a different stylesheet will be used. If you do not specify type, info will be used as default.

Styles will be configured in the system wide settings in the property block.

Will not used if handle is given.

text: String | HTMLElement | jQuery Object

message can be a simple string with HTML elements, an HTMLElement or a jQuery object which will be displayed.

If you set message to an empty string, null, or undefined the method produce an empty container which have the fill width and height of the component. This can be used to display just an empty blocking layer or to add or modify the content as you like over the return value container.

handle: String

Use handle to call the block method again and update an existing blocking layer. Must be created with new BlockHandle() or read from the return value of previous block calls.

settings: Object

Here you can override the system wide settings of block for only this blocking call.

Will not used if handle is given.

sticky: Boolean

Create a "sticky" blocking layer if true. Please read the section about the "stacking concept" above for more details.

async unblock(handle)

Unblock the component. Keep in mind that this is an async method. This is because the blocking layer will fade out and the method will resolve if the fading is finished.

handle: String

The handle returned from the block method have to be used here to unblock a specific blocking layer.

async block({..., element}) and async unblock(handle, element)

The methods block and unblock have one more signature with which you can specify an element to block instead the current component.

Normally you cannot set what HTMLElement should be blocked. This will be set by the component. The only case you could use this property is calling the methods from the super instance with type of Component. The element can be defined as HTMLElement, jQuery object or selector string.

Blocking Getter

blocked

Returns false if component is not blocked or one of the strings info or error depending on the block type.

$blockContainer and $blockArguments

Used internally. Do not change.

Preloading Assets

With the method preload you can preload assets. The default progress behaviour is to block the component and show the progress in percent.

Important: Do not use the preload functions in the init method of an application. Because at this time the application do not have a panel and the default progress handler will try to block the application. This leads to an exception.

Preloading Methods

async preload(config,[args],[settings])

To start a loading process you have to call the method async preload() with a configuration object.

config: Object

Each property of this configuration object have to be a name of a preloading handler. It's value will be used as argument for the specific handler. The returned promise of the method will resolve if all assets are loaded or reject if an error occurs.

The default preloading handlers are dataSources, fonts and fontsRequired. Custom handlers can be added.

args: Any

Specify here whatever you want, that will be passed on to the progress callbacks (see below).

settings: Object

Specify an object with some options. Default values will be taken from settings.preloader of the system instance. The object can have the following properties:

delay: Object

Specify a delay for debuging purposes. The object have the following properties:

enabled: Boolean.

If true delaying will be enabled.

minMSec: Integer.

The minimal time in milliseconds that will use each asset for loading.

maxMSec: Integer.

The maximal time in milliseconds that will be added to the minimal time above. It will be used a random time between minimal and maximal delay on each asset loading.

progress: Class

Progress handler which will be instatiated for each preload call. The methods get notified for each progress update and can use this information to present them to the user. Read below for more informations.

registerPreloader(name,func)

Method to register a new preloading handler.

name: String

The the property name of the configuration object that will be used for this handler.

func(assets, progress, delay)

Callback function of the custom handler.

assets: Any

The configuration value for this handler.

progress: Object

The object have three properties that have to be updated by the handler during the loading process.

total: Number

The handler have to count the assets given in the argument assets as soon as possible and update the number here.

success: Number

Whenever an asset is loaded you have to increase the number here.

failed: Number

Whenever an asset cannot be loaded you have to increase the number here.

delay: Object

Whenever possible implement a delay according to the delay argument (details are explained above).

Progress Handler Class

The start have to provide the following methods:

async start(component,args)

Callback that will be called when the preload processes starts.

component: Component

The current component instance that initiated the preload process.

args: Any

The value that have been used starting the preload process.

async update(component,progress,args)

Callback that Will be called everytime the progress summary of the loading process have changed. Everytime a handler will change its progress object, the component will calculate a summary of all loading assets.

component: Component

The current component instance that initiated the preload process.

progress: Object

An object with the following properties:

total: Number

Total Assets that have to be loaded.

success: Number

Number of succesful loaded assets.

failed: Number

Number of assets that failed loading.

percent: Number

Percent of assets that sucesful loaded or failed.

args: Any

The value that have been used starting the preload process.

async end(component)

Callback that will be called when all preload processes finished and all calls to the update methods have been completed.

component: Component

The current component instance that initiated the preload process.

Default Preloading Handlers

dataSources: Object[]

The value of dataSources must be an array of objects. The objects have to be the arguments used in the component.dataSource() method.

fonts and fontsRequired: string[] | object

The value of fonts and fontsRequired can be an array of font-face names to load. The difference between fonts and fontsRequired is that the promise will rejected if loading fails in fontsRequired and will always resolve in fonts.

In addition to the name, the variation can also be specified. Herefore the Font Variation Description will be used like explained here: [https://github.com/typekit/fvd]

Example: "Font Awesome 5 Free:n4,n9" will load the font Font Awesome 5 Free in normal style and with the weights 400 and 900.

Because the external library Web Font Loader is used under the hood, you can alternatively set the configuration object directly.

Data Handling

Datasource Factory

dataSource(), dataSources

Create and retrieve datasources created by this component. With this.dataSources you can access an object of all datasources of this component. The id of the datasource is used as property name.

► Read more about datasources

Handling Data Changes

Each component have some prepared methods to handle pending data changes in datasources. Whenever a data item of a datasource have been modified, created or deleted the datasource is marked as dirty and this means that pending data changes exists.

Each component handle self-created datasources and all datasources of all child components.

In many cases a component receive a datasource instance as option. In this case all the data handling will not work as expected. For this case you can and should link the datasources with dataSourceLink(). All data handling methods will take linked datasources also into account.

dataSourceLink(dataSource): Void

Link an existing datasource which was not created by the component itself and was passed via an option from a parent component.

All linked datasources will be handled from all other data handling methods as they would be self-created.

hasChanges(commited): Boolean | Null

Checks if any own datasource or any datasource of any child component have pending changes.

Returns true if pending changes exists, false if no pending changes exists and null if the method cannot decide if pending changes exists.

The last case, returning null occurs if a component cannot commit its current input values. To check this before checking the datasources this.commitChanges() will be called. If this method returns false the method hasChanges returns null.

If you want to add more checks in your component, override this method. You should call firstly the super.hasChanges() method and interrupt if changes already detected:

1
2
3
4
5
6
7
8
9
hasChanges(commited) {
    //Do not touch the argument "commited". It will be used internally to avoid multiple
    //calls of `commitChanges`. Interrupt if changes already detected:
    let changes = super.hasChanges(commited);
    if (changes === true || changes === null) return changes;
    //Implement your logic here . Do not check your datasources, this will be checked
    //already by the super method. Return true immediatly if needed. Otherwise return false:
    return false;
}
commitChanges(blured): Boolean

Perform any needed actions to take over currently entered values in input fields or other editor elements and save them in data items of datasources.

The method can return true if the commit has succeeded and false otherwise. This can occur for example if an input field have an invalid value.

The base method just calls document.activeElement.blur() if the browser supports this. This is done so that the focus is removed from the current input field and the entered data may also change a datasource.

After this the method calls the commitChanges method of any child component. All method of all components will be called regardless of their return values. If any of these methods returns false the method will return also false.

If you want to add more actions in your component, override this method. You should override this method especially if your component use input elements. In case the commitChanges is called you should perform a validation of all input fields. If a validation fails you should return false. Do not show the error messages at this time. Wait for calling toggleErrors(true).

If the validation succeed you should persist your values into the datasource.

In your override you should call firstly the super.commitChanges method and merge its return value with yours:

1
2
3
4
5
6
7
commitChanges(blured) {
    //Do not touch the argument "blured". It will be used internally to avoid multiple
    //calls of `document.activeElement.blur`.
    let returnValue = super.commitChanges(blured);
    returnValue = returnValue && implement_your_logic_here();
    return returnValue;
}
async refreshData(): void

Calls the read method of any own datasource or any datasource of any child component. The method will fetch all data from the remote server.

You have to pay attention, because this method will discard all local modifications.

The method try to call all read methods in each component in parallel.

If you want to add more actions in your component, override this method. You should call firstly await super.refreshData():

1
2
3
4
5
refreshData() {
    await super.refreshData(block);
    //Implement your logic here.
    //Do not forget to use "await" on async methods!
}
cancelChanges(): void

Calls the cancelChanges method of any own datasource or any datasource of any child component. The method will reset all data items to the values fetched from the remote server.

You have to pay attention, because this method will discard all local modifications.

If you want to add more actions in your component, override this method. You should call firstly the super.cancelChanges method:

1
2
3
4
cancelChanges() {
    super.cancelChanges();
    //Implement your logic here
}
async saveData(commited): void

Calls the sync method of any own datasource or any datasource of any child component. The method will send all modified data to the remote server.

Before all checks this.commitChanges() will be called. If this method returns false an exception will be thrown. For now a simple Error object will be thrown with the message Commit not possible. In future versions this may change.

You can avoid commiting changes by setting the argument commited to true. You should check in this case the return value of this.commitChanges() or better this.hasChanges() before calling saveData(true).

The method try to call all sync methods in each component in parallel.

If you want to add more actions in your component, override this method. You should call firstly await super.refreshData():

saveData(commited) {
    //Modify DataSources here before saving if needed.

    //Call Super
    //Do not touch the argument "commited". It will be used internally to avoid multiple
    //calls of `commitChanges`.
    await super.saveData(commited);

    //Implement further logic here if needed
}
toggleErrors(mode): void

Set the error mode in component and in any child component. mode should be a boolean value.

The idea behind this is to controll of how the input validation errors should be shown. You do not have to follow these pattern:

  1. The component start with the status of not showing any errors.
  2. The user start to enter values in an input field. Either the field prevent an input of an invalid value or show a subtle note like a red border.
  3. The user try to save although validation errors exists.
  4. The parent logic detects validation errors with hasChanges or commitChanges and calls toggleErrors(true).
  5. Now the component should show detailed validation error messages.
  6. The user correct the values and save again.
  7. The parent logic do not detect any validation errors, the sync of all datasources succeed and calls toggleErrors(false).
  8. From now on no detailed error messages will be shown and anything starts from beginning.

The method saveDataUI implements this pattern.

You should override this method especially if your component use input elements. You should call firstly super.toggleErrors() and remember the current status:

1
2
3
4
5
6
#showErrors = false;
toggleErrors(mode) {
    super.toggleErrors(mode);
    this.#showErrors = mode;
    //Implement your logic here to show or hide error messages.
}

Data Handling UI Methods

In addition to the previous data handling methods exists some data handling methods with UI elements and user interactions. They cover the most use cases in which the data handling methods will be used in combination with UI elements:

refreshDataUI()

Will check pending data changes with this.hasChanges() and call this.refreshData() if no changes detected. Returns true on success, otherwise false.

If they are pending changes the user will be asked if he want to discard all changes before calling this.refreshData(). The confirm messages will be taken from messages.DATA.CONFIRM_REFRESH

After refreshing an error or success hint will be shown. The messages of the hints will be taken from the system wide settings messages.DATA.SUCCESS and messages.DATA.ERROR. An error will be loged in the console.

cancelChangesUI()

Will check pending data changes with this.hasChanges() and call this.cancelChanges() if no changes detected. Returns true on success, otherwise false.

If they are pending changes the user will be asked if he want to discard all changes before calling this.cancelData(). The confirm messages will be taken from messages.DATA.CONFIRM_CANCEL

saveDataUI(consoleError)

Will check pending data changes with this.hasChanges() and call this.saveData() if changes detected. Returns true on success, otherwise false.

If they are no changes just a hint will be shown. The messages of the hints will be taken from the system wide settings messages.DATA.NO_CHANGES. In this case the method returns true.

If validation errors exists and hasChanges returns null a hint will be shown and saveData will not be called. Instead this.toggleError(true) will be called.

After synchronizing the datasources an error or success hint will be shown. The messages of the hints will be taken from the system wide settings messages.DATA.SUCCESS and messages.DATA.ERROR. An error will be loged in the console if you set the argument consoleError to true.

If no error occurs app.toggleErrors(false) will be called.

Manifest

A component can provide a static field manifest which contains properties of how the component should be presented and other descriptive informations. This is used for example for displaying an application component in titles, launching buttons or other UI elements. Because these informations are needed before the application is created it is a static property.

Because a developer maybe do not specify all properties in its component you cannot just read the static property manifest to get all informations. You have to use the static method Component.$manifest(component) which will merge the values of all static properties of all parent classes.

1
2
3
4
5
static manifest = {
  name: "The Name of the App",
  iconHtml: "<i>Something</i>",
  level: "base"
}
name [ String ] (Default: Taken from base class)

The name of the application.

iconHtml [ String ] (Default: Taken from base class)

The html element should inherit the font-size. If you use FontAwesome, you should always add the fa-fw class.

level [ String ]

In which stage level this componenet will be loaded if not otherwise specified.