GWT-EXT: Retain Checkbox selections in page-able GridPanel

December 30th, 2008 by Tom Bollwitt

When working on a sample project for Mersoft using GWT-EXT I came across a use case that required a data table that was page-able, had a checkbox selection per row, and needed to maintain the checkbox selections when paging.

Here is what I ended up doing. It may not be pretty, but hey, its a sample.

I made a few updates to the CheckboxSelectionModel.js in order to properly update the checkbox in the Header row when a user manually (un)selects all the rows. This required that I comment out the CheckboxSelectionModel definition in the ext-all.js and include the CheckboxSelectionModel.js in my HTML. I updated the onMouseDown function as follows…

onMouseDown : function(e, t){
        if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
            e.stopEvent();
            var row = e.getTarget('.x-grid3-row');
            if(row){
                var index = row.rowIndex;
                if(this.isSelected(index)){
                    this.deselectRow(index);
                }else{
                    this.selectRow(index, true);
                }

                //Get the CheckboxHeader so it can be updated.
               //Loop through the header cells since the user may have re-ordered them...
               var cbHeader = null;
               var i = 0;
               var view = this.grid.getView();
               var headerCellEl = view.getHeaderCell(i);

               while(cbHeader == null && headerCellEl != null){
                  if(headerCellEl.className.indexOf('x-grid3-td-checker') > -1){
                     cbHeader = headerCellEl.firstChild;
                     cbHeader = Ext.fly(cbHeader);
                  }
                  headerCellEl = view.getHeaderCell(++i);
               }

                if (cbHeader) {
                if (this.grid.getStore().getCount() == this.selections.length) {
                   //all the rows are selected, so check the header...
                   cbHeader.addClass('x-grid3-hd-checker-on');
                } else {
                   //not all the rows are selected, so uncheck the header...
                   cbHeader.removeClass('x-grid3-hd-checker-on');
                }
             }
            }
        }
    },

I also used the code from the URL http://extjs.com/forum/showthread.php?t=45723&highlight=getselectionmodel, however it too needed to be modified to update the Header checkbox.

I created a CheckBoxMemory.js with the following code and included it in my HTML…

Ext.namespace('Ext.ux.plugins');

Ext.ux.plugins.CheckBoxMemory = Ext.extend(Object, {
   constructor : function(config) {
      if (!config)
         config = {};

      this.prefix = 'id_';
      this.items = {};
      this.idProperty = config.idProperty || 'id';
   },

   init : function(grid) {
      this.grid = grid;
      this.view = grid.getView()
      this.store = null;
      this.sm = grid.getSelectionModel();
      this.sm.on('rowselect', this.onSelect, this);
      this.sm.on('rowdeselect', this.onDeselect, this);
      this.view.on('refresh', this.reConfigure, this);
   },

   reConfigure : function() {
      this.store = this.grid.getStore();
      this.store.on('clear', this.onClear, this);
      this.store.on('datachanged', this.restoreState, this);
   },

   onSelect : function(sm, idx, rec) {
      this.items[this.getId(rec)] = true;
   },

   onDeselect : function(sm, idx, rec) {
      delete this.items[this.getId(rec)];
   },

   restoreState : function() {
      if (this.store != null) {
         var i = 0;
         var sel = [];
         this.store.each( function(rec) {
            var id = this.getId(rec);
            if (this.items[id] === true)
               sel.push(i);

            ++i;
         }, this);
         if (sel.length > 0){

            this.sm.selectRows(sel);
         }

         var cbHeader = null;
         var j = 0;
         var view = this.grid.getView();
         var headerCellEl = view.getHeaderCell(j);

         while (cbHeader == null && headerCellEl != null) {

            if (headerCellEl.className.indexOf('x-grid3-td-checker') > -1) {
               cbHeader = headerCellEl.firstChild;
               cbHeader = Ext.fly(cbHeader);
            }
            headerCellEl = view.getHeaderCell(++j);
         }

         if (cbHeader) {
            if (this.store.getCount() == this.sm.selections.length) {
               cbHeader.addClass('x-grid3-hd-checker-on');
            } else {
               cbHeader.removeClass('x-grid3-hd-checker-on');
            }
         }
      }
   },

   onClear : function() {
      var sel = [];
      this.items = {};
   },

   getId : function(rec) {
      return rec.get(this.idProperty);
   }
});

