Skip to content

CodeCoupler UI Widget

Overview

A widget is a class derived from the CodeCoupler base class Widget. A widget will be initialized by a component within any element of the component. The content of the widget can be freely defined.

Start a Widget

Instantiate

You cannot initialize a Widget class directly. A widget can be started over the widget factory method widget(appStart) which is available in all components.

1
this.widget(widgetStart);

Error Handling

If any error occurs the initializing process will stop and an error will be thrown. The widget will be blocked with a message.

The error message can be modified in the system wide setting messages.WIDGET.ERROR_INIT.

In case that the error occurs during the system initialization, the parent system will catch the and handle this error. Please read the chapter "Error Handling" in the documentation of System for furher informations.

Start Definition Object

The widget start definition object widgetStart is not optional. It have to be an object or an Widget class.

Alternative signatures:

  • this.widget(WidgetClass): Setting widgetStart to Widget class is just a shortcut for { widget: WidgetClass }.

  • this.widget(WidgetClass, WidgetOptions): Setting widgetStart to Widget class with a second argument is just a shortcut for { widget: WidgetClass, options: WidgetOptions }.

The widgetStart object it can have the following properties:

container [ String | HTMLElement | jQuery Object ] (Default: Current Stage)

This is the container where the widget structure will be build. The container itself will be not touched. All additional elements will be created inside of this container.

This is optional in a stageable component (like an Application or System). In other components this is mandantory.

id [ String ] (Default: Random String)

An unique id that represents the widget instance. You cannot create two widgets with the same id on the same parent instance. If you do not specify an id a random id will be used. You should always use an id if you need to access the widget instance over the property widgets which is avaliable in all parent instances.

type [ String ] (Default: "widget")

Can be widget, vue, vue-sr or vue-mr. It specify the type of the widget class which can be a plain JavaScript Widget class or a Vue application.

component [ Class | Vue ] (Mandatory)

The widget class or widget vue application to instantiate.

options [ Object ] (Default: undefined)

Options which should be passed on to the widget.

settings [ Object ] (Default: undefined)

Override here the system wide settings for this widget and all child components.

► Read more about system wide settings

on [ Object ] (Default: undefined)

Early bind callbacks to events. In some cases the application fires events within the initialization phase. Instead of using instance.on("eventname",callback) after initialization (what would be too late) you can define here callbacks which will be early binded to be able to react to this events.

Define just an object with the eventnames as properties and callback functions as values:

1
2
3
on: {
  eventname: callback
}

Implement a Widget

Basics

You can write a new widget as plain JavaScript class or as a Vue application.

To write a new widget class you have to extend the CodeCoupler class Widget:

1
2
3
4
5
6
import { Widget } from "@codecoupler/cc-ui";
export default class extends Widget {
  async start() {
    //Init process here
  }
}

The only method you have to provide is async init().

To write a Vue based widget, just write an Vue application which receives the following props:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<template>
  <!-- Put your Template here -->
</template>
<script>
export default {
  props: {
    component: { type: Object, default: null },
  },
};
</script>
<style lang="postcss" scoped>
/* Put your Stylesheet here */
</style>

The property component is a pointer to a base instance of a Widget class. From there you can access all methods and properties of a component like you were writing a plain JavaScript class and would access over this.

Initialization and Starting Process

flowchart TB

  subgraph S1 [ ]
    S1A[Init Container<br>See 'DOM Layout'] --> S1C
    S1C[Block Widget]
  end

  S1C --> A
  A[Execute `init` Method] --> S2A
  style A color:#000,fill:#ffc929,stroke:#333,stroke-width:4px

  subgraph S2 [ ]
    S2A[Unblock Widget]
  end

  S2A --> I
  I[Widget is initialized]

DOM Layout

After launching a widget, the container element itself is not modified, except for the fact that a few styles are set. Instead, one or two more elements are created within the container.

The most important thing to note is that the container defines the dimensions of the visible area of the widget. These dimensions remain fixed and do not grow with the content of a widget. The container gets the same styles as a stage.

By default two elements will be created inside of the container. The first one will provide a custom scroller and the second is just a plain div. This inner div is the element which can be used to implement the widget interface.

The scroll functionality guarantees the widget to use the entire width and height of the inner div and the container to keep its dimensions.

Default Structure:

