Grouping with Coolite GridPanel

One of the nicest things about Coolite is that you can easily get a very modern looking website in short order, provided you take the time to understand the examples and ExtJS documentation, which maps pretty closely to the controls.  But there are still little niches you need to know to get the controls to work how you want.  One of the most finicky controls, I have found is the GridPanel.

So much interactivity can be achieved these days via JSON and grids it really is breathtaking when you pull it off.  Coolite is no exception, in fact, when configured properly, it has one of the must full featured grids I have seen on the web, once you get there that is. I will talk about the grid a lot in posts to come, primarily about databinding, and updating.  But today we will focus on grouping.

Grouping is an effect where by a top level is rendered based on data groups, these can be expanded to show the children of that group along with aggregation data.  Today I am going to explain how to take a simple array of data and generate a grid panel which supports grouping.  To begin, here is some sample data we will feed our grid:

var aList = new List()
{
    new Order() { OrderId = 100, ProductId = 3,
        ProductName="Product 3", UnitPrice = 5.00m,
        Quantity = 21 },
    new Order() { OrderId = 100, ProductId = 6,
        ProductName="Product 6", UnitPrice = 1.00m,
        Quantity = 22 },
    new Order() { OrderId = 100, ProductId = 7,
        ProductName="Product 7", UnitPrice = 2.00m,
        Quantity = 23 },
    new Order() { OrderId = 200, ProductId = 3,
        ProductName="Product 3", UnitPrice = 3.00m,
        Quantity = 24 },
    new Order() { OrderId = 300, ProductId = 4,
        ProductName="Product 4", UnitPrice = 4.00m,
        Quantity = 25 },
    new Order() { OrderId = 400, ProductId = 5,
        ProductName="Product 5", UnitPrice = 5.00m,
        Quantity = 26 },
    new Order() { OrderId = 500, ProductId = 8,
        ProductName="Product 8", UnitPrice = 6.00m,
        Quantity = 27 },
    new Order() { OrderId = 500, ProductId = 9,
        ProductName="Product 9", UnitPrice = 7.00m,
        Quantity = 28 },
    new Order() { OrderId = 500, ProductId = 1,
        ProductName="Product 1", UnitPrice = 8.00m,
        Quantity = 29 },
    new Order() { OrderId = 200, ProductId = 2,
        ProductName="Product 2", UnitPrice = 9.00m,
        Quantity = 20 },
    new Order() { OrderId = 700, ProductId = 3,
        ProductName="Product 3", UnitPrice = 1.00m,
        Quantity = 21 },
    new Order() { OrderId = 700, ProductId = 4,
        ProductName="Product 4", UnitPrice = 2.00m,
        Quantity = 22 },
    new Order() { OrderId = 800, ProductId = 4,
        ProductName="Product 4", UnitPrice = 3.00m,
        Quantity = 23 }
};

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Ok, maybe that was a bit excessive, but moving on.  Coolite emphasis a UI pattern I like to call “late loading”. The basics of this pattern is that the main frame of the page should come in first and then the data. This gives the user the impression of a page load and lightens the load. This technique is commonly employed with data heavy applications which utilize a single page architecture and simply move data (usually in JSON) to and from the server using asynchronous calls.

My favorite way of getting data with Coolite are ASP .NET Generic Handlers (.ashx), mainly because they are extremely lightweight and have some very nice uses with GridPanels. This is the code for the ASHX, notice the use of the NewtonSoft JSON Library (this is included with the Coolite download) to serialize the List into JSON and pass it back, also notice the content type definition, to text/json.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DataHandler : IHttpHandler
{
        public void ProcessRequest(HttpContext context)
        {
            List list = Order.GetData();

            context.Response.ContentType = "text/json";
            StoreResponseData data = new StoreResponseData();
            data.Data = JSON.Serialize(list);
            data.Return();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
    }
}

What this will do is return a JSON object to be read by a Coolite ExtJS data store and then bound to a control.  In this case, the data that is being returned can easily be grouped by its OrderId property. We can have Coolite take care of most the heavy lifting for us.  So we will first define our data store:

   1:  <ext:Store ID="store" runat="server" AutoLoad="true"
   2:      AutoDataBind="true" GroupField="OrderId">
   3:      <Proxy>
   4:          <ext:HttpProxy DisableCaching="true" Json="true"
   5:              Method="GET" Url="/DataHandler.ashx" />
   6:      </Proxy>
   7:      <Reader>
   8:          <ext:JsonReader>
   9:              <Fields>
  10:                  <ext:RecordField Name="OrderId" />
  11:                  <ext:RecordField Name="ProductId" />
  12:                  <ext:RecordField Name="ProductName" />
  13:                  <ext:RecordField Name="UnitPrice" />
  14:                  <ext:RecordField Name="Quantity" />
  15:                  <ext:RecordField Name="Total" />
  16:              </Fields>
  17:          </ext:JsonReader>
  18:      </Reader>
  19:  </ext:Store>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Ok there is a lot here so we will go line by line.  First we are defining the Store and saying that we want it to AutoLoad which means a request will be made as the page becomes ready.  The key here is the GroupField definition which is what will format the outgoing result in a way that the GridPanel will use a grouping format to output it.