I have not had a chance to really dive in ,but you will need to make sure that your RecordDef has a FieldDef with a name of “id”, example… new StringFieldDef(”id”, 0)

I then created the corresponding Java class as follows.

package com.mersoft.gwtsample.client.widgets.grid;

import com.google.gwt.core.client.JavaScriptObject;
import com.gwtext.client.widgets.Component;
import com.gwtext.client.widgets.ComponentPlugin;

public class CheckBoxMemoryPlugin extends ComponentPlugin {

	public CheckBoxMemoryPlugin() {
		jsObj = create();
	}

	protected native JavaScriptObject create() /*-{
		return new $wnd.Ext.ux.plugins.CheckBoxMemory();
	}-*/;

	@Override
		public void init(Component component) {
	}
}

Then you just need to add the CheckBoxMemoryPlugin to the GridPanel. (This assumes that you already have set up a Local Paging Checkbox Grid Panel)

grid.addPlugin(new CheckBoxMemoryPlugin());

Anyway, I hope this helps someone else who may be looking to solve this.

I still need to update the code in order to be able to get the selected row IDs, but that is for another day.

Share/Save/Bookmark

Try to Stay DRY

December 2nd, 2008 by dglee

Have you ever cut and pasted a piece of code and then tweaked the resultant new code just slightly in order to perform a variation on the first piece of code?  Oh come on now, fess up.  Well that nagging feeling of guilt you have is your innate understanding of the DRY Principle kicking in. Listen to it: it is trying to help you.

The DRY principle (Don’t Repeat Yourself) dictates that there is one and only one authoritative source for a piece of information or logic with in a system.  Imagine that the code you just cut and pasted was an implementation of a business rule.  A new requirement arose that instigated your rash action.  There was just enough of a variance of behavior that generalizing the original method would have required more effort than you were willing to expend so you cut and pasted the code to implement the new functionality.  The only problem is that now you have two pieces of code that share common functionality implemented in two different places.  What happens when that common functionality needs to be changed?  What are the risks that it will not get changed in every instance?  I think you are getting the idea of the reason for the DRY principal.

The Pragmatic Programmer states:

DRY says that every piece of system knowledge should have one authoritative, unambiguous representation. Every piece of knowledge in the development of something should have a single representation. A system’s knowledge is far broader than just its code. It refers to database schemas, test plans, the build system, even documentation.

Failure to follow the DRY principal results in brittle, difficult to maintain code.  One of the most blatant and yet pervasive violations of the DRY principle is coupling the database directly to the presentation layer of an application.  Many naive application implementations will encapsulate SQL statements directly into PHP, or VB forms thus tightly coupling the presentation and the database.  If you have ever had to root through code written by a junior VB programmer you will know exactly what I mean.  Not only is this kind of code difficult to maintain, it makes changing the database very difficult.  This kind of coding practice can have far reaching consequences.  What seemed like such a quick implementation now reveals itself as a huge maintenance trap for the customer.

The best way to avoid this form of coupling is to encapsulate the business logic and the database access in their own components.  The presentation layer can safely use those components without fear that emerging changes will break functionality all over the application:  breakages that will have to be painstakingly hunted down and fixed, usually through an arduous process of trial and error.

While this might seem like SOP to a reasonably competent OO developer, it is amazing how many new applications are developed every day that do not adhere to this fundamental design paradigm.  Don’t let yours be one of them.  Keep it DRY.

Share/Save/Bookmark

.net Clickable Table Header Cell

November 12th, 2008 by Mandi Zinn

