Child pages
  • Specification (.spec file)

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejs
titlespec file definition
{
	"name": "packagename-componentname", // String
	"displayName": "more descriptive name that is shown in the designer", // String
	"version": the component version (integer)
	"icon": "A reference to the icon shown in designer" ,
    "preview": "A reference to the preview gif to be displayed" ,

	"definition": "A reference to the js file that implements this component's in the browser",
    "serverscript": "[optional] A reference to the js file that implements this component's server-side logic, if any.",
	"doc": "[optional] A reference to the js file that contains the jsdocs of the component api or model properties.",
    "group": true // default true, so the definition file can be grouped when creating the .war file for deployment
    "deprecated": "This component will be replaced in the next versions.", // (optional) some string to mark the component as deprecated - if it is deprecated
	"replacement": "package-compname", // (optional) in case of deprecation, developer will provide a quick fix for such components to be automatically changed to the given replacement component
                                       // (make sure they have compatible .spec defined; in most cases this is useful when moving components from a package to another package or when
                                       // rewriting a component but keeping it's contract unchanged)
	"libraries": /* Array of js/css definitions (which are JSON objects with 
                             'name'-the lib name, 'version'-the lib version, 'url'-the lib url, 
                             'mimetype'-the lib mimetype, one of 'text/javascript' or 'text/css', 
                             'group' - give false here when this lib dependency should not be grouped when exported
                             as .war; default true)
                    that need to be included for this component. */,

	"keywords": /* Array of "categoryName": "Advanced", // category for form designer palette; only makes sense for components, not services

	"model": {
	    "property1Name": /* keywords used for searching components in the palette.
				   For instance, for the calendar component some appropriate keywords that might be used are: "day", "month", "year"*/,

    "categoryName": "Advanced", // category for form designer palette; only makes sense for components, not services

	"model": {
	    "property1Name": /* type description (optionally including optional default value and others) */,
	    "property2Name": /* type description (optionally including optional default value and others) */
	},

	"handlers": {
	    "handler1Name": { /* handler function type definition*/ },
	    "handler2Name": { /* handler function type definition*/ }
	},

	"api": {
	    "apiFunction1Name": { */ api function signature description json*/ },
	    "apiFunction2Name": { */ api function signature description json*/ }
	},

    "internalApi" : {
	    "internalApiFunction1Name": { */ internal api function signature description json*/ },
	    "internalApiFunction2Name": { */ internal api function signature description json*/ }
    },

	"types": {
	    "customType1Name": {
            "subProp1Name": /* type description" */,
            "subProp2Name": /* type description" */
        },
	    "customType2Name": {
            "subProp1Name": /* type description" */,
            "subProp2Name": /* type description" */
       }
    }
}

...

When the find mode changes the boolean value of your property will also change. This way you can react to a form/foundset going into find mode. Like resetting a specific format, allowing any kind of input.

...

Anchor
documentingProperties
documentingProperties
Documenting what properties do

Documentation for properties can be added to each property's definition via the "tags" section using key "doc" .The description that you provide or in the .spec file will be used in Servoy Developer asdoc file using a variable with same name as the property.

Code Block
languagejs
/**
 * some desciption
 * @example elements.%%elementName%%.yourName = 'myname'
 */
var yourName;

The description that you provide in the .spec file will be used in Servoy Developer as:

  • tooltip in properties view
  • tooltip in solution explorer view
  • tooltip in script editor
  • any other place in developer where it can help the user of your custom component understand what that property does.

...

