Examples
In HybridForms development, TypeScript functions are crucial for creating dynamic Forms. This guide explores their syntax, strategies, and best practices within the Framework. From event handling to data manipulation, functions empower developers to optimize Form performance and enhance user experience.
Our reliance on the FormAPI is significant for tasks such as retrieving and setting Form Control values, data manipulation, and managing Form events. For further details, please refer to our documentation available here.
Combining Two ComboBoxes​
- TypeScript
- HTML
namespace HFFormdefinition.SomeNamespace {
/*
* filterData
* Type: onChanged Method
* Description:
* When choosing a Value from the Main ComboBox, the content in the Sub ComboBox
* will change to the relevant content for the selection.
* Works in both non- and repeating units.
* @data-hf-options:
* subCtrlId[string](required): formcontrol-id of effected ComboBox
*/
export function filterData() {
const filterField = this.val();
let currentRU = '';
if (HybridForms.API.RepeatingUnits.isRepeatingUnit(this.element)) {
currentRU = HybridForms.API.RepeatingUnits.getPostfixFieldId(this.element);
}
const comboBoxTargetCtrl = HybridForms.API.FormControls.getCtrl(this.subCtrlId + currentRU);
if (typeof comboBoxTargetCtrl === 'undefined') {
return;
}
const dataSource = comboBoxTargetCtrl.dataSource;
if (!filterField || !filterField.value) {
comboBoxTargetCtrl.setDataSource(dataSource);
return;
}
const filterByValue = filterField.value;
const filtered = dataSource.filter(function (item) {
return filterByValue === item.kat;
});
comboBoxTargetCtrl.setDataSource(filtered);
}
}
<!-- ComboBox: Begin - Field Dependence: When choosing a Value from the Main ComboBox,
the content in the Sub ComboBox will change to the relevant content for the selection. -->
<div
id="tab1_combobox_haupt"
data-hf-control="ComboBox"
data-hf-options="{
label: 'Main ComboBox',
dataSource: [
{
kat:1,
name:'Main selection 1'
},
{
kat:2,
name:'Main selection 2'
},
{
kat:3,
name:'Main selection 3'
}],
dataTextField: 'name',
dataValueField: 'kat',
onChanged: HFFormdefinition.SomeNamespace.filterData,
subCtrlId: 'tab1_combobox_sub'
}"
></div>
<!-- Replace Namespace from added Script -->
<div
id="tab1_combobox_sub"
data-hf-control="ComboBox"
data-hf-options="{
label: 'Sub ComboBox',
dataSource: [
{
value:101,
name:'Sub selection 1.1',
kat:1
},
{
value:102,
name:'Sub selection 1.2',
kat:1
},
{
value:201,
name:'Sub selection 2.1',
kat:2
},
{
value:202,
name:'Sub selection 2.2',
kat:2
},
{
value:301,
name:'Sub selection 3.1',
kat:3
},
{
value:302,
name:'Sub selection 3.2',
kat:3
}],
dataTextField: 'name',
dataValueField: 'value'
}"
></div>
<!-- ComboBox: End - Field Dependence -->
Initializr Control​
- TypeScript
- HTML
namespace HFFormdefinition.SomeNamespace {
export function highlightCell() {
$('input:radio, input:checkbox').each(function () {
const id = this.dataset?.hfId ? this.dataset.hfId : this.id;
const ctrl = HybridForms.API.FormControls.getCtrl(id);
const val = ctrl.val();
$(this)
.closest('div.color')
.toggleClass('highlight', val ? val : false);
});
}
/**
* Initializr: calls a callback function when the form is rendered
*/
export class Initializr extends HybridForms.API.UIControls.BaseControl {
private onRendered: () => void;
public callback: () => void;
constructor(element, options) {
super(element, options);
if (typeof this.callback === 'function') {
this.onRendered = () => {
this.callback.call(this);
};
HybridForms.API.Page.addEventListener('rendered', this.onRendered);
HybridForms.API.Page.addEventListener('viewrendered', this.onRendered);
}
}
public dispose() {
if (this.disposed) {
return;
}
if (this.onRendered) {
HybridForms.API.Page.removeEventListener('rendered', this.onRendered);
HybridForms.API.Page.removeEventListener('viewrendered', this.onRendered);
}
this.disposed = true;
}
}
}
<div
id="init_highlight_cell"
class="pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.Initializr"
data-hf-options="{
callback: HFFormdefinition.SomeNamespace.highlightCell,
}"
></div>
Data Source Handling​
- TypeScript
- HTML
namespace HFFormdefinition.SomeNamespace {
/**
* setDataSource: sets the data source for a control
*/
export function setDataSource() {
HybridForms.API.FormStorage.getCatalog('StateCatalog').then((dataSource) => {
const amountField = HybridForms.API.Fields.getById('programmatic_set_datasource');
const fieldValue = amountField.value;
const amountCtrl = HybridForms.API.FormControls.getCtrl('programmatic_set_datasource');
amountCtrl.isLoaded().then(() => {
amountCtrl.setDataSource(dataSource);
if (fieldValue) {
amountCtrl.val(fieldValue);
}
});
});
}
}
<var data-hf-name="DataSource" data-hf-data-source-id="StateCatalog"
>/api/catalogs/Staat?$select=Title,KatalogText&$orderby=KatalogText&$top=5000</var
>
User Display Name Handling​
- TypeScript
- HTML
namespace HFFormdefinition.SomeNamespace {
export function setUser() {
return HybridForms.API.User.get().displayName || '';
}
}
<input
id="username"
type="text"
placeholder="Username"
data-hf-control="TextField"
data-hf-options="{
label: 'Username,
disabled: true,
defaultValue: HFFormdefinition.SomeNamespace.setUser
}"
/>
Repeating Inputs​
- TypeScript
- HTML
- SCSS
namespace HFFormdefinition.SomeNamespace {
export class addInput extends HybridForms.API.UIControls.BaseControl {
constructor(element, options) {
super(element, options);
}
private clickEvent(event) {
const targetId = event.currentTarget.id;
const $block = $(`#${targetId}`).closest('.repeating-input-container');
$('.repeating-input.hf-hide', $block).first().removeClass('hf-hide');
}
private getElementId(element: HTMLElement): string {
if (this.isView) {
return element.dataset.hfId;
}
return element.id;
}
private showRepeatingInputs($ct: JQuery<HTMLElement> | HTMLElement) {
$('.repeating-input:not(.init)', $ct).each((idx, element) => {
const $element = $(element);
const $input = $('.hf-formcontrol', $element);
let filled = false;
$input.each((index, el) => {
const field = HybridForms.API.Fields.getById(this.getElementId(el));
const value = field?.value;
if (value) {
filled = true;
}
});
if (filled) {
$element.removeClass('hf-hide');
} else {
$element.addClass('hf-hide');
}
});
}
protected createView() {
const $ct = this.$element.closest('.repeating-input-container');
this.showRepeatingInputs($ct);
}
protected createControl(): Promise<void> {
const currentStatus = HybridForms.API.Form.getStatus();
if (currentStatus > 1) {
this.$element.parents('.add-input-wrapper').addClass('hf-hide');
}
const $ct = this.$element.closest('.repeating-input-container');
this.showRepeatingInputs($ct);
this.$element.on('click', (ev) => {
ev.stopImmediatePropagation();
ev.preventDefault();
this.clickEvent(ev);
});
return Promise.resolve();
}
public dispose() {
if (this.disposed) {
return;
}
this.$element.off('click');
this.disposed = true;
}
}
export class removeInput extends HybridForms.API.UIControls.BaseControl {
constructor(element, options) {
super(element, options);
}
private clickEvent(event) {
const targetId = event.currentTarget.id;
const $block = $(`#${targetId}`).closest('.repeating-input-container');
const $inputBlock = $('#' + event.currentTarget.id, $block).closest('.repeating-input');
const $input = $('.hf-formcontrol', $inputBlock);
$input.each((idx, element) => {
HybridForms.API.FormControls.getCtrl(element.id).val(null);
});
const $ruInputs = $('.repeating-input:not(.hf-hide)', $block);
$ruInputs.each((idx, context) => {
const $context = $(context);
const idPostfix = idx + 1;
let emptyRow = true;
const $formControls = $('.hf-formcontrol', $context);
$formControls.each((index, element) => {
if ($(element).hasClass('filled')) {
emptyRow = false;
}
});
if (emptyRow && idPostfix < $ruInputs.length) {
$formControls.each((index, element) => {
const id = element.id;
const ctrl = HybridForms.API.FormControls.getCtrl(id);
const nextId = id.replace(/input_(?:\d*)/, `input_${idPostfix + 1}`);
let nextCtrl = HybridForms.API.FormControls.getCtrl(nextId);
let nextVal = nextCtrl.val();
if (nextVal && typeof nextVal.value !== 'undefined') {
nextVal = nextCtrl.val().value;
}
ctrl.val(nextVal);
nextCtrl.val(null);
nextCtrl = null;
});
}
});
$('.repeating-input:not(.hf-hide):not(.init)', $block).last().addClass('hf-hide');
}
protected createControl(): Promise<void> {
const currentStatus = HybridForms.API.Form.getStatus();
if (currentStatus > 1) {
this.$element.addClass('hf-hide');
}
this.$element.on('click', (ev) => {
ev.stopImmediatePropagation();
ev.preventDefault();
this.clickEvent(ev);
});
return Promise.resolve();
}
public dispose() {
if (this.disposed) {
return;
}
this.$element.off('click');
this.disposed = true;
}
}
}
<div class="repeating-input-container">
<div class="grid column1">
<div class="r1 c1">
<div
id="example"
type="text"
data-hf-control="Label"
data-hf-options="{
label: 'label',
required: true,
requiredFields: 'example_input_1'
}"
></div>
</div>
</div>
<div class="grid column1 repeating-input init">
<div class="r1 c1">
<input id="example_input_1" type="text" data-hf-control="TextField" />
<div
id="example_remove_input_1"
class="remove-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.removeInput"
>
<p class="remove-icon"><i class="fal fa-minus"></i></p>
</div>
</div>
</div>
<div class="grid column1 repeating-input">
<div class="r1 c1">
<input id="example_input_2" type="text" data-hf-control="TextField" />
<div
id="example_remove_input_2"
class="remove-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.removeInput"
>
<p class="remove-icon"><i class="fal fa-minus"></i></p>
</div>
</div>
</div>
<div class="grid column1 repeating-input">
<div class="r1 c1">
<input id="example_input_3" type="text" data-hf-control="TextField" />
<div
id="example_remove_input_3"
class="remove-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.removeInput"
>
<p class="remove-icon"><i class="fal fa-minus"></i></p>
</div>
</div>
</div>
<div class="grid column1 repeating-input">
<div class="r1 c1">
<input id="example_input_4" type="text" data-hf-control="TextField" />
<div
id="example_remove_input_4"
class="remove-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.removeInput"
>
<p class="remove-icon"><i class="fal fa-minus"></i></p>
</div>
</div>
</div>
<div class="grid column1 repeating-input">
<div class="r1 c1">
<input id="example_input_5" type="text" data-hf-control="TextField" />
<div
id="example_remove_input_5"
class="remove-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.removeInput"
>
<p class="remove-icon"><i class="fal fa-minus"></i></p>
</div>
</div>
</div>
<div class="grid column3 add-input-wrapper">
<div class="r1 c1"> </div>
<div class="r1 c2"> </div>
<div class="r1 c3">
<div
id="example_add_input"
class="add-input pdf-hide"
data-hf-control="HFFormdefinition.SomeNamespace.addInput"
data-hf-options="{
inputId: 'example_'
}"
>
<p><strong>Hinzufügen</strong></p>
</div>
</div>
</div>
</div>
/* Repeating Input START */
.add-input-wrapper {
margin-bottom: 15px;
}
.add-input {
background-color: #d9d9d9;
border-radius: 4px;
padding: 10px 0;
cursor: pointer;
&:hover {
background-color: #cccccc;
}
p {
text-align: center;
margin: 0 !important;
}
}
.remove-input {
position: absolute;
top: 0;
right: 10px;
width: 30px;
height: 30px;
padding: 5px;
z-index: 9;
.remove-icon {
text-align: center;
color: black !important;
background-color: #d9d9d9;
margin: 0 !important;
height: 20px;
width: 20px;
border-radius: 100%;
cursor: pointer;
&:hover {
background-color: #cccccc;
}
}
}
.repeating-input.multiline {
.remove-input {
position: relative;
right: 0;
margin-left: 10px;
padding: 0;
.remove-icon {
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
width: 30px;
height: 30px;
}
}
.flex-1-shrink {
align-self: flex-end;
margin-bottom: 10px;
}
&:nth-child(even) {
background-color: rgba(217, 217, 217, 0.3);
}
}
.repeating-input {
display: none;
&.init {
display: block;
}
&:not(.multiline) {
.hf-numericfield:not(.no-padding) .k-input,
.hf-textfield:not(.no-padding) {
padding-right: 30px;
}
}
.hf-formcontrol {
&.hf-view {
display: none;
&.filled {
display: block;
}
}
}
}
/* Repeating Input END */