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 16 Next »

Webcomponents need meta data to be able to function in a (form) designer and runtime environment.

On the web are some specifications arising like:

http://www.openajax.org/member/wiki/OpenAjax_Metadata_1.0_Specification_Widget_Overview (thanks to Paul for pointing us to this one)

At time of writing these seem overly complex when using angular directives...

Definition

An simple meta data specification can be expressed in json format. (spec definition file .spec)

The specification defines:

  • identifier info
  • dependencies
  • definition of component logic
    • model properties
    • event handler
    • callable API
  • additional/child type info

spec file definition
name: String simple name of the component
displayName: String more descriptive name that is shown in the designer
libraries: Array of js/css references that needs to be included for this component,
palette_icon: A reference to the icon shown in designer 
definition: A reference to the js file of this component
model: {
  propertyName: type description, optional default value
},
handlers: {
  functionName: function type
},
api: {
  functionName: signature description
},
types: {
   typename: {
     model: {
        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 that the type description can be a simple (property) types like:

  • string: A plain string property 

  • tagstring: A string that can have tags inside it (so it will be processed by servoy to have tags replaced before it gets to the client)

  • color: A color property (#FFFFFF)

  • point: A point representation \{x:10,y:20\}

  • dimension: A dimension representation \{width:100,height:200\}

  • insets:

  • font:

  • border:

  • boolean: true/false

  • scrollbars:

  • byte: 

  • double: A floating point number

  • float: A floating point number

  • int: A number

  • long: A number

  • short: A number

  • values: Fixed values can have real/display values.

  • dataprovider: Servoy maps this on a record or scope variable, This can be a complex object: \{ 'type':'dataprovider', 'ondatachange': \{ 'onchange':'onDataChangeMethodID', 'callback':'onDataChangeCallback'\}\} if support for ondatachange is needed.

  • valuelist: Servoy maps this on a valuelist referene

  • form: This property will hold a url point to a form (like a tab in a tabpanel)

  • format: format property, this must be specified with a complex object like: \{'for':'dataProviderID' ,'type':'format'\} so that we know which dataprovider property must be used to map this format property on

  • relation: Servoy maps this on a relation reference

  • media:

  • date: A date type property

 

The function description in the handlers section can be just "function" or a more complex type describing the arguments and return type {parameters: [{start: 'int'}, {end: 'int'}], returns: 'string'}

The  signature description should also be like the complex function type, describing the arguments and return type {parameters: [{start: 'int'}, {end: 'int'}], returns: 'string'}

Example

As an example we could have a textfield that has a definition like this

datafield.spec
name: 'svy-textfield',
displayName: 'Text input',
definition: 'servoydefault/textfield/textfield.js',
libraries: ['servoydefault/textfield/formatting.js','//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css'],
model:
{
	toolTipText: 'string',
	background: 'color',
    dataProviderID: { 'type':'dataprovider', 'ondatachange': { 'onchange':'onDataChangeMethodID', 'callback':'onDataChangeCallback'}},
    format: {'for':'dataProviderID' ,'type':'format'}, // value will be just the format string as a whole
},
handlers:
{
    onDataChangeMethodID: 'function',
},
api:
{
    requestFocus: { },
	getSelectedText: {
	    returns: 'string'
	},
	setSelection: {
	    parameters: [{start: 'int'}, {end: 'int'}]
	}
}

In this example it has a simple properties like toolTipText (a string) and background (a color) and a dataproviderID property that is of type 'dataprovider' which has an ondatachange that points to the handler onDataChangeMethodID function and the textfield wants to have a callback function called that is called 'onDataChangeCallback'


The field component template html will be expressed like:

<svy-textfield name="dataFieldFormatNumber" svy-model="model.dataFieldFormatNumber" svy-api="api.dataFieldFormatNumber" svy-handlers="handlers.dataFieldFormatNumber" svy-apply="handlers.dataFieldFormatNumber.svy_apply" svy-servoyApi="handlers.dataFieldFormatNumber.svy_servoyApi"/>

which is expanded by angular to real html. So every webcomponent gets a number of objects pushed to the instance:

  • model: This has all the properties set in the designer or at runtime (if they are data driven like a dataprovider property), it reflects the model object defined in the spec.
  • handlers: This has all the event functions that are set in the designer like "onclick" or "ondatachange"
  • api: On this object the webcomponents needs to add the api calls that the server can call. Like a requestFocus function.
  • apply: This is a special method that a webcomponent can use to let the server know a property (of the model) is changed. So the value can be applied also on the server (pushed into the record), If a component has fields that uses ng-model then the directive "svy-autoapply" can be used in the html template instead of coding it out in the webcomponent javascript file.
  • servoyApi: This is a component that holds some special servoy api functions to call, currently "setFormVisibility(form,visible,[relation]);

 

So the javascript file of the bean component can put those into its scope, every webcomponent is a angular module of its own, with at least 1 directive that is describing the component itself. So the module and the one directive should be named like the bean (camel case notation so an svy- prefix will result in svyName). The bean module should also include the servoy module so that the bean can use the various servoy directives (starting with svy)

 

angular.module('svyTextfield',['servoy']).directive('svyTextfield', function() {  
    return {
      restrict: 'E',
      transclude: true,
      scope: {
        model: "=svyModel",
        api: "=svyApi"
      },
      controller: function($scope, $element, $attrs) {
    	 // fill in the api defined in the spec file
    	 $scope.api.onDataChangeCallback = function(event, returnval) {
    		 if(!returnval) {
    			 $element[0].focus();
    		 }
    	 },
    	 $scope.api.requestFocus = function() { 
    		  $element[0].focus()
    	 },
    	 $scope.api.getSelectedText = function() { 
    		 var elem = $element[0];
    		 return elem.value.substr(elem.selectionStart, elem.selectionEnd - elem.selectionStart);
    	 }
    	 $scope.api.setSelection = function(start, end) { 
    		 var elem = $element[0];
    		 if (elem.createTextRange) {
    		      var selRange = elem.createTextRange();
    		      selRange.collapse(true);
    		      selRange.moveStart('character', start);
    		      selRange.moveEnd('character', end);
    		      selRange.select();
    		      elem.focus();
    		 } else if (elem.setSelectionRange) {
    		    	elem.focus();
    		    	elem.setSelectionRange(start, end);
    		 } else if (typeof elem.selectionStart != 'undefined') {
    		    	elem.selectionStart = start;
    		    	elem.selectionEnd = end;
    		    	elem.focus();
    		 } 
    	 }
      },
      templateUrl: 'servoydefault/datatextfield/datatextfield.html',
      replace: true
    };
  })

The model and api objects are stored in its own scope and then inside the controller function the various api calls are implemented besides the onDataChangeCallback function used as a callback of the ondatachange for a dataprovider.

The template then looks like this:

<input type="text" style="width:100%; height:100%; background-color:{{model.background}};"
   ng-model="model.dataProviderID" title="{{model.toolTipText}}"
    svy-autoapply  svy-format="model.format"/>

Where the various properties are then taken from the model for specific html tags.

A handler call to the server does get a promise back (http://docs.angularjs.org/api/ng.$q) where the webcomponent could register a callback on so that an event that executes on the server can return a value to the webcomponents call.

  • No labels