Code Block
	(...)
	"model": {
		"columns": {
						"type": "column[]",
						"tags": { "doc": "Define the table's columns using this property." },
						"elementConfig": {
							"tags": { "doc": "A column definition describes all that is needed to show that column properly in the table." }
						},
						(...)
		           },
		(...)
	},
	"types": {
		"column": {
			"dataprovider": { "type": "dataprovider", "forFoundset": "foundset", (...), "tags": { "doc" : "Choose the data that is to be shown in this column." } ),
			"format": { "type": "format", "for": ["valuelist", "dataprovider"], "tags": { "doc" : "This format will be applied on the dataprovider's data before showing it in the table." } },
			"valuelist": { "type": "valuelist", "for": "dataprovider", "forFoundset": "foundset" },
			(...)
		},
	(...)

Handlers

The function description in the handlers section can be just "function" For information about documenting handlers see Documenting handlers and handler templates below. For information about documenting API functions see the documenting api functions example.

Handlers

The function description in the handlers section can be just "function" or a more complex type describing the arguments and return type:

Code Block
"functionName": {
        "returns":  "string",
        "parameters": [
            { "name": "start", "type": "int" },
            { "name": "end", "type": "int" } 
        ],
		"private": true // since Servoy 8.3, optional, default is false,
		"ignoreNGBlockDuplicateEvents": true // since Servoy 2020.12, default is false
}

the The "private" configuration makes the handler only accessible through from Server Side Scripting. But , not by from the client/browser itself.

Api

This section defines the signatures and behaviors of functions that can be called from the solution on this component/service.

The signature description json for functions in the api section describes the arguments, type of call and return type, For example:

...

The "ignoreNGBlockDuplicateEvents" configuration makes the handler ignore the system NG_BLOCK_DUPLICATE_EVENTS property.

A handler can have a JSEvent property type which is able to map a Dom event to a JSEvent object that the handler can have in Servoy scripting. A mapping is mostly made automatically by calling using in the template: 


Code Block
svy-click='handlers.onActionMethodID($event)'

If you need to make one manually you can also do that by using the $utils factory service


Code Block
$utils.createJSEvent(e,"onselectionchanged")


Anchor
documentingHandlers
documentingHandlers
Documenting handlers and handler templates

Handlers can be documented - for use inside the developer - using the following keys (these are used in properties view tooltip or when generating new handler methods):

  • doc (used to be description; describes when the handler will be called and what it's for)
  • code (when a new method  is created for that handler, this default code template will be inserted)
  • for return value details returns can be an object with sub-keys: { type, doc, default (only relevant if "code" was not given) }
  • for parameter details parameters: [ { doc, optional (is false by default) } ].

For example:

Code Block
languagejs
"onDataChange": {
        "returns":  { "nametype": "startIndexboolean", "typedoc": "int" },
            { "name": "endIndexif it returns true then the data change is considered to be valid, otherwise it will be blocked/reverted by the component", "typedefault": "int"true },
        "parameters": [
  {               {  "name": "theDatanewValue", "type": "string", "doc": "the new value entered by         "type": {the user" },
            { "name":       "oldValue", "type": "datasetstring", "optional": true, "doc": "the previous value if available" } 
        ],
 		"includeColumnNamesdoc": true,
                    "columnTypes": { "icon": "media" }
                }
            }
        ]
}

The implementation of such api functions can be located either in the browser-side component/service logic implementation ("definition" in .spec file) or in the server-side component/service logic implementation ("serverscript" in .spec file)

There are several types of api calls described below.

Note
titleAbout calling browser-side api functions of components

When a component (not service) api function that the solution can call is implemented in browser-side component/service logic implementation ("definition" in .spec file) it is important to note that calling such a function when the form of that component is not shown will result in a temporary "force-load" of that form in a hidden div in the browser - just to be able to call that api function. As this is usually not useful and will slow-down the solution due to the hidden loading of a form, this situation should be avoided (either by the solution or, where possible, by using delay until form loads async api functions).

Sync functions

This is the default type of api function; the example above is a sync api function definition. Sync functions will call the client and wait for the api function to execute/return before continuing. Sync api functions can have a return value.

Sync functions that do not block event processing

A special kind of sync api function type is sync functions that do not block event processing. This is not needed - in almost all cases.

