<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.infinite-erp.co.id/index.php?action=history&amp;feed=atom&amp;title=How_to_implement_a_new_main_view</id>
	<title>How to implement a new main view - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.infinite-erp.co.id/index.php?action=history&amp;feed=atom&amp;title=How_to_implement_a_new_main_view"/>
	<link rel="alternate" type="text/html" href="https://wiki.infinite-erp.co.id/index.php?title=How_to_implement_a_new_main_view&amp;action=history"/>
	<updated>2026-04-06T16:58:30Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.31.1</generator>
	<entry>
		<id>https://wiki.infinite-erp.co.id/index.php?title=How_to_implement_a_new_main_view&amp;diff=67&amp;oldid=prev</id>
		<title>Wikiadmin: Created page with &quot;== Introduction ==  This howto discusses how a new ''view'' can be added to Openbravo and made available through the menu and quick launch options.   A '''view''' is shown ins...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.infinite-erp.co.id/index.php?title=How_to_implement_a_new_main_view&amp;diff=67&amp;oldid=prev"/>
		<updated>2018-10-14T14:00:16Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;== Introduction ==  This howto discusses how a new &amp;#039;&amp;#039;view&amp;#039;&amp;#039; can be added to Openbravo and made available through the menu and quick launch options.   A &amp;#039;&amp;#039;&amp;#039;view&amp;#039;&amp;#039;&amp;#039; is shown ins...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This howto discusses how a new ''view'' can be added to Openbravo and made available through the menu and quick launch options. &lt;br /&gt;
