How to Create a Custom Field
This cheat sheet shows how to implement your own custom field for a ScoutJS application. In this example we will write a FlipCard field that will show a playing card. Clicking on the card will flip it from one side to the other.
Setup
For this example we use the helloscout git repository, but you can easily create the field in your own code base as well. In case you want to use the helloscout repo, clone and import it into your favourite IDE:
git clone https://github.com/bsi-software/helloscout.git
Read the readme and start the hellojs application to make sure it works.
Add Empty TS and CSS Files
Create the following files in the folder org.eclipse.scout.hellojs.ui.html
-
src/main/js/flipcard/FlipCardField.ts
The TypeScript file representing the field. -
src/main/js/flipcard/FlipCardField.less
The LESS file containing the styles of the field.
Minimal Code for a New FormField
Create a Minimal FormField
The FlipCard will inherit from FormField. Every form field consists of a container, a label, the actual field, a mandatory-indicator and a status.
import {FormField} from '@eclipse-scout/core';
export class FlipCardField extends FormField {
protected override _render() {
// Create the container
this.addContainer(this.$parent, 'flip-card-field');
// Add a label
this.addLabel();
// Create the actual field. This will be your flip card.
let $field = this.$parent.appendDiv('content');
// add the field to the form field.
this.addField($field);
// Add other required form field elements
this.addMandatoryIndicator();
this.addStatus();
};
}
Since we use JQuery in our FlipCardField, we should add it as dependency to our package.json
to get code completion and proper syntax highlighting.
Don’t forget to add the types as well because JQuery does not include them.
{
...
"devDependencies": {
...
"@types/jquery": "3.5.32"
},
"dependencies": {
...
"jquery": "3.7.1"
}
}
Add the FlipCard to the HelloForm
Don’t forget to add the import
for your FlipCardField
.
import {FlipCardField, FormModel} from '@eclipse-scout/core';
export default (): FormModel => ({
...
rootGroupBox: {
...
fields: [
{
id: 'DetailBox',
...
fields: [
{
id: 'NameField',
...
},
(1)
{
id: 'FlipCardField',
objectType: FlipCardField,
label: 'Flip the card',
gridDataHints: {
h: 5,
weightY: 0
}
},
{
id: 'GreetButton',
...
}
]
}
]
}
});
1 | The FlipCard field |
Now reload your browser and you should get the following result:
Full Featured Flip Card Field
import {FormField} from '@eclipse-scout/core';
export class FlipCardField extends FormField {
$card: JQuery;
$front: JQuery;
$back: JQuery;
flipped: boolean;
frontImage: string;
backImage: string;
constructor() {
super();
this.flipped = false;
}
protected override _render() {
// Create the container
this.addContainer(this.$parent, 'flip-card-field');
// Add a label
this.addLabel();
// Create the actual field (1)
let $field = this.$parent.appendDiv('content');
// Create the card inside the field
this.$card = $field.appendDiv('card')
.on('mousedown', this._onCardMouseDown.bind(this)); (2)
this.$front = this.$card.appendDiv('front');
this.$back = this.$card.appendDiv('back');
// Add the field to the form field. It will be available as this.$field.
this.addField($field);
// Add other required form field elements
this.addMandatoryIndicator();
this.addStatus();
}
protected override _renderProperties() { (3)
super._renderProperties();
this._renderFrontImage();
this._renderBackImage();
this._renderFlipped();
}
protected _renderFrontImage() {
if (this.frontImage) {
this.$front.append(`<img src="${this.frontImage}">`);
}
}
protected _renderBackImage() {
if (this.backImage) {
this.$back.append(`<img src="${this.backImage}">`);
}
}
protected override _remove() { (4)
super._remove();
this.$card = null;
this.$front = null;
this.$back = null;
}
protected _onCardMouseDown(event: JQuery.MouseDownEvent) { (2)
this.setFlipped(!this.flipped);
}
setFlipped(flipped: boolean) {
this.setProperty('flipped', flipped);
}
protected _renderFlipped() {
this.$card.toggleClass('flipped', this.flipped);
}
}
1 | Create the dom elements in the render function. |
2 | Add event handler which toggles the CSS class flipped . |
3 | Initial rendering of the properties. Applies the state to the DOM. |
4 | Keep the references clean. Reset DOM references when the field has been removed. |
import {FlipCardField} from '../index';
export default {
id: 'hellojs.HelloForm',
...
rootGroupBox': {
...
fields: [
{
id: 'DetailBox',
...
fields: [
{
id: 'NameField',
...
},
(1)
{
id: 'FlipCardField',
objectType: FlipCardField,
label: 'Flip the card',
frontImage: 'img/card-back.jpg',
backImage: 'img/card-front.jpg',
gridDataHints: {
h: 5,
weightY: 0
}
},
{
id: 'GreetButton',
...
}
]
}
]
}
}
1 | FlipCard field is inserted after the name field. |
.flip-card-field {
.card {
position: absolute;
cursor: pointer;
height: 100%;
width: 152px;
transition: transform 1s; (1)
transform-style: preserve-3d;
&.flipped {
transform: rotateY( 180deg );
}
& > div {
display: block;
height: 100%;
width: 100%;
position: absolute;
backface-visibility: hidden; (2)
&.back {
transform: rotateY( 180deg ); (3)
}
& > img {
height: 100%;
width: 100%;
}
}
}
}
1 | Animation of the card. |
2 | Ensure back side is not visible. |
3 | Rotation to back side. |
Finally, create a folder img
in the WebContent folder (org.eclipse.scout.hellojs.ui.html.app/src/main/resources/WebContent) and paste the two images of the card into that folder. You should be able to find the images using Google ;-)