┌──────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│  This is the container defined in the widgetStart argument.              │
│  Accessible over this.env.start.stage.element or $element                │
│                                                                          │
│  This is always a stage element which have the following styles set      |
|  directly:                                                               |
│                                                                          │
│  position: absolute | relative | fixed (Default: relative)               │
│  isolation: isolate                                                      │
│  overflow: hidden                                                        │
│  flex-basis: 0 (Will only be set if flex-grow is "1")                    │
│                                                                          │
│  Furthermore the element have the class "codecoupler__stage".            │
│                                                                          │
│  ┌─Widget─────────────────────────────────────────────────────────────┐  │
│  │                                                                    │  │
│  │ div                                                                │  │
│  │ Container in where the PerfectScrollbar will be initialized.       │  │
│  │ Accessible over this.env.scroll or this.env.$scroll                │  │
│  │                                                                    │  │
│  │ height: 100%;                                                      │  │
│  │ width: 100%;                                                       │  │
│  │ position: relative;                                                │  │
│  │                                                                    │  │
│  | Furthermore the element have the class "codecoupler__widget" which |  |
|  | stretch all the widget elements below to "min-height:100%;         |  |
|  | min-width:100%".                                                   │  |
│  │                                                                    │  │
│  │  ┌──────────────────────────────────────────────────────────────┐  │  │
│  │  │ div                                                          │  │  │
│  │  │ Here is the place where the widget will be initialized.      │  │  │
│  │  │ Accessible over this.env.element or this.env.$element        │  │  │
│  │  │                                                              │  │  │
│  │  │ min-height: 100%; (Set by codecoupler__widget)               │  │  │
│  │  │ min-width: 100%; (Set by codecoupler__widget)                │  │  │
│  │  └──────────────────────────────────────────────────────────────┘  │  │
│  │                                                                    │  │
│  │  ┌──────────────────────────────────────────────────────────────┐  │  │
│  │  │ Technically needed divs for the scroller                     │  │  │
│  │  └──────────────────────────────────────────────────────────────┘  │  │
│  │                                                                    │  │
│  └────────────────────────────────────────────────────────────────────┘  │
│                                                                          │
│  ┌─Canvas─────────────────────────────────────────────────────────────┐  |
│  │                                                                    │  |
│  │ div                                                                │  |
│  │ Here is the place where the widget will be initialized.            │  |
│  │ Accessible over this.env.element or this.env.$element              │  |
│  │                                                                    │  |
│  │ height: 100%; (Set by codecoupler__stage)                          │  |
│  │ width: 100%; (Set by codecoupler__stage)                           │  |
│  │                                                                    │  |
│  └────────────────────────────────────────────────────────────────────┘  |
│                                                                          │
│  ┌────────────────────────────────────────────────────────────────────┐  │
│  │ Other divs of applications and needed for blocking                 │  │
│  └────────────────────────────────────────────────────────────────────┘  │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

The property this.env contains the properties container, scroll and element to access the single elements. The properties $container, $scroll and $element are the jQuery counterparts. Normally you should not manipulate the container and the scroll element. The scroll element can be used to scroll your widget. You can use here plain JavaScript DOM methods scrollTop and scrollLeft.

If the scroll container is actually not needed, the widget can announce this via overriding the prepare method (read below). In this case the inner div will get the style overflow: "hidden"; height: "100%"; width: "100%". This guarantees the container to keep its dimensions.

Structure without Scroller:

┌────────────────────────────────────────────────────────────────────────┐
│  This is the container defined in the definition argument.             │
│  Accessible over env.start.stage.element or $element                   │
│                                                                        │
│  position: absolute | relative | fixed (Default: relative)             │
│  isolation: isolate                                                    │
│  overflow: hidden                                                      │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ div                                                              │  │
│  │ Here is the place where the widget will be initialized.          │  │
│  │ Accessible over env.element or env.$element                      │  │
│  │                                                                  │  │
│  │ min-height: 100%;                                                │  │
│  │ min-width: 100%;                                                 │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ Other divs of applications and needed for blocking               │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────────────┘

In addition to the JavaScript-based widget, you can also create a Vue-based widget. In this case you have to provide a Vue application which will be instantiated.

You have to specify if the Vue application use a single root template or not. Depending on this the inner div described above is the root element (this.$el) of your vue application or just a parent container:

Single root Vue application:

┌────────────────────────────────────────────────────────────────────────┐
│  The parent containers and all styles will be                          │
│  constructed like with JavaScript-based widgets                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ div                                                              │  │
│  │                                                                  │  │
│  │ Root element of your Vue template                                │  │
│  │ Accessible over this.component.env.element or ...$element        │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ Other divs of applications and needed for blocking               │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────────────┘

Vue with single root template

The variable this.component.env.element where you get normally the root element will not be available while mounting (and thus in the methods beforeCreate, created, beforeMount and mounted).

Multiple root Vue application:

┌────────────────────────────────────────────────────────────────────────┐
│  The parent containers and all styles will be                          │
│  constructed like with JavaScript-based widgets                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ div                                                              │  │
│  │ Accessible over this.component.env.element or ...$element        │  │
│  │                                                                  │  │
│  │  Multiple root elements of your Vue template:                    │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │                                                            │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │                                                            │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │                                                            │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  │                                                                  │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ Other divs of applications and needed for blocking               │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────────────┘

What's next

Do whatever you want and build amazing interfaces within this.element!

Replacing Root Element

Some components aloow you to replace this.element with your own. In this case you should never use this.env.$element.replaceWith(). Use instead this.replaceElement()`!

Implementation Details

Controlling the DOM Structure and Initialization Phase

A Widget build the previous DOM structure and block the widget while initializing. Sometimes this is not needed. To control these features you have implement the following method:

1
2
3
4
5
6
7
import { Widget } from "@codecoupler/cc-ui";
export default class extends Widget {
  async prepare(env) {
    env.feature.block = false; //Disable blocking while initializing
    return super.prepare(env);
  }
}

Reference

In addition to the common instance members described in the base classe Component a widget has the following:

Instance Properties

vue and vueApp

This properties will be accessible if the widget is Vue based. vue points to the Vue instance and vueApp to the Vue application object.

Instance Methods

replaceElement(newElement)

This will replace the root element of the widget with the given newElement. The argument can be a HTMLElement, a jQuery object or a selector string.

The variables this.env.$element and this.env.element will be redefined.