&lt;br /&gt;
A '''view''' is shown inside the tab of the multi-document-interface (MDI) in the main content area of the Openbravo user interface. This is illustrated in the screenshot below which shows several opened tabs, what's shown inside a tab is called a view. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obuiapp_main_view_mdi2.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
This howto uses the following terminology:&lt;br /&gt;
* '''view''': is a generic term used to refer to the type of object which is opened through a user action (menu choice for example) and shown inside a tab.&lt;br /&gt;
* '''view type''' or '''view definition''': corresponds to the concept of a java class, a view type defines the functionality and properties of a view. A view instance is created by calling the create method on the view type. &lt;br /&gt;
* '''view instance''': corresponds to the concept of a java object instance, when a view is created a specific instance of that view is created with its own title or other dynamic information. The view instance is shown in a tab. So multiple tabs can show the same view type but they are different instances.&lt;br /&gt;
&lt;br /&gt;
=== Presentation - Blogs ===&lt;br /&gt;
&lt;br /&gt;
This blog contains a link to an interesting presentation/demo on how to develop a manual window:&lt;br /&gt;
&lt;br /&gt;
* [http://planet.openbravo.com/?p=48209 How to build a Manual Window in Openbravo 3 – Part I]&lt;br /&gt;
&lt;br /&gt;
== Example Module ==&lt;br /&gt;
&lt;br /&gt;
This howto is supported by an example module which shows example of the code shown and discussed in this howto. &lt;br /&gt;
&lt;br /&gt;
The code of the example module can be downloaded from this mercurial repository: https://code.openbravo.com/erp/mods/org.openbravo.client.application.examples/&lt;br /&gt;
&lt;br /&gt;
The example module is available through the Central Repository (See 'Client Application Examples'), for more information see the [[Projects/ExamplesClientApplication|Examples Client Application]] project page.&lt;br /&gt;
&lt;br /&gt;
{{(!)|The example module also contains implementations of other howtos.}}&lt;br /&gt;
&lt;br /&gt;
{{(!) | When implementing your own components it often makes sense to extend existing components. Make sure that your module then depends on the module providing the base types. This ensures that the javascript is loaded in the correct order. Most of the time it makes sense to add a dependency from your module to the Client 'User Interface Application/client.application' module}}&lt;br /&gt;
&lt;br /&gt;
== Main flow of view handling ==&lt;br /&gt;
&lt;br /&gt;
A view is opened when a user makes a menu choice or uses quick launch (or through hyperlinks). To obtain and render the view the system goes through a number of steps:&lt;br /&gt;
* the chosen menu option/quick launch defines the type of view which should be opened and a set of parameters. This is all defined in the Openbravo Menu table.&lt;br /&gt;
* using the view name Openbravo will check its internal cache of view definition (Smartclient types). &lt;br /&gt;
** If there is a javascript class with the view name then an instance of the view is created using the parameters which are passed in from the chosen menu option.&lt;br /&gt;
** if the view is not yet defined then a call to the server is done to check if the view definition can be generated on the basis of a view implementation record. If so it is called and the returned view definition is added to the cache and used in the next step.&lt;br /&gt;
* a new tab is created, the tab title is obtained from the view instance or from the selected menu.&lt;br /&gt;
* the view instance is added to the tab&lt;br /&gt;
* the tab is shown and selected&lt;br /&gt;
&lt;br /&gt;
The above flow also illustrates that view definitions can be provided in 2-ways to the system:&lt;br /&gt;
* as static javascript resources which are (pre-)loaded on the client when the complete layout is loaded&lt;br /&gt;
* as dynamic javascript resources which are generated on first use&lt;br /&gt;
&lt;br /&gt;
The first approach is the preferred approach, the easiest from an implementation point of view and will probably fit most usecases. The second approach is best if the view definition depends on dynamic information which is easier to incorporate on the server (using java) than on the client (using javascript).&lt;br /&gt;
&lt;br /&gt;
Both implementation methods are discussed below in more detail.&lt;br /&gt;
&lt;br /&gt;
== Preferred: Implementing your view in Static javascript ==&lt;br /&gt;
&lt;br /&gt;
Implementing a view as static javascript is the most straight forward method. Any Smartclient canvas is allowed to be used as a view. When implementing your view in a static javascript you should take the following steps:&lt;br /&gt;
* Create a javascript file with your view definition and place it in the correct directory&lt;br /&gt;
* Register the javascript file (and other static resources such as css files) in Openbravo using a [[Openbravo_3_Architecture#Component_Provider|ComponentProvider]]&lt;br /&gt;
* Create a view implementation record with the correct name (the same name as the javascript class)&lt;br /&gt;
* Define a menu entry for the view implementation&lt;br /&gt;
&lt;br /&gt;
Let's first start with a simple view and go through the steps to create it, define a view definition record and add it to the menu. Then an example of a more complex view is shown.&lt;br /&gt;
&lt;br /&gt;
=== Create a javascript file with the class definition of the view ===&lt;br /&gt;
&lt;br /&gt;
The implementation of the view needs to be done as an extension of Smartclient canvas class. The most common canvas to extend is the Layout. This is an example of a layout which contains one label as its content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;isc.defineClass(&amp;quot;OBEXAPP_HelloWorldLabelView&amp;quot;, isc.Layout).addProperties({&lt;br /&gt;
  &lt;br /&gt;
  labelContent: 'Label content should be passed in as a parameter',&lt;br /&gt;
  &lt;br /&gt;
  width: '100%',&lt;br /&gt;
  height: '100%',&lt;br /&gt;
  &lt;br /&gt;
  align: 'center',&lt;br /&gt;
  defaultLayoutAlign: 'center',&lt;br /&gt;
  &lt;br /&gt;
  initWidget: function() {&lt;br /&gt;
    &lt;br /&gt;
    this.children = [isc.Label.create({&lt;br /&gt;
      height: 1,&lt;br /&gt;
      width: 1,&lt;br /&gt;
      overflow: 'visible',&lt;br /&gt;
      align: &amp;quot;center&amp;quot;,&lt;br /&gt;
      valign: &amp;quot;center&amp;quot;,&lt;br /&gt;
      contents: this.labelContent})];&lt;br /&gt;
    &lt;br /&gt;
    this.Super(&amp;quot;initWidget&amp;quot;, arguments);&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
A short description of this source code:&lt;br /&gt;
* the isc.defineClass method is a Smartclient method call to create a new class, in this case our class (OBEXAPP_HelloWorldLabelView) extends the isc.Layout Smartclient component&lt;br /&gt;
* the call to addProperties adds specific properties to our class, such as width and height&lt;br /&gt;
* the labelContent is used later to illustrate the usage of menu parameters&lt;br /&gt;
* the align properties will force the content of this layout to be center aligned both in height as well as width&lt;br /&gt;
* the initWidget is like the constructor of the class, it is called when an instance is created. In this case a new child is added a label. The label gets as its text the labelContent of the OBEXAPP_HelloWorldLabelView&lt;br /&gt;
&lt;br /&gt;
{{(!) | &lt;br /&gt;
'''Note''': For screens that will appear in a standard UI tab, we recommend extending the [https://code.openbravo.com/erp/devel/pi/file/tip/modules/org.openbravo.client.application/web/org.openbravo.client.application/js/main/ob-base-view.js OBBaseView] javascript class instead of the Smartclient layout (isc.Layout):'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;isc.defineClass(&amp;quot;OBEXAPP_HelloWorldLabelView&amp;quot;, isc.OBBaseView).addProperties({....});&amp;lt;/source&amp;gt;&lt;br /&gt;
This will also take care of proper titles on screen refresh and back-button functionality (see ''Taking care of history/back button/page refresh'' section below).&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The javascript file should be placed inside the module in a directory: web/[modulejavapackage]/js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obuiapp_static_js_location.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next step is to register the js file in Openbravo. This is done through a ComponentProvider. For more detailed information on the ComponentProvider concept visit this [[Openbravo_3_Architecture#Component_Provider|page]]. &lt;br /&gt;
&lt;br /&gt;
The example module contains an example of a ComponentProvider, it has this method to tell Openbravo where to find the javascript file which contains the view implementation:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public List&amp;lt;String&amp;gt; getGlobalComponentResources() {&lt;br /&gt;
  final List&amp;lt;String&amp;gt; globalResources = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
  globalResources.add(createStaticResource(&lt;br /&gt;
      &amp;quot;web/org.openbravo.client.application.examples/js/example-view-component.js&amp;quot;, false));&lt;br /&gt;
  globalResources.add(createStaticResource(&lt;br /&gt;
      &amp;quot;web/org.openbravo.client.application.examples/js/example-grid-view.js&amp;quot;, false));&lt;br /&gt;
  globalResources.add(createStaticResource(&lt;br /&gt;
      &amp;quot;web/org.openbravo.client.application.examples/js/example-simple-view.js&amp;quot;, false));&lt;br /&gt;
  return globalResources;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create a view implementation record - handle role access - Add to the menu ===&lt;br /&gt;
&lt;br /&gt;
The js file is ready and registered, the next step is to make it accessible through the menu and quick launch. This requires three steps:&lt;br /&gt;
# create a view implementation record&lt;br /&gt;
# ensure that the correct roles have access&lt;br /&gt;
# create a menu entry and place the menu entry in the correct location&lt;br /&gt;
&lt;br /&gt;
==== Create a view implementation record ====&lt;br /&gt;
&lt;br /&gt;
Goto the View Implementation Window, either through the menu (Application Dictionary &amp;gt; User Interface &amp;gt; View Implementation) or through Quick Launch (type in View Implementation). Create a new record, the most important thing is that the '''Name''' of the record should be the same as the class name defined in the js file (in our case: OBEXAPP_HelloWorldLabelView).&lt;br /&gt;
&lt;br /&gt;
{{(!)|The field '''Name''' is the one who must match the class name defined in the .js file, the ''Classname of the view implementation'' is something different and can be left empty for this example}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
==== Role access ====&lt;br /&gt;
&lt;br /&gt;
Then go to the Role window (General Setup &amp;gt; Security &amp;gt; Role) and check the View Implementation child tab to check that the intended roles have access:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation_role.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menu Entry ====&lt;br /&gt;
&lt;br /&gt;
The view implementation should be accessible from the menu and quick launch. This is done by entering a menu record and placing the menu entry in the correct location in the tree. Adding to the menu will automatically make the view available through quick launch.&lt;br /&gt;
&lt;br /&gt;
Create a new menu record with the action set to 'Open View in MDI', the View Implementation combo will appear, there choose 'OBEXAPP_HelloWorldLabelView' (or the view name you have &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation_menu.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Try it! ===&lt;br /&gt;
&lt;br /&gt;
Log out and then log in again to refresh the menu (the menu is cached in the user session). Then access the new view through the menu or through quick launch. It should show something as illustrated below. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation_result1.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As you can see the text in the middle needs to be replaced. We can control this with so-called 'Menu Parameters'. This is explained in more detail in the next secion.&lt;br /&gt;
&lt;br /&gt;
=== Working with menu parameters ===&lt;br /&gt;
&lt;br /&gt;
Menu parameters allow you to re-use the same class definition in different menu entries but still ensure different behavior by passing them different parameters. In this example we will display a different text in the middle of the new simple view. Go to the menu window and select the menu entry added a few steps earlier. Then go to the child tab 'Menu Parameters' and add a record with name: labelContent and a value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation_menu_parameter.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now log out and log in again, open the view through the menu (don't use any of the 'recent' links as they store the parameters also), now the result should be something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_implementation_result2.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Taking care of history/back button/page refresh ===&lt;br /&gt;
&lt;br /&gt;
If you had implemented your view by extending the '''isc.Layout''' base class, try to refresh the complete page with the view opened. You will see that the view get's re-opened but that the tab title is wrong and that the content is wrong. When opening a view Openbravo will store information to be able to reconstruct the view when the page is refreshed or when the back/forward button in the browser is pressed. &lt;br /&gt;
&lt;br /&gt;
The give Openbravo the correct information the view needs to implement a function (getBookMarkParams):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;getBookMarkParams: function() {&lt;br /&gt;
  var result = {};&lt;br /&gt;
  result.viewId = this.getClassName();&lt;br /&gt;
  result.labelContent = this.labelContent;&lt;br /&gt;
  result.tabTitle = this.tabTitle;&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Some notes: the getBookMarkParams returns an object which is used to recreate the view instance when rebuilding the page, as you can see both the tab title and the labelContent are passed back (as well as the class name of the view).&lt;br /&gt;
&lt;br /&gt;
For a complete overview of the api which Openbravo checks for additional functionality (opening a help view, preventing multiple tabs of the same type), see the [[How_to_implement_a_new_main_view#View_API|View API]] section below.&lt;br /&gt;
&lt;br /&gt;
'''Note''': To avoid duplicating the above piece of code for your standard tabbed views, an '''OBBaseView''' wrapper class is available that includes all above actions. Hence, instead of extending the isc.Layout class, consider extending the OBBaseView, e.g.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;isc.defineClass(&amp;quot;OBEXAPP_HelloWorldLabelView&amp;quot;, isc.OBBaseView).addProperties({....});&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A more elaborate view: grid with button, parameterized messages ===&lt;br /&gt;
&lt;br /&gt;
In the previous step we created a simple view. This section shows a more complex example. It is part of the example module in the file example-grid-view.js. It shows a grid with a button which is enabled when selecting one or more records in the grid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_example_grid.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The source code of the test view is shown below. It start with the definition of the grid class and then defines the view itself (further down). The class name of the view itself is OBEXAPP_SimpleView. So to make use of this view, a view implementation record with this name (OBEXAPP_SimpleView) should be created and added to the menu. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;// This javascript defines a grid and a layout containing the grid &lt;br /&gt;
// together with a button to show the selected records&lt;br /&gt;
&lt;br /&gt;
// This testgrid shows data from the product table&lt;br /&gt;
isc.defineClass('OBEXAPP_TestGrid', isc.OBGrid);&lt;br /&gt;
&lt;br /&gt;
isc.OBEXAPP_TestGrid.addProperties({&lt;br /&gt;
  dataSource: null,&lt;br /&gt;
  showFilterEditor: true,&lt;br /&gt;
&lt;br /&gt;
  dataProperties: {&lt;br /&gt;
    useClientFiltering: false&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  gridFields: [{&lt;br /&gt;
    name: 'name',&lt;br /&gt;
    // allow filtering on name&lt;br /&gt;
    canFilter: true,&lt;br /&gt;
    // filter automatically when the user types&lt;br /&gt;
    filterOnKeypress: true&lt;br /&gt;
  },&lt;br /&gt;
  &lt;br /&gt;
  {&lt;br /&gt;
    // description is a property of the product&lt;br /&gt;
    name: 'description',&lt;br /&gt;
    // allow filtering on description&lt;br /&gt;
    canFilter: true,&lt;br /&gt;
    // filter automatically when the user types&lt;br /&gt;
    filterOnKeypress: true&lt;br /&gt;
  }],&lt;br /&gt;
&lt;br /&gt;
  setDataSource: function(ds) {&lt;br /&gt;
    // is called when the datasource is retrieved from the server&lt;br /&gt;
    this.Super('setDataSource', [ds, this.gridFields]);&lt;br /&gt;
    this.refreshFields();&lt;br /&gt;
    this.sort('name');&lt;br /&gt;
    this.fetchData();&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  initWidget: function() {&lt;br /&gt;
    // get the datasource, if it is not yet defined&lt;br /&gt;
    // it is retrieved from the server and returned&lt;br /&gt;
    // Datasources refer to tables using the entity name&lt;br /&gt;
    OB.Datasource.get('Product', this, null, true);&lt;br /&gt;
    this.Super('initWidget', arguments);&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
isc.defineClass('OBEXAPP_SimpleView', isc.VLayout);&lt;br /&gt;
&lt;br /&gt;
isc.OBEXAPP_SimpleView.addProperties({&lt;br /&gt;
  // do some margins between the members&lt;br /&gt;
  membersMargin: 10,&lt;br /&gt;
  defaultLayoutAlign: 'center',&lt;br /&gt;
  &lt;br /&gt;
  initWidget: function() {&lt;br /&gt;
    // create a button which is enabled &lt;br /&gt;
    var grid, btn;&lt;br /&gt;
    &lt;br /&gt;
    // create an instance of the grid&lt;br /&gt;
    grid = isc.OBEXAPP_TestGrid.create({&lt;br /&gt;
      // add logic to enable/disable the button when&lt;br /&gt;
      // the selected records changes&lt;br /&gt;
      selectionUpdated: function(record, recordList) {&lt;br /&gt;
        if (recordList &amp;amp;&amp;amp; recordList.length &amp;gt; 0) {&lt;br /&gt;
          btn.setDisabled(false);&lt;br /&gt;
        } else {&lt;br /&gt;
          btn.setDisabled(true);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
    &lt;br /&gt;
    // and create a button which refers to the grid&lt;br /&gt;
    btn = isc.OBFormButton.create({&lt;br /&gt;
        title: OB.I18N.getLabel('OBEXAPP_ClickMe'),&lt;br /&gt;
        // let it be enabled when more than one &lt;br /&gt;
        disabled: true,&lt;br /&gt;
        action: function() {&lt;br /&gt;
          // show the number of selected records&lt;br /&gt;
          // illustrates the usage of a parameterized message&lt;br /&gt;
          isc.say(OB.I18N.getLabel('OBEXAPP_SelectedRecordsMsg', &lt;br /&gt;
              [grid.getSelectedRecords().length]));&lt;br /&gt;
        } &lt;br /&gt;
      });&lt;br /&gt;
    &lt;br /&gt;
    // add the grid&lt;br /&gt;
    this.addMember(btn);&lt;br /&gt;
    this.addMember(grid);&lt;br /&gt;
    this.Super('initWidget', arguments);&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  // the following three methods are related to the view handling&lt;br /&gt;
  // api of Openbravo&lt;br /&gt;
  isSameTab: function(viewId, params){&lt;br /&gt;
    // return false, allows this view to be opened many times&lt;br /&gt;
    return false;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  // just return the classname and nothing else to be bookmarked&lt;br /&gt;
  getBookMarkParams: function() {&lt;br /&gt;
    var result = {};&lt;br /&gt;
    result.viewId = this.getClassName();&lt;br /&gt;
    return result;&lt;br /&gt;
  },&lt;br /&gt;
  &lt;br /&gt;
  // this view does not have a help view&lt;br /&gt;
  getHelpView: function(){&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the button click shows a parameterized message. A message can contain parameters (%0, %1) which are substituted with real values when showing the message.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_view_example_parameterized_message.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== View API ==&lt;br /&gt;
&lt;br /&gt;
Any Smartclient canvas can be used as an Openbravo view. When opening a view Openbravo will check if the view class/instance supports certain methods. If so then Openbravo will make use of these methods. The methods are reflected in the OBBaseView type (available from Openbravo 3.0MP3):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;// = OBBaseView =&lt;br /&gt;
//&lt;br /&gt;
// A class which implements the view api.&lt;br /&gt;
isc.ClassFactory.defineClass('OBBaseView', isc.Layout);&lt;br /&gt;
&lt;br /&gt;
isc.OBBaseView.addProperties({&lt;br /&gt;
  &lt;br /&gt;
  // ** {{{ OBBaseView.showsItself }}} **&lt;br /&gt;
  // If this boolean property is set to true then the Openbravo view manager&lt;br /&gt;
  // will not place the view instance in a tab in the main Multi-Document-Interface.&lt;br /&gt;
  // Instead it will call the show method on the instance. This makes &lt;br /&gt;
  // it for example possible to define views which are implemented as &lt;br /&gt;
  // popups instead of opened in the main MDI.&lt;br /&gt;
  showsItself: false,&lt;br /&gt;
  &lt;br /&gt;
  // ** {{{ OBBaseView.isSameTab() }}} **&lt;br /&gt;
  // Is called by the view manager when opening a view. The view manager&lt;br /&gt;
  // will first check if there is already a tab open by calling the &lt;br /&gt;
  // isSameTab method on each opened view. If one of the views returns&lt;br /&gt;
  // true then the requested view is opened in that tab (effectively&lt;br /&gt;
  // replacing the current open view there). This is needed for cases&lt;br /&gt;
  // when a certain view may only be opened once.&lt;br /&gt;
  isSameTab: function(viewId, params){&lt;br /&gt;
    // a common implementation does this, this allows only &lt;br /&gt;
    // one instance of certain view class to be open at one point &lt;br /&gt;
    // in time.&lt;br /&gt;
    // return viewId === this.getClassName();&lt;br /&gt;
    // this will allow multiple tabs to be opened:&lt;br /&gt;
    return false;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  // ** {{{ OBBaseView.getBookMarkParams() }}} **&lt;br /&gt;
  // Is used to create a bookmarkable url in the browser's address bar.&lt;br /&gt;
  // For each opened view this method is called and the result is added&lt;br /&gt;
  // to the address bar. This makes it possible for the user to do &lt;br /&gt;
  // back in the browser, to bookmark the url and to build history in the &lt;br /&gt;
  // browser itself. &lt;br /&gt;
  getBookMarkParams: function() {&lt;br /&gt;
    var result = {};&lt;br /&gt;
    result.viewId = this.getClassName();&lt;br /&gt;
    return result;&lt;br /&gt;
  },&lt;br /&gt;
  &lt;br /&gt;
  // ** {{{ OBBaseView.getHelpView() }}} **&lt;br /&gt;
  // This method can return an object containing a view definition. &lt;br /&gt;
  // If this method returns an object then a link is activated in the &lt;br /&gt;
  // help pull-down in the top.&lt;br /&gt;
  getHelpView: function(){&lt;br /&gt;
    return;&lt;br /&gt;
    // an example of returning a view definition, the viewId contains&lt;br /&gt;
    // the help view classname, the tabTitle denotes the tab title of the&lt;br /&gt;
    // help view&lt;br /&gt;
//    return {&lt;br /&gt;
//        viewId: 'ClassicOBHelp',&lt;br /&gt;
//        tabTitle: this.tabTitle + ' - ' + OB.I18N.getLabel('UINAVBA_Help'),&lt;br /&gt;
//        windowId: this.windowId,&lt;br /&gt;
//        windowType: 'W',&lt;br /&gt;
//        windowName: this.tabTitle&lt;br /&gt;
//    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
});&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Dynamically generated javascript ==&lt;br /&gt;
&lt;br /&gt;
Instead of creating a static javascript file with a view definition it is also possible to use dynamically generated javascript. The javascript is generated at the first usage of the view definition by the user. &lt;br /&gt;
&lt;br /&gt;
To create a dynamically generated view the following parts need to be implemented:&lt;br /&gt;
* create a java class (the component) which provides the template with information (from the database for example)&lt;br /&gt;
* create a template which generates the javascript which creates the view definition on the client&lt;br /&gt;
&lt;br /&gt;
Each of these steps is described in more detail below.&lt;br /&gt;
&lt;br /&gt;
The example module contains a Hello World component with a template (hello_world_view.js.ftl). This example creates a view with one clickable button.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:example_template_component_structure2.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Creating a component ===&lt;br /&gt;
&lt;br /&gt;
A component is useful when you want to add runtime information to the view definition javascript when it gets generated. &lt;br /&gt;
&lt;br /&gt;
''If you don't have the requirement to use dynamic information in the generated javascript of your component then you don't need to implement a component (only a template). You can leave the class field empty in the view implementation record (see below).''&lt;br /&gt;
&lt;br /&gt;
The example module has a hello world component which provides the current logged in user to the template. The component can be found in the module's src directory. Here is the code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;package org.openbravo.client.application.examples;&lt;br /&gt;
&lt;br /&gt;
import org.openbravo.client.kernel.BaseTemplateComponent;&lt;br /&gt;
import org.openbravo.dal.core.OBContext;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Provides a widget which shows a hello world message when clicked.&lt;br /&gt;
 * &lt;br /&gt;
 * @author mtaal&lt;br /&gt;
 */&lt;br /&gt;
public class HelloWorldComponent extends BaseTemplateComponent {&lt;br /&gt;
&lt;br /&gt;
  public String getName() {&lt;br /&gt;
    return OBContext.getOBContext().getUser().getName();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Note: for simplicity we choose to get the user name on the server, this information is also available on the client in the global OB.User object (which contains information about the currently logged in user.&lt;br /&gt;
&lt;br /&gt;
=== Creating a template ===&lt;br /&gt;
&lt;br /&gt;
The template contains the actual javascript. A template consists of two parts:&lt;br /&gt;
# a template file (the template source) ending on ftl (a [http://freemarker.sourceforge.net/docs/ freemarker] extension) which is located in the source tree (in the classpath).&lt;br /&gt;
# a record in the template table&lt;br /&gt;
&lt;br /&gt;
=== The template source ===&lt;br /&gt;
To create the template for your view, create a ftl file in the source tree of your module. The ftl file should contain plain javascript with possible freemarker constructs to read information from the component.&lt;br /&gt;
&lt;br /&gt;
As an example, the hello world template can be found in the org.openbravo.client.application.examples.templates package, it creates a button which can be clicked. The content of the template is this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;/* jslint */&lt;br /&gt;
isc.defineClass(&amp;quot;OBAPPEX_HelloWorldButtonView&amp;quot;, &amp;quot;Button&amp;quot;).addProperties({&lt;br /&gt;
  title: &amp;quot;Click me&amp;quot;,&lt;br /&gt;
  overflow: &amp;quot;visible&amp;quot;,&lt;br /&gt;
  width: 100,&lt;br /&gt;
  layoutAlign: &amp;quot;center&amp;quot;,&lt;br /&gt;
  showRollOver: false,&lt;br /&gt;
  showFocused: false,&lt;br /&gt;
  showDown: false,&lt;br /&gt;
  click: function() {&lt;br /&gt;
    isc.say(OB.I18N.getLabel('OBAPPEX_SayHello', ['${data.name}']));&lt;br /&gt;
  }&lt;br /&gt;
});&amp;lt;/source&amp;gt;&lt;br /&gt;
Some special things in this template source:&lt;br /&gt;
* The ''/* jslint */'' tells Openbravo to do a check on the generated javascript. Errors are printed in the console or output log.&lt;br /&gt;
* as you can see, the templates extends the Smartclient Button class.&lt;br /&gt;
* the title of the message dialog button is retrieved through the OB.I18N.getLabel method. This is to support translation, see [[Projects/ClientKernel/Developers_Manual#Internationalization.2C_translation_of_labels|this wiki page]] for more information on this.&lt;br /&gt;
* See the ''${data.name}'' part, this is a [http://freemarker.sourceforge.net/docs/ freemarker] template construct whereby information is retrieved from a java object. In the Openbravo templating system the component instance is available as the ''data'' object. The ''${data.name}'' will call the accessor getName on the HelloWorldComponent.&lt;br /&gt;
&lt;br /&gt;
=== Template Record ===&lt;br /&gt;
&lt;br /&gt;
The next step is to let Openbravo know that the template exists. This is done by registering the template in Openbravo in the Template table. The template maintenance function can be found here: Application Dictionary &amp;gt; User Interface &amp;gt; Template.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obuiapp_view_template_record.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Some specifics:&lt;br /&gt;
* the component type of the template has to be set to 'UI View Implementation'&lt;br /&gt;
* the template class path location contains the path to the template file (incl. the file itself) in the source&lt;br /&gt;
&lt;br /&gt;
For a description of the other fields of the template record, see [[Projects/ClientKernel/Developers_Manual#Implement_a_Template | here]].&lt;br /&gt;
&lt;br /&gt;
=== Register the View Implementation ===&lt;br /&gt;
&lt;br /&gt;
To use the view implementation in a menu need to register it in Openbravo. This is done through the Application Dictionary &amp;gt; User Interface &amp;gt; View Implementation window. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obuiapp_register_view_implementation.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The client, organization, module, description and active fields are standard Openbravo fields. The other fields have more specific meanings:&lt;br /&gt;
* name: the name must be unique (it must start with the dbprefix of the module). The name is used to create the view type name on the client.&lt;br /&gt;
* classname: the classname of a class which extends the org.openbravo.client.kernel.BaseTemplateComponent class. If you don't have any specific component you can leave this field empty.&lt;br /&gt;
* template: a template set for the UI View Component type. &lt;br /&gt;
&lt;br /&gt;
''If the view is implemented as static javascript then the classname and template can remain empty. The name should correspond to the class name used when calling defineClass (for the example above the name is: OBAPPEX_HelloWorldLabelView).''&lt;br /&gt;
&lt;br /&gt;
The view implementation can be enabled or disabled by role through the View Role Access tab. When a new view is created it is automatically enabled for all roles.&lt;br /&gt;
&lt;br /&gt;
=== Use the view implementation in a menu entry ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add the view as a menu item. This is done through General Setup &amp;gt; Application &amp;gt; Menu.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obuiapp_register_view_menu.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When creating or updating a menu select as an action the ''Open View in MDI'' action and select a view implementation in the field which is shown.&lt;br /&gt;
&lt;br /&gt;
=== Try it! ===&lt;br /&gt;
&lt;br /&gt;
Now log out and log in again and find the menu entry in the menu, select it. You should see something like the below:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:obexapp_dynamic_javascript_result.png|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:HowTo]]&lt;/div&gt;</summary>
		<author><name>Wikiadmin</name></author>
		
	</entry>
</feed>