Child pages
  • Specification (.spec file)
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 70 Next »

A (WYSIWYG) form designer needs component meta data to be able to handle components. This meta data is expressed in a specification.

Definition

A simple metadata specification is expressed in json format. (called the .spec definition file)

The specification defines:

  • identifier info (component name)

    Name convention: the provided string is of the form package name, followed by a dash sign ('-'), followed by the component name. The package name is the name of the package the component is part of, and component name is simply the name of the component. Both package name and component name should be written in lower-case.

  • dependencies
  • definition of component logic, to support:
    • model properties
    • event handler
    • callable API
  • additional/child type info

In a json format:

spec 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 of this component",
	"libraries": Array of js/css definitions (map with 'name'-the lib name, 'version'-the lib version, 'url'-the lib url, 'mimetype'-the lib mimetype, one of 'text/javascript' or 'text/css') that needs to be included for this component,
	"model": {
	  "propertyName": type description, optional default value
	},
	"handlers": {
	  "functionName": "function type"
	},
	"api": {
	  "functionName": signature description json
	},
	"types": {
	  "typename": {
	    "propertyName": "type description"
 	  }
	}
}

A WebComponent specifies all its properties under the model, all the events under handlers and api has the javascript api the webcomponent has that the server can call.

Types are defining internal types that are used by this webcomponent for one or more of its properties.  (for example a tabpanel component has a tab type that describes one tab inside the tabpanel). They have the same support as under model:{}, there is no support for api or handlers on those types again because they are just container objects that push data to the webcomponent from the server.

Besides custom type descriptions any of the default provided property types can be used.

 

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

"functionName" : {
        "returns" : "string",
        "parameters" : [
            { "name" : "start", "type" : "int" },
            { "name" : "end", "type" : "int" } 
        ]
}

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

"functionName" : {
        "returns" : "string",
        "parameters" : [
            { "name" : "start", "type" : "int" },
            { "name" : "end", "type" : "int" } 
        ],
        "blockEventProcessing": true,
		"async": true
}

"blockEventProcessing" is optional and not needed in almost all cases. It defaults to true. When it is set to false, Servoy will expect that this API call will be long-running (so it will not time out) 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 call like "askForSomeValue(customForm)" 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.

"async" is another optional parameter (default is false), which means the client side api is not called right away, but at next message sent to the client. By default all apis are sync, so they call the client and wait for the response. Async methods cannot return a value.

Data synchronization

Data modifications are automatically propagated from server to client.

For performance reasons, data modifications from client to server are not propagated by default. To enable this configure pushToServer setting on the property.

pushToServer valuedescription
rejectthis is the default, no data synchronization from client to server for this property
allowdata changes from client to server are allowed
shallowsame as allow, but sablo will also add a watch on the model (in the client) and send changes to the server; the watch is based on object reference/primitive value (more performant, but in case of structured/nested objects it will only trigger data synchronization when the objects are different by reference, even if they are actually 'equal' by content)
deep

same as allow, but sablo will also add a watch on the model (in the client) and send changes to the server; the watch is based on object equality (compares old and new values of structured/nested objects, less performant because it keeps and compares to copies of structured/nested objects)

 

For example:

pushToServer example
"model": {
   "searchfield" : { "type": "string", "pushToServer": "shallow" }
   }
} 

 

Note that in Servoy, data-provider properties are synchronized using svy-apply svy-autoapply, for these properties pushToServer should be set to allow.

Tags

Properties can be configured with special tags that may be interpreted by the deployment environment.

Supported tags are: scope, directEdit, addToElementsScope and showInOutlineView


scope: Restricts the model property to: 'design', 'runtime' or 'private'. 

Design means property can only be assigned from designer (not from scripting). Runtime means the property cannot be assigned from designer (will be hidden in Properties View). Private is an internal property, that should only be used by component itself (so component.js or component_server.js). Will not show in Properties View and cannot be assigned from scripting.

 

directEdit: boolean 

One property of a component can be tagged as directEdit for designer, this way that property can be edited directly by double clicking the component (for example text property on label/button).

 

showInOutlineView: boolean

In the Outline View, Custom Type objects have the name of the type as lables. The showInOutlineView:true tag can be added to any property definition in order to append the design time value of that property to the label of the custom type object in the Outline View.


addToElementsScope : boolean

For component type, specify whether component should be added to elements scope. Default is false, so component can be accessed only via component property. If true, will be accessible like any other element of the form.

Security

Components can be protected using two special security types: visible and protected.

For example. when a component is made readonly from the server the component will make the its UI non-editable. However, a malicious client could still send json messages to the server and update data.