One of my tasks at Mersoft was to update a .net web site. In the website, there was a data table that needed to be sortable. However, the .net’s TableHeaderCell did not have a clickable feature, and I couldn’t find any equivalent that was clickable. So, I created my own.  Here’s how I did it…

Code for the Clickable Table Header Cell control:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

namespace ClickableWebControl
{

/// <summary>
/// Summary description for ClickableTableHeaderCell
/// </summary>
public class ClickableTableHeaderCell : TableHeaderCell, IPostBackEventHandler
{
public ClickableTableHeaderCell()
{
//
// TODO: Add constructor logic here
//
}
protected override void Render(HtmlTextWriter writer)
{
this.Attributes.Add(”onclick”, Page.ClientScript.GetPostBackEventReference(this, “”, true));
base.Render(writer);

}

/// <summary>
/// Event raised when a text box click event takes place
/// </summary>
public event EventHandler TableHeaderCellClicked;

/// <summary>
/// Handler for the text box event
/// </summary>
/// <param name=”sender”></param>
/// <param name=”e”></param>
protected void TableHeaderCell_Click(object sender, EventArgs e)
{
if (TableHeaderCellClicked != null) {
TableHeaderCellClicked(sender, e);
}

}

#region IPostBackEventHandler Members

void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{

if (TableHeaderCellClicked != null)
{
TableHeaderCellClicked(this, new EventArgs());
}

}

#endregion
}
}

Code on the .aspx page:

<%@ Register Namespace=”ClickableWebControl” TagPrefix=”clkweb” %>

<asp:Table ID=”myTable” runat=”server” Width=”930px”>
<asp:TableHeaderRow ID=”headerRow”>
<clkweb:ClickableTableHeaderCell Text=”Name” OnTableHeaderCellClicked=”SortName” id=”sortName”/>
</asp:TableHeaderRow>
</asp:Table>

In the .cs file, add the function to be performed when the header is clicked:
public void SortName(Object sender, EventArgs e)
{
//Code goes here to handle event when it’s clicked
}

Share/Save/Bookmark

My Experience with Google AJAX toolkit

November 12th, 2008 by Mandi Zinn

 

Google Web Toolkit

Google Web Toolkit

 

 

My first task at Mersoft was to develop an ajax-rich web interface with a Java backend. I was told that it must be tabbed and was shown the existing interface which pretty much was just a file upload. The project was to keep the file upload plus have several tabs containing different types of reports generated from the file upload.

I was originally directed to Tibco’s GI. The GI has it’s own “Interface Builder” used to build the gui. Creating tabs using the builder was easy, but it looked like the only way the interface could talk to a server was through a web service. Also, I didn’t see anything that would allow me to do a file upload. I started looking at the source code, and it appeared difficult to customize and/or expand.

After doing some searching, I came across Google’s Web Toolkit (GWT). This uses Java code to build the interface and gets compiled to html and javascript. This even works with Eclipse. GWT was able to handle file uploading (although due to security issues, this had to be done with a form submit). I have been able to find lots of plugins, plus it has been very easy to expand most plugins and it seems easy to create my own plugin for GWT. Also, although I haven’t done this personally, it looks like it’s easy to integrate with a Javascript library, if the need ever arises. I was also able to integrate it with Struts 2.

I have looked at several other toolkits. Most of them required a lot of Javascript, even with the use of the toolkit. For me (a Java programmer), it is much easier writing code in Java than in Javascript. After reviewing the other frameworks and working with projects that do not use GWT, I highly recommend GWT and look forward to using it on future projects.

Share/Save/Bookmark

Do What You Do Best… Let Someone Else Do The Rest.

