Creating a Custom Ext GWT Component
Tweet
Creating custom Web components can prove challenging without a good foundation. Fortunately, the Ext GWT framework contains a Component API that provides the ability to quickly and easily create custom components while using Java. This post will walk you through the steps needed to create our ContentScroller - an Ext GWT UX component.
The ContentScroller
The ContentScroller component will allow any content to be vertically scrolled by placing a clickable navigation bar above and below the content. When the navigation bars are clicked the content is scrolled either up or down. The finished example can be viewed here.
Our UX User Interface
Our user extension will display two navigation bars. These bars must be styled which includes the HTML markup, CSS, and images.
Images
There are two images used for the component: the bar background, and the up and down arrows. All the arrow icons are in a single file. CSS offsets are used to display the appropriate part of the image. As an alternative to this method, the GWT ImageBundle could have been used rather than creating the CSS and offsets manually. Here are the two images used by the component: bar and arrows.
HTML Markup
For the HTML markup, a HTML TABLE element is used for each bar. The table has a single row with 3 cells: left, center, and right. The left and right will show the rounded ends of the bar, while the center region will show the center section of the bar. The width of the left and right cells are fixed, with the center width being dynamic based on the available width.
<div unselectable="on" class=" x-cscroller-bar x-unselectable " id="x-auto-0">
<table cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td class="x-cscroller-left"></td>
<td class="x-cscroller-center">
<div>
</div></td>
<td class="x-cscroller-right"></td>
</tr>
</tbody>
</table>
</div>
Given the images and HTML markup, the following CSS styles are used.
.x-cscroller-left { width: 20px; height: 22px; background: url(bar.gif); } .x-cscroller-right { width: 20px; background: url(bar.gif) top right; } .x-cscroller-center { background: url(bar.gif) top center; } .x-cscroller-center div { background: url(arrows.gif) no-repeat center -9px; height: 8px; overflow: hidden; } .x-cscroller-top-disabled .x-cscroller-center div { background-position: center 0px; } .x-cscroller-bottom .x-cscroller-center div { background-position: center -16px; } .x-cscroller-bottom-disabled .x-cscroller-center div { background-position: center -24px; } .x-cscroller .container { overflow: hidden; position: relative; margin: 6 0px; } .x-cscroller .scroller { position: relative; }
Create The Custom Component
Extending Component
The first decision for our custom component is to decide which base class to extend. The two primary choices are Component and BoxComponent. BoxComponent should be used when the custom component can be “sized” which is the case for our custom component.The component will essentially have three components: the top bar, the content area, and the bottom bar.
NavigationBar
For the navigation bars a custom component is defined as an inner class of ContentScroller. This will encapsulate the rendering and logic of each of the navigation bars. Since the bars do not need to be sized they can extend Component, rather than BoxComponent. Ext GWT renders its components “lazily”. That is, the DOM elements are not created when the component is initially constructed. Rather, the DOM elements are created right before a component is added to the DOM. This is different than GWT widgets which create the DOM elements at construction time. Lazy rendering ensures that DOM elements are not created until they are actually needed.
The first step when creating a custom component is to override the onRender method and supply the components root DOM element. For this component, the component is represented as a HTML DIV element.
Next, we need to construct the HTML table structure for the bar. Rather than manually creating the elements using the DOM API, the HTML structure is created using a StringBuffer which is then used to create the elements. This approach is much faster than doing manual DOM operations.
We are going to need to know when the bars are clicked so we must set this up. We call sinkEvents to specify what events the component is interested in being notified about.
After sinking the events, we need to override onComponentEvent to add our code that needs to be executed when the clicks occur. When working with Ext GWT, we do not override onBrowserEvent as done with straight GWT development.
We are also using a ClickRepeater. This utility class allows an action to be repeated as the user holds the mouse down over the bars. This allows the content to be scrolled quickly with a single mouse click.
@Override
protected void onRender(Element target, int index) {
setElement(DOM.createDiv(), target, index);
StringBuffer sb = new StringBuffer();
sb.append("<table width='100%' cellpadding='0' cellspacing='0'><tr>");
sb.append("<td class='x-cscroller-left'></td>");
sb.append("<td class='x-cscroller-center'><div> </div></td>");
sb.append("<td class='x-cscroller-right'></td>");
sb.append("</tr></table>");
el().setInnerHtml(sb.toString());
ClickRepeater repeater = new ClickRepeater(el());
repeater.addListener(Events.OnClick, new Listener<BaseEvent>() {
public void handleEvent(BaseEvent be) {
if (isTop) {
scrollUp();
} else {
scrollDown();
}
}
});
addAttachable(repeater);
disableTextSelection(true);
sinkEvents(Event.ONCLICK);
}
}
ContentScroller
Now that we have the code for the navigation bars we can look at the onRender method of ContentScroller. You will see that we create two instances of the new bar component. In addition, we set up the elements needed to contain and scroll the content. This is accomplished with two DIV elements. The first will be sized and have its CSS overflow property set to "hidden". The nested DIV will be the “scroller” element whose CSS top property is adjusted to move the content up and down.
@Override protected void onRender(Element target, int index) { setElement(DOM.createDiv(), target, index); top = new Bar(true); top.addStyleName("x-cscroller-top-disabled"); bottom = new Bar(false); bottom.addStyleName("x-cscroller-bottom"); top.render(getElement()); getElement().appendChild(XDOM.create("<div class='container'><div class='scroller'></div></div>")); container = el().selectNode(".container"); scroller = el().selectNode(".scroller"); if (content.isRendered()) { scroller.appendChild(content.getElement()); } else { content.render(scroller.dom); } // fixes firefox issue with bleeding content content.el().setStyleAttribute("overflow", "hidden"); bottom.render(getElement()); // make div focusable for keyboard events el().setTabIndex(0); // hide the focus outline el().setElementAttribute("hideFocus", "true"); new KeyNav<ComponentEvent>(this) { @Override public void onUp(ComponentEvent ce) { scrollUp(); } @Override public void onDown(ComponentEvent ce) { scrollDown(); } }; }
We override the onResize method so that we can make any size adjustments needed when the component is resized. All we need to do is set the height of our container element to be the current height of the component minus the heights of the two bars. The bar’s heights are fixed, so we do not need to retrieve the bar height dynamically.
@Override protected void onResize(int width, int height) { super.onResize(width, height); container.setHeight(height - 44); }
Attach / Detach
Since we are not using a Container or Panel for the component, we are responsible for attaching and detaching our child components. So we override onAttach and onDetach and manually attach and detach all three child components. Without this code, our child components would not receive any events.
@Override protected void onAttach() { super.onAttach(); ComponentHelper.doAttach(top); ComponentHelper.doAttach(bottom); ComponentHelper.doAttach(content); } @Override protected void onDetach() { super.onDetach(); ComponentHelper.doDetach(top); ComponentHelper.doDetach(bottom); ComponentHelper.doDetach(content); }
Scrolling
The last bit of code is the code that executes when the bars are clicked. To move the content up and down, we adjust the CSS top property of the scroller element. In addition, we change the bar’s active state based on the current scroll position. By adding and removing CSS styles to the bar, the arrow colors are changed.
private void scrollDown() {
int contentHeight = content.el().getHeight() - container.getHeight();
int top = scroller.getTop();
top = -(Math.abs(top) + scrollDistance);
top = Math.max(-contentHeight, top);
scroller.setTop(top);
update(top);
}
private void scrollUp() {
int top = scroller.getTop();
top = -(Math.abs(top) - scrollDistance);
top = Math.min(0, top);
scroller.setTop(top);
update(top);
}
private void update(int scrollTop) {
top.el().setStyleName("x-cscroller-top-disabled", scrollTop == 0);
int contentHeight = content.el().getHeight() - container.getHeight();
bottom.el().setStyleName("x-cscroller-bottom-disabled", scrollTop == - contentHeight);
}
Keyboard Support
In addition to the navigation bar, the content can be scrolled via the up and down arrow keys. We first make the component "focusable" by giving the root element a tab index. This is needed to allow the component to receive the keyboard events. Next, we use the KeyNav utility class that makes it trivial to add support for key events within your component.Summary
This tutorial demonstrated how a custom component can be designed and implemented with Ext GWT. I hope this tutorial inspires you to create some cool components using Ext GWT.The source code for the example is available for download.

There are 21 responses. Add yours.
Tweets that mention Ext JS - Blog -- Topsy.com
2 years ago[...] This post was mentioned on Twitter by extjs, Open4G Media. Open4G Media said: Creating a Custom Ext GWT Component - http://bit.ly/5rXF3M [...]
Michael
2 years agoOutstanding write up. Can be easily ported to regular JS components. Thanks for the overview. You guys rock.
Kirt
2 years agoGreat work Darrell. I always wanted to learn how to create a component using GWT, just never knew where to start. This will help tremendously.
alldevnet.com
2 years agoCreating a Custom Ext GWT Component…
Creating custom Web components can prove challenging without a good foundation. Fortunately, the Ext GWT framework contains a Component API that provides the ability to quickly and easily create custom components while using Java. This post will walk y…
Asad
2 years agoGreat write up. What’s great about using Box component as the base class is that it can participate in layouts. I just incorporated a ContentScroller into a border layout and it worked perfectly. Very nice work!
Vinícius Rabelo
2 years agoExcelent, helped me to understand very much…
Cristina Mayorga
2 years agoHi, exelent work, i really like EXT-GWT.
I was wondering if you are going to integrate some print functionality in future release. We are developing a huge project and the users are asking for this kind of function for the grids.
Thank you.
Greetings from Mexico.
triathlon
2 years agoExcellent work !
Jonathan Janisch
2 years agoNice article Darrell. I wrote something similar for our internal blog here. One thing which I was surprised worked was using tabIndex for focus. When I wrote my article, in both chrome and safari only INPUT elements could receive focus AND act as tab stops (but I did try your example and it seemed to work fine). The GXT Component class also has protected variable “focusable” which when true appends an input element specifically for webkit browsers. I found this never worked for me because the input field must have a width and height greater than 0 which is what I do for our app here. It may be helpful to explain more about these focus issues as tab navigation is really important.
matt nathanson music fan
2 years agoHey, thanks for this!
Diyet
2 years agoHTML Markup
For the HTML markup, a HTML TABLE element is used for each bar. The table has a single row with 3 cells: left, center, and right. The left and right will show the rounded ends of the bar, while the center region will show the center section of the bar. The width of the left and right cells are fixed, with the center width being dynamic based on the available width.
Zay?flama
2 years agoH?zl? Zay?flama
2 years agodiv unselectable=“on” class=” x-cscroller-bar x-unselectable ” id=“x-auto-0”>
</div
oya-dantel
2 years agoI wrote something similar for our internal blog here. One thing which I was surprised worked was using tabIndex for focus. When I wrote my article, in both chrome and safari only INPUT elements could receive focus AND act as tab stops (but I did try your example and it seemed to work fine). The GXT Component class also has protected variable “focusable” which when true appends an input element specifically for webkit browsers. I found this never worked for me because the input field must have a width and height greater than 0 which is what I do for our app here. It may be helpful to explain more about these focus issues as tab navigation is really important
Dantel Modelleri
2 years agoAll plugins must implement a method named init which is called by the Component, passing itself as the sole parameter at initialization time right at the beginning of the Component’s lifecycle, before it has been rendered.
Upon gaining control, a plugin must initialize itself to add capabilities and processing to its client Component.
JenniferB
2 years agoThanks for the info~
??
2 years agoIt may be helpful to explain more about these focus issues as tab.
Siki?
2 years agoExcellent work !
rocky lemon
2 years agoAmazing, I didn’t knew this, thankyou.
Kad?n
1 year agoThe GXT Component class also has protected variable “focusable” which when true appends an input element specifically for webkit browsers. I found this never worked for me because the input field must have a width and height greater than 0 which is what I do for our app here. It may be helpful to explain more about these focus issues as tab navigation is really important
Amazing, I didn’t knew this, thankyou
Akvaryum
1 year agoThe GXT Component class also has protected variable “focusable” which when true appends an input element specifically for webkit browsers. I found this never worked for me because the input field must have a width and height greater than 0 which is what I do for our app here. It may be helpful to explain more about these focus issues as tab navigation is really important
Comments are Gravatar enabled. Your email address will not be shown.
Commenting is not available in this channel entry.