It can be defined by adding a "blockEventProcessing" : false (that defaults to true) to the function definition. When it is set to false, Servoy will expect that this API function call will be long-running (so it will not time out if it takes long to execute) and at the same time it will allow other events to be handled by the event dispatcher while waiting for the return value. This is helpful for example if a component would have an API function call like "askUserForSomeValue(someForm, ...)" which shows a modal dialog containing a form and returns a value. That API call has to wait for the return value an indefinite amount of time without blocking other events from being dispatched. Example:

Code Block
languagejs
        "askUserForSomeValue": 
        {
            "blockEventProcessing": false,
            "returns": "string","<b>onDataChange</b> will be called if the user modified the field's content and then either tabbed out/clicked outside of the field or hit enter. The handler can decide wether to allow the change or not.",
		"code": "// if we set this we should remove the 'default' from 'returns'\nreturn !!newValue;"
}

For information about documenting properties see Documenting what properties do above,. For information about documenting API functions see the documenting api functions example.

Api

This section defines the signatures and behaviors of functions that can be called from the solution on this component/service.

The signature description json for functions in the api section describes the arguments, type of call and return type, For example:

Code Block
"someFunctionName": {
        "parameters": [
            { "name": "startIndex", "type": "int" },
            { "name": "endIndex", "type": "int" },
            {
                "name": "theData",
                "type": {
                    "type": "dataset",
                    "includeColumnNames": true,
                    "columnTypes": { "icon": "media" }
                }
            }
        ]
}

The implementation of such api functions can be located either in the browser-side component/service logic implementation ("definition" in .spec file) or in the server-side component/service logic implementation ("serverscript" in .spec file)

There are several types of sync/async api calls described below.

Anchor
HiddenDivNote
HiddenDivNote

Note
titleAbout calling browser-side api functions of components


When a component (not service) sync or simple async api function (see below) that the solution can call is implemented in browser-side component/service logic implementation ("definition" in .spec file), it is important to note that calling such a function when the form of that component is not already created in browser DOM will result in a temporary "force-load" of that form in a hidden div in the browser - just to be able to call that api function. As this is usually not useful and will slow-down the solution due to the hidden loading of a form, this situation should be avoided (either by the solution - calling the api call later after the form was shown - or, where possible, by using delay until form loads async api functions in components - see below).

Servoy will log warning messages each time a sync API call to browser executes when the browser doesn't have the needed form present in DOM yet (triggering a force-load of the form in a hidden div). Most of the times this happens when solutions call component sync api functions to browser inside the onShow handler of a form.

For information about documenting API functions see the documenting api functions example.

Sync functions

This is the default type of api function; the example above is a sync api function definition. Sync functions will call the client and wait for the api function to execute/return before continuing. Sync api functions can have a return value.

Client side code of the sync api function can return either directly the intended return value or a Promise that in the end resolves with the intended return value. Server will wait for any client returned Promise to resolve before resuming server-side code execution.

In case of component sync functions, if the form is not present in browser's DOM then sync calls will force-load it in a hidden div just in order to execute the sync function on the component.

Sync functions that do not block event processing

A special kind of sync api function type is sync functions that do not block event processing. This is not needed - in almost all cases.

It can be defined by adding a "blockEventProcessing" : false (that defaults to true) to the function definition. When it is set to false, Servoy will expect that this API function call will be long-running (so it will not time out if it takes long to execute) and at the same time it will allow other events to be handled by the event dispatcher while waiting for the return value. This is helpful for example if a component would have an API function call like "askUserForSomeValue(someForm, ...)" which shows a modal dialog containing a form and returns a value. That API call has to wait for the return value an indefinite amount of time without blocking other events from being dispatched. Example:

Code Block
languagejs
        "askUserForSomeValue": 
        {
            "blockEventProcessing": false,
            "returns": "string",
            "parameters": [ { "name": "formToShow", "type": "form" }, { "name": "dialogTitle", "type": "string", "optional": "true" } ]
        }

Async