November 3rd, 2008 by Carolyn Wood
Applying Maslow’s hierarchy of needs to your IT decisions, you will find that your operations and infrastructure comprise the lowest level. Your absolute “musts” include a reliable Internet connection, email, security, backup, and disaster recovery. These elements are fundamental, and you cannot succeed and grow without them.  Small companies need these critical areas to function just as well as their counterparts in Fortune 500 companies do.  Your firewall needs to be just as secure as theirs, your privacy standards just as tight, and your understanding of legal issues just as strong.
With these fundamentals in place, you must then determine which core business functions your company needs to own and fully manage, and which procedures can be outsourced.  If you produce widgets, for example, you need to own the widget producing system, but it may make good sense to outsource your billing to a company that specializes in that and has the appropriate systems already in place.  Similarly, if you need custom software, it makes good sense to find a software development company that understands your needs and goals and can work with you to develop appropriate software and hardware solutions, rather than trying to adapt a one-size-fits-all off-the-shelf program.  A good software design company will embrace your business vision and create the tools you need to achieve your goals.
Finally, be sure to keep your finger on the pulse of new IT developments that are applicable to your industry. IT groups and industry organizations can be excellent sources of information, as can user groups for the business systems you are using. Keeping current with what others in your field are doing in terms of IT helps as well.  Your goal is always to think strategically and to stay at least a little ahead of where your company is right now technologically.

Share/Save/Bookmark

Problems with duplicate or multiple log messages using log4j xml

November 3rd, 2008 by Peter Tarlos

Are you seeing duplicate or multiple log messages using log4j xml configuration?
The configuration below produces three log messages.

##############
# log4j.xml
##############
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE log4j:configuration SYSTEM “log4j.dtd”>
<log4j:configuration xmlns:log4j=”http://jakarta.apache.org/log4j/”>
  <appender name=”console” class=”org.apache.log4j.ConsoleAppender”>
    <param name=”Target” value=”System.out” />
    <layout class=”org.apache.log4j.PatternLayout”>
      <param name=”ConversionPattern” value=”%-5p %c{1} - %m%n” />
    </layout>
  </appender>
  <logger name=”com.acme.LoggingTest”>
    <level value=”debug” />
    <appender-ref ref=”console” />
  </logger>
  <logger name=”com.acme”>
    <level value=”debug” />
    <appender-ref ref=”console” />
  </logger>
  <root>
    <priority value=”debug” />
    <appender-ref ref=”console” />
  </root>
</log4j:configuration>

#####################
# LoggingTest.java
#####################
package com.acme;
public class LoggingTest {
  
  private Logger log = Logger.getLogger(this.getClass());

  public static void main(String[] args) {
    // initialize log4j
    URL propsUrl = Loader.getResource(”log4j.xml”);
    DOMConfigurator.configure(propsUrl);
    new LoggingTest();
  }
  
  public LoggingTest(){
    log.debug(”Hello World”);
  }
}

The reason for the multiple log messages is that the three loggers (”com.acme.LoggingTest”, “com.acme” and “root”) are configured to use the same “console” appender. The “com.acme.LoggingTest” logger logs the first message to its appender and then traverses through its ancestors and logs to their appenders as well. This behavior is called appender additivity and it is set to “true” by default on all loggers including the “root” logger.

Logger Hiearchy:
 - root
  - com.acme
   - com.acme.LoggingTest

The appender additivity can be turned off by specifying additivity=”false” on a logger. This causes the logger to only log to its appender but not to the appenders of its ancestors. Setting the additivity=”false” on all the loggers solves the multiple logging problem. The modified configuration below only outputs a single log message.

##########################################
# Fixed log4j.xml with additivity=”false”
##########################################
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE log4j:configuration SYSTEM “log4j.dtd”>

<log4j:configuration xmlns:log4j=”http://jakarta.apache.org/log4j/”>
  <appender name=”console” class=”org.apache.log4j.ConsoleAppender”>
    <param name=”Target” value=”System.out” />
    <layout class=”org.apache.log4j.PatternLayout”>
      <param name=”ConversionPattern” value=”%-5p %c{1} - %m%n” />
    </layout>
  </appender>
  <logger name=”com.acme.LoggingTest” additivity=”false”>
    <level value=”debug” />
    <appender-ref ref=”console” />
  </logger>
  <logger name=”com.acme” additivity=”false”>
    <level value=”debug” />
    <appender-ref ref=”console” />
  </logger>
  <root>
    <priority value=”debug” />
    <appender-ref ref=”console” />
  </root>
