Options
All
  • Public
  • Public/Protected
  • All
Menu

Version 6.0.0

General

Terminology

Name Description
Complex Hive type Any non-primitive type or in simple words: Data which contains key-value pairs

Examples:
  • Complex: Record<{Key=Number, Name=Text}>
  • Primitive: List<Text> (primitive value simply wrapped in an array)
Raw data Unconverted data which is sent between client & server
  • Example: [[1,"Entry 1"],[2,"Entry 2"]]
  • Can also be viewed in the Hive-Editor when clicking on Toggle raw data
Parsed data Raw data which got parsed/converted to a JS object
  • Example: [{ Key: 1, Name: "Entry 1" }, { Key: 2, Name: "Entry 2" }]

What changed

Working with complex Hive types was quite cumbersome in previous versions and had a mixed behavior:

  • Records returned parsed data (CmpUtils.getRecord...())
  • When setting record data (CmpUtils.setCmpInput()) it had to be parsed to raw data
  • Value components and nested data were always raw data
    • even when directly referencing a record

To sum it up, it was the job of the developer to know when and how to convert data.

From now on, conversion of raw data isn't necessary anymore!

The functions of CmpUtils always return parsed data when the component is of a complex Hive type and it can also be used to set any cmp-input with it, as long as the type matches.

Visual explanation
Example

How to upgrade

Register additional interfaces

Upgrade from version 4 or lower If you're upgrading from version 4 or lower, please see Version 5.0.1 first!

Besides the record components it's now required to register the TS interface of all components which are used in CustomJS and consist of a complex Hive type.
Recommended process to do so: Hive Interface Registration - Mapping

To find unregistered components easier an automatic check happens, which compares the components defined in CustomJSCmps with the registered ones. So after updating the cjs-utils version simply run the cfgr in preview-mode and check for new errors.
Go through them from top to bottom as some errors might appear only due to leading ones. After you registered the missing interfaces, all such errors should be gone and you can proceed with the next chapter.

Refactoring - Remove obsolete data conversions

Every project has its own kind of conversion, so the following chapter should only be seen as a collection of guidelines and hints.

Refactor everything one by one Because you probably can't test the configurator in between, you should focus on one objective at a time.
For example, if there's an Element and ElementList only rework the Element until all build-errors are resolved.
Only then proceed with the ElementList.

Get an overview

  • Find major files/classes which do conversions
    • E.g. search for functions containing toCmpValue, fromCmpValue, toCmpInput, fromCmpInput, syncFrom, syncTo, ...
  • Find files/objects which define the indices to convert raw data to parsed data
    • Example: const ElementRecIndices = { Key: 0, Name: 1 };
    • Might also be named something like ...Column or ...Cols

JS Classes - Simple

If the conversion logic is contained in its own class, which only consists of those conversion functions (toCmpInput, fromCmpInput, ...) and without additional business logic (e.g. getPrice(), calcBoundingBox()) the whole class can probably be deleted. Afterwards replace any occurences where the class gets instantiated, a conversion function is called or its type is used.

JS Classes

Remove all the conversion functions (check the body beforehand for any special logic) and go through your errors.

Options to proceed:

  • Keep class
    If you want to keep your class, no further steps should be required.

  • Change to module with exported functions
    Get rid of the class and change every function to an exported function which takes its "instance" as first parameter. Be careful to replace any usage of this.

    // Before
    export class ElementItem {
      getPrice(withVAT) {
        return withVAT ? this.price * 1.2 : this.price;
      }
    }
    
    // After
    export function getPrice(elementItem, withVAT) {
      return withVAT ? elementItem.price * 1.2 : elementItem.price;
    }
    

Improve poorly typed code

  • Search for occurences of any or @ts-ignore which are used in combination with a CmpUtils function.
  • Search for usages of CmpUtils with no typing at all
    • Wrong: const data = CmpUtils.getCmpValue('MyData');
    • Correct: const data = /** @type {MyComplexData} */ (CmpUtils.getCmpValue('MyData'));

If the interfaces are correctly registered, there shouldn't be any cases where a typing isn't possible. Otherwise reach out to your development team.

Accessing object properties with brackets

Even when the object is typed correctly there's no build-error when accessing properties with brackets.
E.g. myObj[123] or myObj[asdf] instead of myObj.asdf

Errors related to this might be hard to spot and therefore requires to test the final configurator very well. You might search for following occurences:

// Retrieving data by number index
// Error:
const newValue = myRecord[0];
// Fixed:
const newValue = myRecord.Key;

// Object which defines indices
// Error:
const MyRecordCols = { Key: 0, Name: 1 };
const newValue = myRecord[MyRecordCols.Key];
// Fixed:
const newValue = myRecord.Key;

Replace usages of removed function CmpUtils.getRecordCmpColValue

This function required to additionaly hard-code the ColName although it's already defined in the interface. Instead retrieve the whole record and access its property.

Before

export class ColNames {
  static get InitMsg() {
    return 'InitMsg';
  }
  static get LoadModelMsg() {
    return 'LoadModelMsg';
  }
}
const loadModelMsg = CmpUtils.getRecordCmpColValue(CmpNames.StatusMsgs, ColNames.LoadModelMsg);
displayMessage(loadModelMsg);

After

interface StatusMsgs {
  InitMsg: string;
  LoadModelMsg: string;
}
const statusMsgs = /** @type {StatusMsgs} */ CmpUtils.getRecordCmpValue(CmpNames.StatusMsgs);
displayMessage(statusMsgs.LoadModelMsg);

Replace usages of removed function CmpUtils.getCmp

There is no direct replacement for this function. For every usage there should exist a better suited CmpUtils function by now.
Otherwise reach out to your development team.

Check usages of CmpUtils.onAnyCmpValueChanged

This was added in version 6.1.0.

In previous versions the returned callback param changedCmps sometimes contained "more than requested" as new values of all cmps which have changed where included instead of only the ones which were actually requested with the cmpNames param of CmpUtils.onAnyCmpValueChanged.

The returned changedCmps now only contains values which are explicitly defined in the cmpNames param (and actually changed).

CmpUtils.onAnyCmpValueChanged(
  changedCmps => {
    // ==============
    // Before v6.1.0:
    // ==============
    //
    // If cmps `Chairs` & `Cabinets` changed, `changedCmps` also included the new value of
    // `Chairs` even though it was not requested with the `cmpNames` param.
    //
    // ==============
    // After v6.1.0:
    // ==============
    //
    // The new value of `Chairs` (or any other cmp which was not requested with the `cmpNames`
    // param) is not included in the given `changedCmps` anymore.
    //
    // E.g.:
    // - Cmps `Cabinets` & `Chairs` change -> `changedCmps` only contains the new value of
    //   `Cabinets`
    // - Cmps `Cabinets`, `Chairs` & `Desks` change -> `changedCmps` contains the new values
    //   of `Cabinets` & `Desks`
  },
  ['Cabinets', 'Desks'] // <- Requested `cmpNames`
);

Generated using TypeDoc