The connections to the data sources are initiated through Proxies.  The interesting thing here is that you can also define a WriteProxy to handle changes to Store via the Grids inline editing feature.  In this tutorial we are only going to be covering using a ReadProxy:

  • DisableCaching: This prohibits the browser from caching a request.
  • Json: This just ensures that what we get back from the request is JSON, I define this just to be explicit with what is coming back
  • Method: Once again this overloads the default value, and just explicitly states that we wish this request to be carried out over GET
  • Url: The URL of where to make the request to, in this case we are specifying the URL to our custom ASHX

The next part is the Reader definition; this is defining what the JSON object is going to look like on the client side as it is worked with on the client side.  Make sure the values of the Name attributes match their respective property values of on the JSON object coming over.  I mistakenly thought this was the order of serialization, but it is not; they match one to one.  You can optionally define the Type attribute, but for the most part Coolite will infer the types.

With this defined, we are now ready to move onto defining our GridPanel which will actually provide the UI for the user to see.  The definition is rather long, so I will be breaking this apart based on the internal nodes for definition, to start here is the outer structure which has the base property definitions:

<ext:GridPanel ID="gpPanel" runat="server" AutoHeight="true"
    StripeRows="true" StoreID="store" AutoDataBind="true">
</ext:GridPanel>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The important attributes here StoreID (which defines which store this control is associated with), also AutoDataBind which will automatically rebind the grid whenever a change is detected in the associated store.  The other attributes are defined for aesthetic purposes.

The next thing to do is to define how the grid will look without Grouping and what columns will be included, to do this we define a ColumnModel node within the GridPanel:

<ColumnModel runat="server">
    <Columns>
        <ext:Column DataIndex="OrderId" />
        <ext:Column DataIndex="ProductName"
            Header="Product" />
        <ext:Column DataIndex="UnitPrice"
            Header="Price">
            <Renderer Format="UsMoney" />
        </ext:Column>
        <ext:Column DataIndex="Quantity"
            Header="Qty" />
        <ext:Column DataIndex="Total">
            <Renderer Format="UsMoney" />
        </ext:Column>
    </Columns>
</ColumnModel>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

You notice the DataIndex matches the value of the Name attribute from the JsonReader RecordFields.  Understand that the column model defines ONLY what fields from the JSON object you want to display.  The row will STILL contain a reference to the whole JSON object, but this way you can choose to hide fields like PKs (We are doing this with ProductId in this case), but they are still usable when a GridCommand is invoked.

Notice also that we are using the built in Render formatting that is provided by Coolite.  There are quite a few of them that come out of the box, in addition you can define your own.

So if you run this code right now, you will get a simple grid, but you will still not see any groupings, this is because we need to define a GroupingView node for this GridPanel to see the grouping in action.

<View>
    <ext:GroupingView ID="gvGrouping" runat="server"
        ForceFit="true" HideGroupedColumn="true">
    </ext:GroupingView>
</View>

Views are an extremely vital piece to the GridPanel as they can help you define further configuration in terms of function and formatting.  Here we are using the GroupingView which defines how the Grid will look when it is grouping.  We define two attributes for aesthetical purposes: ForceFit and HideGroupedColumn.  ForceFit merely forces the Grid to fit its entire available width, it tends to make the grid look a lot nicer.  HideGroupedColumn hides the column you are grouping on.

Running the code now you will see the GridPanel and your data grouped by the OrderId.  The blocks are expandable and collapsible, and their initial state can be defined on the GroupingView.  The GridPanel offers an immense amount of functionality and can really help you display your data.  It also best demonstrates ExtJs reliance on the “late-load” content loading technique.  Your controls will be rendered and the data will be brought in via Store’s that in most cases will be linked to a web service of some kind.  This data is then placed into either an Array, JSON, or XML, depending on what you define. In our example, we used JSON.  We associated this Store with a Grid via the StoreID attribute of the GridPanel.  In addition, we define the Grouping column for the Store, this is what enables our grouping.

Finally, we use a ColumnModel and View to define how the Grid will look under certain situations.  Remember that you do not have to define a column for all members of your JSON object, only the ones you want to display, sending a row command will still give you access to all the properties defined on the object via the JSON reader.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Advertisements

3 thoughts on “Grouping with Coolite GridPanel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s