</log4j:configuration>

More information on this topic can be found on the log4j wiki.

Share/Save/Bookmark

Five Things to Look for in a Custom Software Development Company

November 3rd, 2008 by Carolyn Wood
Selecting an IT company to help build your growing business can be a daunting undertaking, to say the least…  With such an abundance of software development companies vying for your business, what should you look for?  How do you separate the good from the not-so-good?  Other companies that have successfully tackled this challenge have found the following points to be important factors to consider. 
 
Your ideal Software Development Partner should:
  1. Listen to your needs and goals and embrace your business plan as its own.
  2. Work with you to understand and define your unique requirements and to build a relationship with your company based upon trust, open communication, and a shared dedication to your business objectives 
  3. Offer outstanding technical capability, with teams of engineers who have considerable depth of experience in multiple hosting environments and are proficient in a wide range of developmental languages
  4. Provide a variety of hardware and software solutions, ranging from complete custom-designed systems to programs that will interface with your current technology – and back up its solutions with ongoing technical support.
  5. Demonstrate a proven track record of producing successful solutions — and doing it within budget and on time.

 Investing the time and money to expand and fine-tune your IT capabilities is a major step – and one that will prove pivotal to the success of your business.  Taking the time and effort to select your IT partner wisely will pay huge dividends, both now and in the future. 

Share/Save/Bookmark

Growing Your Business with Strategic Information Technology Planning

October 27th, 2008 by Carolyn Wood

As your business grows, your Information Technology needs grow apace, but too many companies fail to give IT the budgetary and planning priority it deserves.  Putting IT expenditures too far down the list of priorities will hamper your efficiency and stifle the growth of your business.

Think strategically about IT, focusing on where you want to be in 6 months or a year, rather than where you are now, so that your IT capabilities will be able to work in tandem with your business strategy without lagging behind. It’s also vitally important to include training and support in your overall IT plan.  If your employees aren’t equipped to fully utilize the software and equipment—efficiency will be significantly diminished and you won’t get full value for your IT investment.

If you give low priority to IT planning, you will often find yourself in a reactive mode, forced to make quick decisions to replace technology that’s outdated or has suddenly failed.  This puts you always behind the curve, buying impulsively – a mistake that can saddle your business with mismatched products that are hard to administer and make subsequent training more difficult.  By thinking strategically in your IT planning and by understanding and anticipating your business needs, you can acquire and integrate leading technologies in a systematic fashion.

Choose your IT vendors carefully.  Establish partnerships of trust with your preferred software development outsourcing firm, hardware suppliers, commercial-off-the-shelf software vendors, and support people.  Avoid the common pitfall of keeping your needs close to the vest.  You need to be clear and open about your current situation, your goals, and priorities. Find a company that is dedicated to understanding your particular business needs and to working within your parameters to develop custom designed software and hardware solutions that deliver good value and low risk. This strategic investment in information technology helps create consistency and standardization and increases efficiency, which in turn reduces labor costs and helps your business grow.

Share/Save/Bookmark

Local Outsourcing for Software Development

October 13th, 2008 by Jim Dusthimer

As of 2005, the U.S. Census Bureau ranked Kansas City 40th by population. A well known transportation, finance/banking, telecommunications, and agriculture hub—companies like Sprint, Kansas City Southern Railroad, Associated Grocers, and YRC Worldwide are based in Kansas City. The area is blessed with a wealth of talented software development teams either in “captivity” (as directly hired employees of a company) or in the form of companies like Mersoft—firms that exist to serve in a consulting and development role when businesses need to utilize external resources. We’re in a good market and we have talent… but the economic downturn we’ve seen recently gives us good cause to look at the outlook for IT Outsourcing and Custom Software Development.

