Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "RAP/Custom Widgets FAQ"

< RAP
(How do I port my RAP 1.x Custom Widget to RAP 2.x?)
m (invlolve to involve)
 
(23 intermediate revisions by one other user not shown)
Line 18: Line 18:
 
=== Where can I find a Tutorial? ===
 
=== Where can I find a Tutorial? ===
  
There is no tutorial yet. For now there are the following resources:
+
There is currently no tutorial. For now there are the following resources:
  
 
* This FAQ
 
* This FAQ
Line 24: Line 24:
 
* The Reference for [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/package-summary.html Java Remote API]
 
* The Reference for [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/package-summary.html Java Remote API]
 
* The Reference for [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html WebClient API].
 
* The Reference for [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html WebClient API].
* The CKEditor [https://github.com/eclipsesource/rap-ckeditor example]
+
* The CKEditor [https://github.com/eclipsesource/rap-ckeditor example]. All RAP-specific JavaScript is contained in [https://github.com/eclipsesource/rap-ckeditor/blob/master/com.eclipsesource.widgets.ckeditor/src/resources/handler.js handler.js]. To understand ckeditor.js, visit the CKEditor 3.x [http://docs.cksource.com/CKEditor_3.x/Developers_Guide devlopers guide] and [http://docs.cksource.com/ckeditor_api/ API reference].
 
* This blog post [http://eclipsesource.com/blogs/2013/04/02/rap-and-jasmine/ blog post] on using Jasmine and custom widgets.
 
* This blog post [http://eclipsesource.com/blogs/2013/04/02/rap-and-jasmine/ blog post] on using Jasmine and custom widgets.
  
 
=== How is this related to ClientScript/RWT Scripting? ===
 
=== How is this related to ClientScript/RWT Scripting? ===
  
Short answer: This approach does '''not''' involve ClientListener, and RWT Scripting does '''not''' invlolve HTML or RemoteObjects.
+
Short answer: This approach does '''not''' involve ClientListener, and RWT Scripting does '''not''' involve HTML or RemoteObjects.
  
 
RWT Scripting (formerly known as ClientScripting) is the practice of attaching a piece of JavaScript ("ClientListener") to an '''existing''' widget and react to events that it fires. The "widget" object used in RWT Scripting (which can be obtained from the event object or from rap.getObject) are '''not''' the internal client widget, but a wrapper with SWT-like API that allows savely interacting with the widget on the client. These wrapper can not be destroyed or created on the client.
 
RWT Scripting (formerly known as ClientScripting) is the practice of attaching a piece of JavaScript ("ClientListener") to an '''existing''' widget and react to events that it fires. The "widget" object used in RWT Scripting (which can be obtained from the event object or from rap.getObject) are '''not''' the internal client widget, but a wrapper with SWT-like API that allows savely interacting with the widget on the client. These wrapper can not be destroyed or created on the client.
Line 46: Line 46:
 
=== What is the basic architecture of a Custom Widget? ===
 
=== What is the basic architecture of a Custom Widget? ===
  
* The [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/service/ResourceManager.html ResourceManager] (or the [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/application/ApplicationConfiguration.html ApplicationConfiguration]) are used to register all resources required on the client. (Could also be served by another servlet/server.)
+
As a custom widget developer you have to provide three components:
* The [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/client/service/JavaScriptLoader.html JavaScriptLoader] is used to load the required scripts on the Client.
+
* A Java class that extends Composite and creates a RemoteObject with an OperationHandler.
* We need a new or existing HTML/JavaScript based widget/component/application that can be attached anywhere in the DOM.
+
* A client "typeHandler", which is an JavaScript object that creates and configures the remote JavaScript object (i.e. the widget).
* A [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html#.registerTypeHandler typeHandler] registers a new RAP protocol type and creates an instance of the above JavaScript component
+
* The JavaScript widget itself (which is instantiated in the typeHandler).
* A java class (the actual custom widget used in an application) extends [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/swt/widgets/Composite.html Composite] and creates a [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/RemoteObject.html RemoteObject] of the type that was registered by the typeHandler.
+
 
* This java widget needs to set it's own id as a parent on the RemoteObject (e.g. <code>remoteObject.set( "parent", WidgetUtil.getId( this ) );</code>)
+
[[Image:CustomWidgetUML.png|800px]]
* The client widget needs to attach itself to the composite, for example in the factory like this:
+
 
 +
To clarify: While the custom widget *extends* Composite on the server, on the client the Composite is the *parent*.
 +
 
 +
Step by step:
 +
 
 +
* Create a java class (the actual custom widget used in an application) that extends [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/swt/widgets/Composite.html Composite].
 +
 
 +
* Use the [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/service/ResourceManager.html ResourceManager] in the constructor register your javascript files and other resources, but be careful to only do it if they haven't already be registered for this RAP application (e.g. use "isRegistered"). Alternatively use the [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/application/ApplicationConfiguration.html ApplicationConfiguration] to register the resources, or use another servlet/server.
 +
 
 +
* In the constructor, use the [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/client/service/JavaScriptLoader.html JavaScriptLoader] to load the required scripts on the Client.
 +
 
 +
* In the constructor, create a [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/RemoteObject.html RemoteObject] using <code>RWT.getUISession().getConnection().createRemoteObject( "my.CustomWidgetType" );</code>. The type can be any string and will later be used to register the typeHandler on the client.
 +
 
 +
* Still in the constructor, set the widgets own id as a parent on the RemoteObject (e.g. <code>remoteObject.set( "parent", WidgetUtil.getId( this ) );</code>)
 +
 
 +
* In one of your JavaScript files a [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html#.registerTypeHandler typeHandler] must be registered. A minimal implementation would look like this: <code>
 +
  rap.registerTypeHandler( "my.CustomWidgetType", {
 +
    factory : function( properties ) {
 +
      return new MyCustomWidget( properties ); // or any other way to create a JavaScript object that represents the widget
 +
    }
 +
  } );</code>
 +
 
 +
* MyCustomWidget in that example would need to be a constructor for an HTML/JavaScript based widget/component/application that can be attached anywhere in the DOM. It's API should mainly consist of setter that can be called by the server (using RemoteObject#set). These also need to be listed in the [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html#.registerTypeHandler typeHandlers] property list.
 +
 
 +
* The client widget needs to attach it's HTML to the composite (for example in the factory or constructor) like this:
 
     <code>var parent = rap.getObject( properties.parent );
 
     <code>var parent = rap.getObject( properties.parent );
 
     var element = document.createElement( "div" );
 
     var element = document.createElement( "div" );
 
     parent.append( element );</code>
 
     parent.append( element );</code>
 +
 +
* Now the widgets HTML should be visible in the application.
 +
 
* To send changes to the server, the client widget can use it's own RemoteObject: <code>rap.getRemoteObject( clientWidget ).set( "prop", value );</code>
 
* To send changes to the server, the client widget can use it's own RemoteObject: <code>rap.getRemoteObject( clientWidget ).set( "prop", value );</code>
 +
 
* The client widget can listen to resize events on the parent composite to layout itself.
 
* The client widget can listen to resize events on the parent composite to layout itself.
 +
 
* When destroyed, the client widget must remove itself from the parent composite and de-register all listeners from the composite or "rap" object.
 
* When destroyed, the client widget must remove itself from the parent composite and de-register all listeners from the composite or "rap" object.
  
== Java ==
+
=== What about Single-Sourcing? ===
== JavaScript ==
+
Custom Widgets based on the WebClient API can not be Single-Sourced with SWT. There is no other way than creating another Custom Widget for SWT with the same API (just like RAP has the same API as SWT). Single-Sourcing with other client platforms is possible for the server part of the custom widget. The client part has to be implemented in the clients native language.
 +
 
 +
Note that are kinds of custom widgets are single-sourcing capeable, as mentioned in the developers guide.
 +
 
 +
== Java API ==
 +
 
 +
=== How exactly do I use the "listen" method? ===
 +
The RemoteObjects [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/RemoteObject.html#listen%28java.lang.String,%20boolean%29 listen] method "Instructs the remote object to listen or to stop listening on the given type of events.". In the context of the WebClient, this means that the client RemoteObjects [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/RemoteObject.html#notify notify] method does only send "notify" operations if the server has previously called "listen( "MyEventType", true )".
 +
 
 +
The idea is that not all user-interactions with the widget need to trigger HTTP-requests. Only if the application has registered a listener for that specific action is that necessary.
 +
 
 +
A full example:
 +
 
 +
We assume your server widget supports listener registration for the type "Selection", and furthermore that you build on top of the SWT event system (which is entirely optional). The registration methods could look like this:
 +
 
 +
<code>
 +
    @Override
 +
    public void addListener( int eventType, Listener listener ) {
 +
      boolean wasListening = isListening( eventType );
 +
      super.addListener( eventType, listener );
 +
      boolean isListening = isListening( eventType );
 +
      String remoteType = eventTypeToString( eventType );
 +
      if( remoteType != null && !wasListening && isListening ) {
 +
        remoteObject.listen( remoteType, true );
 +
      }
 +
    }
 +
 
 +
    @Override
 +
    public void removeListener( int eventType, Listener listener ) {
 +
      boolean wasListening = isListening( eventType );
 +
      super.removeListener( eventType, listener );
 +
      boolean isListening = isListening( eventType );
 +
      String remoteType = eventTypeToString( eventType );
 +
      if( remoteType != null && wasListening && !isListening ) {
 +
        remoteObject.listen( remoteType, false );
 +
      }
 +
    }</code>
 +
 
 +
The "eventTypeToString" method should be implemented to return "Selection" for SWT.Selection. Now, assuming you have "Selection" in your [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/jsdoc/symbols/rap.html#.registerTypeHandler typeHandlers] "events" list, the client will only send requests on calls of <code>rap.getRemoteObject( this ).notify( "Selection" )</code> if there are selection listeners attached, in which case your servers RemoteObject will call it's [http://download.eclipse.org/rt/rap/doc/2.1/guide/reference/api/org/eclipse/rap/rwt/remote/OperationHandler.html#handleNotify%28java.lang.String,%20org.eclipse.rap.json.JsonObject%29 OperationHandler].
 +
 
 +
This is only necessary for "notify" operations. "set" operations will always be sent, but not immediately. If you want to notify the server immediately regardless of any attched listener, simply use "call".
 +
 
 +
== Client API ==
 +
 
 +
=== I'm not an JavaScript expert. Any tips? ===
 +
JavaScript isn't that hard if you do it right. Here are some recommendations:
 +
* Use JavaScript frameworks that make your code shorter, like [http://underscorejs.org Underscore.js] or [http://jquery.com jQuery].
 +
* Use [http://github.eclipsesource.com/jshint-eclipse/ JSHint] to prevent common JavaScript mistakes.
 +
* Use [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode strict mode] whenever possible.
 +
* Read this [http://wiki.eclipse.org/RAP/ClientScripting#JavaScript_Hints_for_Java_Developer list] of similarities and differences of Java and JavaScript.
 +
* Write [http://eclipsesource.com/blogs/2013/04/02/rap-and-jasmine/ tests]. For JavaScript, if you have more than 50 lines of code it will almost definetly save you time in the long run!
 +
* For OO-programming, either learn how prototypes and closures work in JavaScript, or use a framwork that provides a pseuso-class system (like [http://prototypejs.org/learn/class-inheritance prototype] or [http://backbonejs.org backbone]). Or use a [http://blog.thepete.net/blog/2012/02/06/class-less-javascript/ factory pattern], if you can live with slightly more memory consumption. Also, here's a [http://eclipsesource.com/blogs/2013/07/05/private-members-in-javascript/ blog post] on private members in javascript.
 +
 
 +
=== Isn't RAP based on qooxdoo? Can/Should I use that? ===
 +
Yes and No, Yes, No.
 +
 
 +
The RAP WebClient is basically a fork of qooxdoo 0.7.4. However, over the years the client has been, reduced, optimzed, re-written, enhanced and re-organized so that the qooxdoo reference for that version would not help you much. These frequent changes are the reason that RAP 2.x introduced a new, stable, qooxdoo independent, public API. RAP may now modify or completely replace it's underlying library without breaking Custom Widgets.
 +
 
 +
Of course it is still technically possible to built on the qooxdoo part of the WebClient, but you will find no documentation how to do that and it is entirely on your own risk.
 +
 
 +
=== How do I use undocument Feature XYZ? ===
 +
You don't. By reading RAP source code or inspecting JavaScript objects you may discover methods, fields or parameters that are not in the official WebClient API documentation. Do not use them. Not only may they change without notice, but mixing public and internal API may cause completely new problems that do not exist when sticking to either API.
 +
 
 +
=== How do I make an existing JavaScript Widget work as a RAP Custom Widget? ===
 +
You will need to wrap the widget in an JavaScript object that adapts the API to a something the type-handler can manage. The [https://github.com/eclipsesource/rap-ckeditor CKEditor] does just that. All you need from the widget is an HTMLElement object to attach to the parent Composite.
 +
 
 +
=== How do I register my CustomWidget ? ===
 +
 
 +
=== What should the factory method return? ===
 +
 
 +
=== How do I keep the server in sync with the client? ===
 +
 
 +
=== How do I insert my Widget into the DOM? ===
 +
 
 +
=== How do I get the parents HTML element? ===
 +
 
 +
=== How do I use rap.on? ===
 +
 
 +
== Troubleshooting ==
 +
 
 +
=== How do I debug my Custom Widget? ===
 +
 
 +
=== Why doesn't my Custom Widget work in Browser X? ===
 +
RAP can do very little to ensure cross-browser compatibility. If you develop your widget from scratch you should use additional JavaScript libraries that handle some of the cross-browser issues and check [http://www.quirksmode.org/compatibility.html Quirksmode] and/or [http://caniuse.com Can I use] to see what browser supports what. If you wrap an existing JavaScript component, check first if it works umodified in a standalone HTML document.
 +
 
 +
=== After calling Composite.append my Element it is not in the DOM ===
 +
The append method may be called at a time where the parent element does not yet exist. In this case your element will be added as soon as it is created.
 +
 
 +
If you want to be notified after that happend you can do it like this:
 +
 
 +
<code>
 +
    var element = document.createElement( "div" );
 +
    var onRender = function() {
 +
      if( element.parentNode ) {
 +
        rap.off( "render", onRender );
 +
        // DO YOUR STUFF HERE
 +
      }
 +
    };
 +
    parent.append( element );
 +
    rap.on( "render", onRender );
 +
</code>
 +
 
 +
=== My widget works fine, but shortly after it is disposed the client crashes! ===
 +
You likely forgot to de-register an javascript event listener that was registered by your custom widget.
 +
 
 +
=== Calling remoteObject.set on the client does nothing ===
 +
As described in the [http://download.eclipse.org/rt/rap/doc/2.2/guide/reference/jsdoc/symbols/RemoteObject.html#set WebClient API], "This method does not cause the message to be sent immediately. Instead it will be sent the next time a "notify" or "call" operation is written to the message". The purpose of "set" is to synchronize the state of server and client objects, not to cause any code execution beyond that. If your custom widget provides a "change" event (in java) for this property, then in addition to "set" use "notify". E.g.:
 +
<code>
 +
  remoteObject.set( "text", newValue );
 +
  remoteObject.notify( "textChanged" );
 +
</code>
 +
 
 +
If there is some internal code that needs to be executed in your java custom widget when the property changes, use "call" in addition to "set". This should rarely be needed, usually when the widget behavior logic is somehow split between server and client. Example:
 +
 
 +
<code>
 +
  remoteObject.set( "text", newValue );
 +
  remoteObject.notify( "doSomething" );
 +
</code>
 +
 
 +
=== Calling remoteObject.notify does nothing ===
 +
For your "notify" call to do something you need to satisfy two conditions:<br/>
 +
1) Your typeHandler has en entroy for this event type in the "events" array<br/>
 +
2) The server has previsously sent a "listen" operation for this event type.<br/>
 +
<br/>
 +
See also [[#How exactly do I use the "listen" method?|How exactly do I use the "listen" method?]].

Latest revision as of 21:26, 11 March 2015

General

What is this about?

This is an FAQ about HTML/RemoteObject based custom widget development for RAP 2.x (WebClient). For other kinds of custom widgets, see RAP/Custom_Widgets and here

How is Custom Widget Development different in RAP 2.x?

Compared to RAP 1.x:

  • Custom widgets no longer need an LCA.
  • Custom widgets no longer have to work with unstable internal client API.
  • Custom widgets no longer extend existing client widgets.
  • Custom widgets now use RemoteObjects to communicate between client and server.
  • Custom widgets now have a stable API to built on.
  • Custom widgets now are only rendered based on HTML/DOM/CSS that is attached to a Composite.

Where can I find a Tutorial?

There is currently no tutorial. For now there are the following resources:

How is this related to ClientScript/RWT Scripting?

Short answer: This approach does not involve ClientListener, and RWT Scripting does not involve HTML or RemoteObjects.

RWT Scripting (formerly known as ClientScripting) is the practice of attaching a piece of JavaScript ("ClientListener") to an existing widget and react to events that it fires. The "widget" object used in RWT Scripting (which can be obtained from the event object or from rap.getObject) are not the internal client widget, but a wrapper with SWT-like API that allows savely interacting with the widget on the client. These wrapper can not be destroyed or created on the client.

It is possible to create custom widgets using the SWT/compound approach and enhance them with RWT Scripting, but this is not what this FAQ is about.

What the two have in common is the Composite client object type. Like any other widget obtained on the client it is a wrapper with SWT-like API, but also some exclusive methods (append, getClientArea, addListener, removeListener) which allow creating custom widgets.

How do I port my RAP 1.x Custom Widget to RAP 2.x?

Either you use the new API, in which case your widget is future-proof, but you will have to re-write much or all of your code. Or you continue to use LCAs and client internals, in which case you will have to read the client code to figure out what changed, and the widget will not be future-proof.

Obviously we recommend to use the new API, but it is strongly discouraged to somehow mix the two approaches.

What is the basic architecture of a Custom Widget?

As a custom widget developer you have to provide three components:

  • A Java class that extends Composite and creates a RemoteObject with an OperationHandler.
  • A client "typeHandler", which is an JavaScript object that creates and configures the remote JavaScript object (i.e. the widget).
  • The JavaScript widget itself (which is instantiated in the typeHandler).

CustomWidgetUML.png

To clarify: While the custom widget *extends* Composite on the server, on the client the Composite is the *parent*.

Step by step:

  • Create a java class (the actual custom widget used in an application) that extends Composite.
  • Use the ResourceManager in the constructor register your javascript files and other resources, but be careful to only do it if they haven't already be registered for this RAP application (e.g. use "isRegistered"). Alternatively use the ApplicationConfiguration to register the resources, or use another servlet/server.
  • In the constructor, use the JavaScriptLoader to load the required scripts on the Client.
  • In the constructor, create a RemoteObject using RWT.getUISession().getConnection().createRemoteObject( "my.CustomWidgetType" );. The type can be any string and will later be used to register the typeHandler on the client.
  • Still in the constructor, set the widgets own id as a parent on the RemoteObject (e.g. remoteObject.set( "parent", WidgetUtil.getId( this ) );)
  • In one of your JavaScript files a typeHandler must be registered. A minimal implementation would look like this:
 rap.registerTypeHandler( "my.CustomWidgetType", {
   factory : function( properties ) {
     return new MyCustomWidget( properties ); // or any other way to create a JavaScript object that represents the widget
   }
 } );
  • MyCustomWidget in that example would need to be a constructor for an HTML/JavaScript based widget/component/application that can be attached anywhere in the DOM. It's API should mainly consist of setter that can be called by the server (using RemoteObject#set). These also need to be listed in the typeHandlers property list.
  • The client widget needs to attach it's HTML to the composite (for example in the factory or constructor) like this:
   var parent = rap.getObject( properties.parent );
   var element = document.createElement( "div" );
   parent.append( element );
  • Now the widgets HTML should be visible in the application.
  • To send changes to the server, the client widget can use it's own RemoteObject: rap.getRemoteObject( clientWidget ).set( "prop", value );
  • The client widget can listen to resize events on the parent composite to layout itself.
  • When destroyed, the client widget must remove itself from the parent composite and de-register all listeners from the composite or "rap" object.

What about Single-Sourcing?

Custom Widgets based on the WebClient API can not be Single-Sourced with SWT. There is no other way than creating another Custom Widget for SWT with the same API (just like RAP has the same API as SWT). Single-Sourcing with other client platforms is possible for the server part of the custom widget. The client part has to be implemented in the clients native language.

Note that are kinds of custom widgets are single-sourcing capeable, as mentioned in the developers guide.

Java API

How exactly do I use the "listen" method?

The RemoteObjects listen method "Instructs the remote object to listen or to stop listening on the given type of events.". In the context of the WebClient, this means that the client RemoteObjects notify method does only send "notify" operations if the server has previously called "listen( "MyEventType", true )".

The idea is that not all user-interactions with the widget need to trigger HTTP-requests. Only if the application has registered a listener for that specific action is that necessary.

A full example:

We assume your server widget supports listener registration for the type "Selection", and furthermore that you build on top of the SWT event system (which is entirely optional). The registration methods could look like this:

   @Override
   public void addListener( int eventType, Listener listener ) {
     boolean wasListening = isListening( eventType );
     super.addListener( eventType, listener );
     boolean isListening = isListening( eventType );
     String remoteType = eventTypeToString( eventType );
     if( remoteType != null && !wasListening && isListening ) {
       remoteObject.listen( remoteType, true );
     }
   }
 
   @Override
   public void removeListener( int eventType, Listener listener ) {
     boolean wasListening = isListening( eventType );
     super.removeListener( eventType, listener );
     boolean isListening = isListening( eventType );
     String remoteType = eventTypeToString( eventType );
     if( remoteType != null && wasListening && !isListening ) {
       remoteObject.listen( remoteType, false );
     }
   }

The "eventTypeToString" method should be implemented to return "Selection" for SWT.Selection. Now, assuming you have "Selection" in your typeHandlers "events" list, the client will only send requests on calls of rap.getRemoteObject( this ).notify( "Selection" ) if there are selection listeners attached, in which case your servers RemoteObject will call it's OperationHandler.

This is only necessary for "notify" operations. "set" operations will always be sent, but not immediately. If you want to notify the server immediately regardless of any attched listener, simply use "call".

Client API

I'm not an JavaScript expert. Any tips?

JavaScript isn't that hard if you do it right. Here are some recommendations:

  • Use JavaScript frameworks that make your code shorter, like Underscore.js or jQuery.
  • Use JSHint to prevent common JavaScript mistakes.
  • Use strict mode whenever possible.
  • Read this list of similarities and differences of Java and JavaScript.
  • Write tests. For JavaScript, if you have more than 50 lines of code it will almost definetly save you time in the long run!
  • For OO-programming, either learn how prototypes and closures work in JavaScript, or use a framwork that provides a pseuso-class system (like prototype or backbone). Or use a factory pattern, if you can live with slightly more memory consumption. Also, here's a blog post on private members in javascript.

Isn't RAP based on qooxdoo? Can/Should I use that?

Yes and No, Yes, No.

The RAP WebClient is basically a fork of qooxdoo 0.7.4. However, over the years the client has been, reduced, optimzed, re-written, enhanced and re-organized so that the qooxdoo reference for that version would not help you much. These frequent changes are the reason that RAP 2.x introduced a new, stable, qooxdoo independent, public API. RAP may now modify or completely replace it's underlying library without breaking Custom Widgets.

Of course it is still technically possible to built on the qooxdoo part of the WebClient, but you will find no documentation how to do that and it is entirely on your own risk.

How do I use undocument Feature XYZ?

You don't. By reading RAP source code or inspecting JavaScript objects you may discover methods, fields or parameters that are not in the official WebClient API documentation. Do not use them. Not only may they change without notice, but mixing public and internal API may cause completely new problems that do not exist when sticking to either API.

How do I make an existing JavaScript Widget work as a RAP Custom Widget?

You will need to wrap the widget in an JavaScript object that adapts the API to a something the type-handler can manage. The CKEditor does just that. All you need from the widget is an HTMLElement object to attach to the parent Composite.

How do I register my CustomWidget ?

What should the factory method return?

How do I keep the server in sync with the client?

How do I insert my Widget into the DOM?

How do I get the parents HTML element?

How do I use rap.on?

Troubleshooting

How do I debug my Custom Widget?

Why doesn't my Custom Widget work in Browser X?

RAP can do very little to ensure cross-browser compatibility. If you develop your widget from scratch you should use additional JavaScript libraries that handle some of the cross-browser issues and check Quirksmode and/or Can I use to see what browser supports what. If you wrap an existing JavaScript component, check first if it works umodified in a standalone HTML document.

After calling Composite.append my Element it is not in the DOM

The append method may be called at a time where the parent element does not yet exist. In this case your element will be added as soon as it is created.

If you want to be notified after that happend you can do it like this:

   var element = document.createElement( "div" );
   var onRender = function() {
     if( element.parentNode ) {
       rap.off( "render", onRender );
       // DO YOUR STUFF HERE
     }
   };
   parent.append( element );
   rap.on( "render", onRender );

My widget works fine, but shortly after it is disposed the client crashes!

You likely forgot to de-register an javascript event listener that was registered by your custom widget.

Calling remoteObject.set on the client does nothing

As described in the WebClient API, "This method does not cause the message to be sent immediately. Instead it will be sent the next time a "notify" or "call" operation is written to the message". The purpose of "set" is to synchronize the state of server and client objects, not to cause any code execution beyond that. If your custom widget provides a "change" event (in java) for this property, then in addition to "set" use "notify". E.g.:

 remoteObject.set( "text", newValue );
 remoteObject.notify( "textChanged" );

If there is some internal code that needs to be executed in your java custom widget when the property changes, use "call" in addition to "set". This should rarely be needed, usually when the widget behavior logic is somehow split between server and client. Example:

 remoteObject.set( "text", newValue );
 remoteObject.notify( "doSomething" );

Calling remoteObject.notify does nothing

For your "notify" call to do something you need to satisfy two conditions:
1) Your typeHandler has en entroy for this event type in the "events" array
2) The server has previsously sent a "listen" operation for this event type.

See also How exactly do I use the "listen" method?.

Back to the top