Async api calls will allow the calling code to continue executing and will execute the api call later/decoupled.

Async api functions cannot return a value.

There are 3 types of async calls.

Simple async functions

Simple async calls - when called from solution (server) code - will get executed in browser later, when current request to server is done.

Just add "async": true (another optional parameter (that by default is false)) to the call definition; it means the client side api is not called right away, but at next message sent to the client.

In case of component async functions, if the form is not present in browser's DOM when the async call is supposed to execute (later) then it will force-load it in a hidden div just in order to execute the async function on the component.

Code Block
languagejs
        "executeSomethingLater": 
        {
   "parameters": [ { "name": "formToShow", "type": "form" }, { "name": "dialogTitle", "type": "string", "optionalasync": "true" }
]         }

Async

...

Async api calls will allow the calling code to continue executing and will execute the api call later/decoupled.

Async api functions cannot return a value.

There are 3 types of async calls.

Simple async functions

...

-now service functions

These are currently supported only for ng services, not components. These are async functions that will be called in browser right away (rather then when the current request to server is done), but the code that calls them does not wait for them to finish execution before proceeding.

They are useful when for example you want to send progress information to the service client-side while executing a long-running-operation server-side.

Async-now function calls will not send other pending async calls to client nor update the client's state with other pending changes (model updates of components/services/etc.).

Just add "async-now": true (another optional parameter (that by default is false)) to the call definition; it means the client side api is not called right away, but at next message sent to the client.

Code Block
languagejs
        "executeSomethingLatersendProgress": 
        {
            "async-now": true,
            "parameters": [ { "name": "percent", "type": "int" } ]
        }

Async-now service functions

...

Delay-until-form-loads component functions
Anchor
delayUntilFormLoads
delayUntilFormLoads

These are special types of async calls meant only for components, not services. These functions, when called, will execute later (similar to simple async, when a request to server is done executing) , but the code that calls them does not wait for them to finish execution before proceeding.

They are useful when for example you want to send progress information to the service client-side while executing a long-running-operation server-side.

Just add "async-nowonly if the form is loaded inside the browser.

If the form is not loaded inside the browser they will wait for the form to get loaded (shown by solution somewhere) before being executed.

You can use these to avoid accidental calls from the solution to execute the api function without the solution intending to show the form. The difference compared to simple async functions is that simple async functions will load the form - if not already loaded in browser - in a hidden div just in order to execute the api call... this can be time-consuming and is usually not needed (a form that is not shown just being loaded and discarded in order to call an async function).

Just add "delayUntilFormLoads": true (another optional parameter (that by default is false)) to the call definition. This flag was also known previously as "delayUntilFormLoad" (but that is now deprecated).

Code Block
languagejs
        "sendProgressinitializeMyComponent": 
        {
            "async-nowdelayUntilFormLoads": true,
            "parameters": [ { "name": "percentinitialProgress", "type": "int" } ]
        }

...

These are special types of async calls meant only for components, not services. These functions, when called, will execute later (similar to simple async, when a request to server is done executing) but only if the form is loaded inside the browser.

If the form is not loaded inside the browser they will wait for the form to get loaded (shown by solution somewhere) before being executed.

You can use these to avoid accidental calls from the solution to execute the api function without the solution intending to show the form. The difference compared to simple async functions is that simple async functions will load the form - if not already loaded in browser - in a hidden div just in order to execute the api call... this can be time-consuming and is usually not needed (a form that is not shown just being loaded and discarded in order to call an async function).

...

Discard-previously-queued-similar-calls functions

This extra-flag only makes sense in combination with either "async" or "delayUntilFormLoads". Does not make sense for sync or async-now functions.

Calls to async functions with the same name and marked as "discardPreviouslyQueuedSimilarCalls" that come in before previously queued such calls are really sent to the browser window will discard those previous calls completely (and keep only the latest).