Similarly, when a form or component is marked invisible, its data should not be sent to the client since it may contain sensitive data.

Protecting properties

Protecting properties (of type protected)are used to protect an entire component or specific properties or handlers.

When the property is blocking no changes or function calls from the client are accepted.

For example, the editable property as defined below will be true by default, when set to false changes from the client to the component will be rejected. Also functions cannot be called from the client.

Example editable
"model": {
   "editable" : { "type": "protected", "blockingOn": false, "default": true }
} 

 

Protection can be done for specific properties or functions.

In this example, when protectCustomer is set to true, customerAddress can still be changed the the client, but customerName and

removecustomer are protected.

Example protecting properties and handlers
"model:" {
    "customerAddress": "string", 
    "customerName": "string", 
    "protectCustomer": { "type": "protected", "for": ["removecustomer", "customerName"] }
   },
"handlers:" {
   "removecustomer": "function"
}

 

Protecting properties themselves can never be modified from the client.

Visibility properties

Visibility properties (of type visible) are similar to protecting properties. They are protecting the component and also hide the data from the client if the component is not visible.

In this example, a component's model can be filled with a customer name, but when the property visible is set to false, the component will be protected and the data will not be sent to the client. When visible is set to true, data not sent before will be pushed to the client.

Example visible
"model": {
   "visible" : "visible",
   "customerName": "string"
} 

 

Visibility properties themselves can never be modified from the client.

Enable properties

Similarly to properties of type visible , the properties  of type enabled are also protecting properties, so they can never be modified from the client. 

Example enabled
"model": {
   "enabled" : { "type": "enabled", "blockingOn": false, "default": true}
} 

It is important to use type enabled  if we want the value from the parent container (i.e. form, portal) to be pushed to the component. For instance if a portal is disabled, then all the components from the portal which have a property of type enabled will also be disabled.

Container security

Protecting and visibility properties on containers will protect the container and also components inside the comtainer.

Readonly/Findmode

Servoy has 2 special controller/foundset based properties where a component also can be notified for.

controller.readOnly = true in the developer will set this boolean to a property called "readOnly" of a webcomponent

 

Listening to readonly
"model" : {
  "readOnly": "protected"
}

 

With this property a component can do its thing to set or unset the readonly mode for itself.

It's better to have type this property as "protected" because it should only be able to change at the server, never from the client.

 

Findmode is a special type: Findmode property type which can be used to set all kind of other properties which are normally protected from changing on the client side. Or you can just use it as a type for any property you want: 

Listening to findmode
"model" : {
  "myfindmodeproperty": "findmode"
}

 

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.

Example

As an example we could have a Tab Panel that has a definition like: (note: this example does not contain the entire Tab Panel spec)

