Nuclos web addon: extend web client, Angular, TypeScript, Node.js, npm, WEBCLIENT_SRC_DIR_FOR_DEVMODE, DEV mode, calculated fields.

globe with meridians Language: Deutsch · English

hammer and wrench Web Addon

Extend the web client with Angular/TypeScript – dev setup, live reload and an example of calculated fields.

On this page

What are web addons?

Menu: Configuration → Web Addon.

With web addons you flexibly extend the web client using Angular/TypeScript – e.g. with client-side calculations in subforms.

Prerequisites

Create a web addon in the administration.

Set up local development

  1. Create the addon and assign it to a nuclet. It is then located under [instance]/data/webaddons-webclient-src/addons.
  2. Create a copy of this directory (e.g. webaddons-webclient-src_) and point the parameter WEBCLIENT_SRC_DIR_FOR_DEVMODE to it; restart the server.
  3. In src/assets/config.json set nuclosURL to the local server.
  4. Install dependencies and start the dev server:
npm install
npm run install-addon-deps
npm run start --webclient-host="127.0.0.1" --webclient-port=4200

Thanks to the npm watcher, changes are visible directly in the browser. Transfer finished changes into the nuclet in the web addon administration and export them.

Transfer the finished addon into the nuclet.

Example: calculated fields

A global addon calculates values client-side in subforms. test_SubBo_refMainBo is the subform's BO name; the business logic is in calculate_Attribute() and is called whenever the observed attributes change.

import { Injectable } from '@angular/core';
import {GlobalContext, ISubEntityObject, Logger} from "@nuclos/nuclos-addon-api";
import {
	IEntityObject,
	IEntityObjectDependents,
	IEntityObjectEventListener
} from "@nuclos/nuclos-addon-api/api/entity-object";

@Injectable({
	providedIn: 'root'
})

class CalculationRule {

	constructor(public attributeNames : string[], public targetAttributeName : string, public funcCalculate : (eo : IEntityObject, attributeName : string) => number) {
	}
}

@Injectable()
export class BerechneteFelderService {
	private attributeChangeListener : IEntityObjectEventListener;
	private selectedEo : IEntityObject;
	private calculationRules : CalculationRule[];

	callForDependents(eo : IEntityObject, func: {(entity:ISubEntityObject):void}) :any {
		eo.getDependents("test_SubBo_refMainBo").asObservable().subscribe(subEntities => {
			if( subEntities === undefined)
				return;
			for(let i in subEntities) {
				let entity : ISubEntityObject = subEntities[i];
				func(entity);
				this.logger.log('BerechneteFelder: onEoSelection() ' + func.prototype.name);
			}
		})
	}

	constructor(private addonCtx: GlobalContext, private logger: Logger) {
		this.logger.log('BerechneteFelder: constructor');
		this.calculationRules = [];
		this.calculationRules.push(new CalculationRule(["subbo_attribute1", "subbo_attribute2"], "subbo_attribute3", calculate_Attribute));
		this.attributeChangeListener = {
			afterAttributeChange: (entityObject: IEntityObject, attributeName: string, oldValue: any, newValue: any) => {
					this.logger.log("BerechneteFelder: entityObject: " + entityObject.getEntityClassId() + ", attributeName: " + attributeName +
						", oldValue: " + oldValue + ", newValue: " + newValue);
					this.calculationRules.forEach(calculationRule => {
						if(calculationRule.attributeNames.includes(attributeName)) {
							let result = calculationRule.funcCalculate(entityObject, attributeName);
							entityObject.setAttribute(calculationRule.targetAttributeName, result);
						}
					})
			}
		}

		addonCtx.getEntityObjectApi().onEoSelection().subscribe(eo => {
			this.logger.log('BerechneteFelder: onEoSelection()');
			if (this.selectedEo !== undefined) {
				this.selectedEo.getDependents("test_TestBo_ref1").asObservable().subscribe((entities => {
					entities?.forEach((subEo) => subEo.removeListener(this.attributeChangeListener));
				}))
			}

			if (eo !== undefined) {
				eo.getDependents("test_TestBo_ref1").asObservable().subscribe((entities => {
					entities?.forEach((subEo) => {
						subEo.addListener(this.attributeChangeListener)
					});
				}))
				this.selectedEo = eo;
			}
		})
		addonCtx.getEntityObjectApi().onEoModification().subscribe(eo => {
		});
	}
}

function calculate_Attribute(eo : IEntityObject, attributeName : string) : number {

	let dAttribute1 = eo.getAttribute("subbo_attribute1");
	let dAttribute2 = eo.getAttribute("subbo_attribute2");
	
	return dAttribute1 * dAttribute12;
}

Related pages

package Nuclet


Nuclet.

Open →

gear Client-side rules


Client-side.

Open →