Unfortunately, nationwide spending on equipment and software shrunk by 5% in the second quarter of this year—56% more than was anticipated. It was the second straight quarter that spending in this category contracted. This is a signal for economists that production plans are being scaled back. These are not the kind of statistics that anyone wants to hear. When production is slowed, everyone feels it. We could see job loss and decreases in consumer spending—signs of recession.

We’ve positioned ourselves to overcome the slowdown. Economists can read the proverbial cards all they want with respect to decreases in software spending reflecting a production slowdown—but there are other reasons to spend money on software development. So despite what the economists are saying, we remain optimistic about putting our local experts to work for Kansas City area companies in need of outsourced development teams.

We know that businesses outsource software development for other good reasons, like:

  • Increasing efficiency. Spending the dough on software solutions that allow reallocation of employee resources will enable a more efficient multi-tasking model. An outsourced software solution can decrease the burden on employees in multiple areas, allowing them to divide their workday in a way that allows them to do the work normally done by 2 or more people.
  • Avoiding HR headaches and expense. Salaries, foibles, health insurance, time-off—just a few examples of what can be avoided by local outsourcing for software development.
  • Doubt free prioritization and outcomes. Hiring an outside firm to take a project to completion doesn’t permit internal priorities to dilute efforts. In general, internal IT staff is routinely pulled in different directions as challenges erupt. End users constantly “opening tickets” for help with legacy systems is a prime example of this. If the engineers only had more time to work on the solution that would replace the legacy system in the first place… In many cases, outsourcing the project is more likely to get the project done on-time and with a superior outcome.
  • Dedication and commitment. When a project is outsourced to a reputable software development firm, the chosen firm is 100% vested in the project. In a market like Kansas City—big enough to offer wealth of choice, and small enough for reputation (good and bad) to spread quickly—it’s a certainty that the right outsourced development firm will attempt much more than simply meeting the minimum requirements on a project. The right company will seek ways to exceed expectations, earning bragging rights, referrals, and repeat business in the process.

So lack of intent to increase production doesn’t preclude local software development as a solution to enhance business processes and, ultimately, drive cost reductions. On a basic level, a downturn in the economy means we need to do more with less. This is what local outsourcing is all about… doing more with the line of business application itself… and without an increase in headcount and headaches.

Share/Save/Bookmark

Custom Software: What do you do when you can’t afford it?

October 8th, 2008 by Jim Dusthimer

Try as we may…we can’t help everybody. Recently, we worked closely with a Kansas City company in the healthcare field that needed a custom software solution. After running through several options in terms of commercial, pre-built options, we came to the realization that there was indeed nothing available that did precisely what our customer needed. Customized applications would need to be created in order to fulfill their dream of more efficient processing, tracking, and reporting.

Their’s is a unique niche…complete with convoluted state forms and processes, complicated billing mechanisms, redundant order entry and paper…lots of paper. Hundreds of hours of development time would be a certainty for the project and costs would run into the tens of thousands of dollars. Their budget was a fraction of what it would ultimately cost.

Do you say, “Sorry…you can’t afford us”, and wash your hands of the situation? Do you take it on and pray that you can resell the solution to other companies operating in the same unique niche? Do you bite off a piece of the project that fits their budget and use every penny of it for a partial solution?

We really wanted to help these folks…they provided a great service for some very disadvantaged people. They were good caring people, deserving of some help. In the end, we settled on a creative path that just might eventually get them where they want to be.

We called a local educational institution and explained the situation to them. Turns out—this sort of project was right up their alley. Any given semester, there are Computer Science graduate students who are assigned projects and internships. The requirements often include assisting a local, established business improve processes with custom software applications. The cost for their service? Zip. Nada. Nothing. Zilch. The university wins, the customer wins, and so does the student. Everybody’s happy…

Sure there are some unknowns…but at least now there’s a possible path to success illuminated. And although we regret that we couldn’t help out directly, helping indirectly feels pretty good too. So talking to local universities is worth a try when a custom solution from an experienced, professional firm blows a budget out of the water before the project ever gets off the ground.

Share/Save/Bookmark