tabpanel.spec
{
    "name": "servoydefault-tabpanel",
    "displayName": "Tab Panel",
    "version": 1,
    "icon": "servoydefault/tabpanel/tabs.gif",
    "definition": "servoydefault/tabpanel/tabpanel.js",
    "serverscript": "servoydefault/tabpanel/tabpanel_server.js",
    "libraries": [{"name":"accordionpanel", "version":"1", "url":"servoydefault/tabpanel/accordionpanel.css", "mimetype":"text/css"}],
    "model":
    {
            "background" : "color", 
            "borderType" : {"type":"border","stringformat":true}, 
            "enabled" : { "type": "enabled", "blockingOn": false, "default": true }, 
            "fontType" : {"type":"font","stringformat":true}, 
            "foreground" : "color", 
            "horizontalAlignment" : {"type" :"int", "tags": { "scope" :"design" }, "values" :[{"LEFT":2}, {"CENTER":0},{"RIGHT":4}],"default" : -1}, 
            "location" : "point", 
            "readOnly" : "protected", 
            "selectedTabColor" : "color", 
            "size" : {"type" :"dimension",  "default" : {"width":300, "height":300}}, 
            "styleClass" : { "type" :"styleclass", "tags": { "scope" :"design" }, "values" :[]}, 
            "tabIndex" : { "type": "object", "pushToserver": "shallow" }, 
            "tabOrientation" : {"type" :"int", "tags": { "scope" :"design" }, "values" :[{"default" :0}, {"TOP":1}, {"HIDE":-1}]}, 
            "tabSeq" : {"type" :"tabseq", "tags": { "scope" :"design" }}, 
            "tabs" : {"type":"tab[]", "droppable":true}, 
            "transparent" : "boolean", 
            "visible" : "visible" 
    },
    "handlers":
    {
            "onChangeMethodID" : "function", 
            "onTabChangeMethodID" : "function" 
    },
    "api":
    {
            "addTab": {
                "returns": "boolean",
                "parameters":[
                                {                                                                 
                                 "name":"form/formname",
                                "type":"object []"
                                },
                                 {                                                                 
                                 "name":"name",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"tabText",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"tooltip",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"iconURL",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"fg",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"bg",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"relatedfoundset/relationname",
                                "type":"object",
                                "optional":true
                                },
                                 {                                                                 
                                 "name":"index",
                                "type":"object",
                                "optional":true
                                }             
                             ]
            },
            "getMaxTabIndex": {
                "returns": "int"
            },
            "getMnemonicAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "getSelectedTabFormName": {
                "returns": "string"
            },
            "getTabBGColorAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"unnamed_0",
                                "type":"int"
                                }             
                             ]
            },
            "getTabFGColorAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "getTabFormNameAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "getTabNameAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "getTabRelationNameAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "getTabTextAt": {
                "returns": "string",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "isTabEnabled": {
                "returns": "boolean",
                "parameters":[
                                {                                                                 
                                 "name":"unnamed_0",
                                "type":"int"
                                }             
                             ]
            },
            "isTabEnabledAt": {
                "returns": "boolean",
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                }             
                             ]
            },
            "removeAllTabs": {
                "returns": "boolean"
            },
            "removeTabAt": {
                "returns": "boolean",
                "parameters":[
                                {                                                                 
                                 "name":"index",
                                "type":"int"
                                }             
                             ]
            },
            "setMnemonicAt": {
                "parameters":[
                                {                                                                 
                                 "name":"index",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"text",
                                "type":"string"
                                }             
                             ]
            },
            "setTabBGColorAt": {
                "parameters":[
                                {                                                                 
                                 "name":"unnamed_0",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"unnamed_1",
                                "type":"string"
                                }             
                             ]
            },
            "setTabEnabled": {
                "parameters":[
                                {                                                                 
                                 "name":"unnamed_0",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"unnamed_1",
                                "type":"boolean"
                                }             
                             ]
            },
            "setTabEnabledAt": {
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"b",
                                "type":"boolean"
                                }             
                             ]
            },
            "setTabFGColorAt": {
                "parameters":[
                                {                                                                 
                                 "name":"i",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"s",
                                "type":"string"
                                }             
                             ]
            },
            "setTabTextAt": {
                "parameters":[
                                {                                                                 
                                 "name":"index",
                                "type":"int"
                                },
                                 {                                                                 
                                 "name":"text",
                                "type":"string"
                                }             
                             ]
            }
    },
"types": {
  "tab": {
          "name": "string",
          "containsFormId": "form",
          "text": "tagstring",
          "relationName": "relation",
          "active": "boolean",
          "foreground": "color",
          "disabled": "boolean",
          "imageMediaID": "media",
          "mnemonic": "string"
      }
}
     
}

 

Serverside scripting

A component or service can have a serverside part, so logic is executed on the server, in the spec file this is configured like:

.spec file
"serverscript": "servoydefault/tabpanel/tabpanel_server.js",

 

That file has that also the implementation of the api that is defined in the spec file, so you can handle complex things server side, this can be used as a performance enhancement so that the api call doesn't have to go to the client to execute at the moment the call comes from servoy code.

The api call that is executed serverside could for example just update some models of the component/service, these will be then send as one thing when the servoy code is done.

Serverside code is build up the same as the clientside so you have $scope object with model and api objects. On that api object you have to define then the api methods that you want to execute server side. An example is the default tabpanel

What can also be done (servoy 8.0.2+) is that clientside scripting can call the server part. These function has to be defined on the $scope object, as an example:

 

serverside script
$scope.mycallback = function(name,type) {
	return "something";
}

 

then a component can use the servoyapi to call this

client javascript
  // assign to the scope the svy-servoyapi
  scope: {
    	  model: '=svyModel',
		  servoyApi: '=svyServoyapi'
      },
 
// in the controller or link functions you can use that then
 $scope.servoyApi.callServerSideApi("mycallback",["test",1]).then(function(retValue) {
   console.log(retValue);    
 });

 

the callServerSideApi wants to have the function name and an array of arguments, it returns a promise of angular where the then function will give you the return value of the callback.

For a service you can use the $services service to do the same thing. the only thing extra is that you have to also give the servicename itself:

clientside service script
$services.callServerSideApi("myservicename","mycallback",["test",1]).then(function(retValue) {
   console.log(retValue);    
 });

Palette categorisation

Web Components are organised 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'.

Example: 

Category Name
{
	"name": "packagename-componentname",
	"displayName": "String more descriptive name that is shown in the designer",
	"categoryName": "Advanced",
...
}



 

 

 

  • No labels