Projects:HTML Widget/How To Develop new widget class
Contents |
Objective
The objective of this article is to describe how to build a new widget type. Currently core's distribution allows users to create widgets using 3 types of widgets:
- URL Widget: Shows the content of the specified URL.
- HTML Widget: Embeds the specified HTML code in the widget.
- Query/List Widget: Shows in a grid the result of the specified HQL.
This article is based on the HTML Widget superclass. In this following example the needed steps to develop a new widget type are described:
- Create a Widget class in JavaScript using SmartClient API extending the OBWidget class.
- Define a ComponentProvider for the module to load by the kernel the necessary JavaScript files.
- Define a Provider extending the WidgetProvider class and implement required methods.
- Register a new widget class in the Application Dictionary.
Additionally some other possible advanced features are described on the last section of the document.
![]() | Note: This article assumes that you have a basic knowledge of SmartClient |
Module
All new widget classes must belong to a module that is not in the core distribution. Please follow the How to create and package a module section to create a new module.
HTML Widget example
Defining the widget
The widget is defined in a JavaScript file within the module, in this case ob-html-widget.js. In this file a new SmartClient class extending Openbravo's OBWidget has to be defined.
isc.defineClass('OBHTMLWidget', isc.OBWidget);
This class has to implement several functions. The most important one is the createWindowContents. This function has to return an SmartClient's Canvas object, or any other class extending a Canvas like a Layout, with the desired content. On the HTML Widget example is returned an HTMLFlow. But it can also be a VStack object containing a grid as in the Query/List widget.
The example creates an HTMLFlow, and also performs the following actions:
- It also evaluates the htmlcode to replace all occurrences of ${parametername} by its value where parametername is the DB Column Name of a widget's parameter.
- It sets the Title of the widget in case the corresponding parameter has some value.
createWindowContents: function(){ if (!this.contentSource && this.parameters.htmlcode) { this.contentSource = this.evaluateContents(this.parameters.htmlcode); } if (this.parameters.widgetTitle) { this.setTitle(this.parameters.widgetTitle); } return isc.HTMLFlow.create({ contents: this.contentSource, height: '100%', width: '100%' }); },
Another important function that often makes sense to override is the refresh function. This function is called when the user clicks the refresh option in the widget menu and after the user saves new values for widget parameters. On this example the refresh function takes care of 3 possible changes that might have been done to the widget:
- Sets the height of the widget. The height of a widget is defined in the Widget class by default. But the HTML Widget allows to override the height defined in the class using the widgetHeight parameter. The refresh function calls the setWidgetHeight() function that calculates the new height based on the value of the parameter.
- Sets the title of the widget in case the user has edited the value of the widgetTitle parameter.
- Sets the content in case the user has edited the value of the htmlcode parameter.
refresh: function(){ this.setWidgetHeight(); if (this.parameters.widgetTitle) { this.setTitle(this.parameters.widgetTitle); } if (this.parameters.htmlcode) { this.contentSource = this.evaluateContents(this.parameters.htmlcode); } this.windowContents.contents = this.contentSource; },
On the example there is another overridden function, the initWidget. Notice that when this function is overridden it is mandatory to call the super method. As said before this widget type has a widgetHeight parameter to set the height of the widget. The default initWidget takes the height defined in the widget class to set the height so it is needed to override it to set the height using the parameter after the default initWidget is run.
initWidget: function(){ this.Super('initWidget', arguments); this.setWidgetHeight(); },
Other functions that can be overridden like maximize or setDbInstanceId() are described on the Advanced Features section.
The widget component provider
Any module that includes new JavaScript files has to include a Component Provider class. This class extends kernel's BaseComponentProvider. See the Developers Manual of the Client Kernel module for more information. In this case it is only needed to add the ob-html-widget.js file as a global resource.
The widget provider
The last class needed to code is the Widget Provider, HTMLWidgetProvider. We could create our own Provider but extending the MyOpenbravo's WidgetProvider class makes our job easier.
The generate() method has to be always overridden. This method defines a new SmartClient class that extends the class defined in the JavaScript file for each widget class implementing it. This is useful to set values to some properties that depend on the widget class. The name of this subclass is the UUID identifier of the widget class with the "_" prefix. In the example this is not needed as all widget classes can use the same OBHTMLWidget JavaScript class, so we just throw an UnsupportedOperationException.
@Override public String generate() { throw new UnsupportedOperationException( "HTMLWidget definition should be pre-loaded on the client"); }
As we are not creating a subclass for each Widget Class it is also needed to override the getClientSideWidgetClassName() that returns the name of the SmartClient class that implements the widget OBHTMLWidget.
Application Dictionary artifacts
Once the sources are compiled and deployed in the server we are ready to add the widgets in the workspace.
The first step is to create a new widget class. In the example there are 2 widget classes, HTML Widget and User defined HTML Widget.
The HTML Widget is a SuperClass widget. A SuperClass is intended to be used by other developers creating their own widgets classes. It sets some properties like height and can maximize and can deliver some parameters. In the example is has disabled the can maximize flag and a default height of 300. Remember that this height is overwritten by a user defined height using a parameter. The HTML Widget includes 3 parameters that are copied to all the widget classes implementing it.
- widgetTitle
- Widget title. A String parameter to set the title of the widget. If it is left blank the widget takes the name of the widget class as a default value.
- widgetHeight
- Widget height. An Integer parameter to set the height of the html code. Notice that this height doesn't include the height of the widget header and margins as these are added later when the height is set on the ob-html-widget.js file.
- htmlcode
- HTML Code. This is a mandatory text parameter where is set the HTML code that is rendered on the widget.
These parameters are Fixed which means that a widget class implementing this superclass has to set the proper fixed values for them. In case that they are kept fixed it is recommended to leave as blank the Widget Title to use the widget class name that can be translated to other languages.
The second widget included, User defined HTML Widget, implements the HTML Widget SuperClass. This widget has the same parameters of the superclass with the fixed flag disabled. This means that users adding this widget to their Workspace will be prompted to set the proper values to them. This widget does not have the Enable for all Users flag enabled so user's Role must have access to this widget class to be able to use it. Giving access to this widget to the users we allow them to add their own instances using their own HTML Code.
Advanced features
Overwrite maximize function
During the development new widgets it might be needed to overwrite some predefined method to change their behavior. For example, in the Query/List widget we want the maximize button to open the widget on a new tab. To do so it is necessary the maximize function. As we want to open a new tab this function just contains a call to the openView() function. The first argument of this function is the name of the class that will render the content. In this case OBQueryListView. This class is defined on the ob-querylist-view.js file. It implements a PortalLayout that contains an instance of the widget in the maximized view mode. The second argument are the properties required by OBQueryListView to build the tab and the widget.
maximize: function() { OB.Layout.ViewManager.openView('OBQueryListView', { tabTitle: this.title, widgetInstanceId: this.dbInstanceId, widgetId: this.widgetId, fields: this.maximizedFields, gridDataSource: this.gridDataSource, parameters: this.parameters, menuItems: this.menuItems, fieldDefinitions: this.fieldDefinitions }); }
The toolbox menu of a widget has some options by default: Edit Settings, Refresh and Delete this widget. On some widgets it is desired to add new elements to it. This is achieved using the Widget Menu Items tab in the Widget window. Notice that if the widget class is implementing a SuperClass the widget will only include items that might be defined in the SuperClass.
In the Query/List widgets it is added an Export as CSV option. To have a separator with the last default option it is also included a second item with the Is Separator flag enabled. The definition of the item includes the Name and the Action. The Action must match a function defined on the widget. In this case it's exportGrid is defined in the OBQueryListWidget class and contains a call to the grid's exportData function to export and generate a CSV file with the data.
exportGrid: function() { var grid = this.widget.grid; var requestProperties = { exportAs: 'csv', //exportFilename: 'Query/List_widget.csv', exportDisplay: 'download', params: { exportToFile: true } }; var additionalProperties = { widgetInstanceId: this.widget.dbInstanceId }; grid.exportData(requestProperties, additionalProperties); }