So for example a requestFocus() function call you would only need to send once - after an event handler finished execution server-side. You can send that later and only for the last requestFocus() that executed. If you click on a button for example and the solution has very complex code executing there that ends up calling requestFocus() 100 times on various components from various forms, you actually only want the last requestFocus to execute after the button is clicked. Otherwise there would be a huge performance penalty. That is why all requestFocus calls from existing components are (or should be) marked as "discardPreviouslyQueuedSimilarCalls".

Just add "discardPreviouslyQueuedSimilarCalls": true (another optional parameter (that by default is false)) to the call definition. This flag was also known previously as "delayUntilFormLoadglobalExclusive" (but that is now deprecated).

Code Block
languagejs
        "initializeMyComponentrequestFocus": {
            "parameters": [ { "name": "mustExecuteOnFocusGainedMethod", "type": "boolean", "optional": true {}],
            "delayUntilFormLoads": true,
            "parametersdiscardPreviouslyQueuedSimilarCalls": [true
{ "name": "initialProgress", "type": "int" } ]
        }

Discard-previously-queued-similar-calls functions

This extra-flag only makes sense in combination with either "async" or "delayUntilFormLoads". Does not make sense for sync or async-now functions.

Calls to async functions with the same name and marked as "discardPreviouslyQueuedSimilarCalls" that come in before previously queued such calls are really sent to the browser window will discard those previous calls completely (and keep only the latest).

So for example a requestFocus() function call you would only need to send once - after an event handler finished execution server-side. You can send that later and only for the last requestFocus() that executed. If you click on a button for example and the solution has very complex code executing there that ends up calling requestFocus() 100 times on various components from various forms, you actually only want the last requestFocus to execute after the button is clicked. Otherwise there would be a huge performance penalty. That is why all requestFocus calls from existing components are (or should be) marked as "discardPreviouslyQueuedSimilarCalls".

Just add "discardPreviouslyQueuedSimilarCalls": true (another optional parameter (that by default is false)) to the call definition. This flag was also known previously as "globalExclusive" (but that is now deprecated).

Code Block
languagejs
        "requestFocus": {
            "parameters": [ { "name": "mustExecuteOnFocusGainedMethod", "type": "boolean", "optional": true }],
            "delayUntilFormLoads": true,
            "discardPreviouslyQueuedSimilarCalls": true
        }

Server Side Scripting

...

Allow server side calls when component or parent form is not visible

By default, server side calls coming from client are ignored if the component or the parent form is not visible (anymore). For example, a call to a server side function when switching to a new form, to do some cleanup, might get blocked. To still allow these calls, you should add "allowaccess" : "visible" to the function definition in the .spec file.

Server Side Scripting

A component or service can have a server-side part as well (optional); this logic is executed directly on the server; in the spec file this is configured like:

Code Block
languagejs
title.spec file
"serverscript": "servoydefault/tabpanel/tabpanel_server.js",

See Server Side Scripting for more info about server side scripting.

Doc file

(From 2021.12 on)

A component or service can have a doc file for specifying documentation of the api and model properties. This is the same as the JSDoc in client side (.js) file, but is needed because NG2 doesn't have a js file that we can use in this way, requiring doc to be specified in another place.

The api doc/property doc can also be specified in the spec file itself using the doc property , however this is suitable only for small descriptions.  

Code Block
languagejs
title.spec file
"serverscriptdoc": "servoydefault/tabpanel/tabpanel_serverdoc.js",

...


Code Block
languagejs
titletabpanel_doc.js file
/**
 * some sample text
 * @example elements.%%elementName%%.yourName = 'myname'
 */
var yourName;

/**
 * This is a sample function from doc javascript file.
 */
function somemethod() {
}

Palette categories

Web Components are organized in component packages. The palette of the WYSIWYG editor shows components grouped by package name. To further group components from the same package, the property 'categoryName' can be used. 'categoryName' is a property that each component can specify in its spec file. The palette then displays components belonging to the same category grouped under the specified 'categoryName'.

...