Ext

Building a Rating Widget with Ext Core 3.0 Final and Google CDN

June 10, 2009 by Tommy Maintz

We are very proud to announce the final release of Ext Core under the MIT license. Your feedback was invaluable. Thank you for all the bugs reported and test cases created. For those of you who are new to Ext Core, we suggest you read the previous blog post about the all the features and examples that we released as part of the beta. You can find a list of changes and fixes we made for the final here.

For this post we will leverage the power of Ext by creating and dissecting a useful star rating example. We hope to share some of the general best practices behind creating unobtrusive, reusable code with Ext Core to liven up your pages.

Making a Splash

Including Ext Core on your site is easier than ever. We are honored to share with the community that Ext Core is now available via the Google AJAX Library API. Many thanks to Ben Lisbakken at Google for working with us to make this a reality.
//any of these will work ;)  <script type="text/javascript" src="....
http://ajax.googleapis.com/ajax/libs/ext-core/3.0/ext-core.js
http://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core.js
http://ajax.googleapis.com/ajax/libs/ext-core/3.0/ext-core-debug.js
http://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core-debug.js
Alternatively, you can use Google AJAX API Loader's google.load() method:
google.load('ext-core', '3');
google.load('ext-core', '3', {uncompressed : true});

Getting Started

Ext Core is a perfect fit for adding behavior to existing HTML. When designing a widget, having a markup structure that provides graceful degradation is an added plus. For this example, we will be using radio buttons. We can "group" the elements to specify which radio buttons are part of the control. It could look something like the following:
<div id="rating1">
    <input type="radio" name="rating1" value="1" title="Very poor">
    <input type="radio" name="rating1" value="2" title="Not that bad">
    <input type="radio" name="rating1" value="3" title="Average">
    <input type="radio" name="rating1" value="4" title="Good">
    <input type="radio" name="rating1" value="5" title="Perfect">
</div>
This markup allows user to have the ability to rate an item even without the fancy stars and additional functionality that we have in mind. Take notice that this simple markup contains powerful information like the name, value and title for each item which we can reuse with our rating widget.

The API

One of the most important aspects of building reusable code is providing your developers a powerful API. Our aim here is to allow developers to progressively enhance and convert the markup into a star rating with a simple API. In this case we will need the element that wraps around the radio controls, and some optional configuration to customize the behavior of the widget. Following the Ext tradition we will provide these configuration options in the form of an object literal. A possible API for our widget could look like this:
//Keep it simple
new Ext.ux.Rating('rating1', {
    showTitles: true
});

Ext.util.Observable

So now that we know how we want to use our component, lets go ahead and actually look at some details on how to write it! In our previous post we mentioned that Ext Core allows you to write neatly structured object-oriented code. Whenever you want to create a piece of functionality, you should try to bundle it into a separate class. In most cases you will need to be able to listen for events on instances of your class. Ext provides a power class, the Ext.util.Observable class, to springboard your development. This is the same class that almost all classes in Ext JS extend from! Our basic shell for our rating plugin could look something like this:
Ext.ns('Ext.ux');
Ext.ux.Rating = Ext.extend(Ext.util.Observable, {
    // Configuration default options
    showTitles: true,
 
    // Our class constructor
    constructor : function(element, config) {
        Ext.apply(this, config);      
        Ext.ux.Rating.superclass.constructor.call(this);     
 
        this.addEvents( 'change');  
 
        this.el = Ext.get(element);
        this.init()
    }
});
For those of you familiar with Ext JS will recognize this pattern. In this piece of code we have created a namespace to put our class into using the Ext.ns() function. Then we create our class using the Ext.extend method with Ext.util.Observable as the base class. We define some configuration options with default values and then set up our constructor method which will be called when we create a new instance of our class. We also define some custom events and wrap the element passed to the constructor with an Ext.Element instance. We use the Ext.get() flexible nature to have support of passing an id string, DOM Element or Ext.Element to our constructor.

Reaching the stars

It is time to think about the things we need to get our widget working. First we want to replace the radio buttons with our stars, we will need to store the values and titles for each star, we want to create a hidden input to put the current value in and finally we need to set up event listeners to listen for mouse hovers and clicks.
init : function() {
    var me = this; 
 
    // Some arrays we are going to store data in
    this.values = [];
    this.titles = [];
    this.stars = [];
 
    // We create a container to put all our stars into
    this.container = this.el.createChild({
        cls: 'ux-rating-container ux-rating-clearfix'
    });
 
    // We use DomQuery to select the radio buttons
    // Then we can loop over the CompositeElement using each
    this.radioBoxes = this.el.select('input[type=radio]');
    this.radioBoxes.each(this.initStar, this);
 
    // We use DomHelper to create our hidden input
    this.input = this.el.createChild({
        tag: 'input',
        type: 'hidden',
        name: this.name,
        value: this.values[this.defaultSelected]
    });
 
    // Lets remove all the radio buttons from the DOM
    this.radioBoxes.remove();
 
    if(this.disabled) {
        this.disable();
    } else {
        // Enable will set up our event listeners
        this.enable();
    }
}

Creating Stars - using DomHelper and accessing the DOM from Ext Element

 
initStar : function(item, all, i) {
    // We use the name and disabled attributes of the first radio button 
    if(i == 0) {
        this.name = item.dom.name;
        this.disabled = item.dom.disabled;		
    }
 
    // Saving the value and title for this star     
    this.values[i] = item.dom.value;
    this.titles[i] = item.dom.title;
 
    // Now actually create the star!
    var star = this.container.createChild({
        cls: 'ux-rating-star'
    });
 
    // Save the reference to this star so we can easily access it later
    this.stars.push(star.dom);
},

Enable and Select Stars - listening for events, using the target of an event and firing custom events

 
enable : function() {
    // ... some code missing here ...
 
    // We will be using the technique of event delegation by listening
    // for bubbled up events on the container       
    this.container.on({
        click: this.onStarClick, 
        mouseover: this.onStarOver,
        mouseout: this.onStarOut,
        scope: this,
        delegate: 'div.ux-rating-star'
    });        
},
 
onStarClick : function(ev, t) {
    if(!this.disabled) {
        this.select(this.stars.indexOf(t));
    }
},
 
select : function(index) {
    // ... some code missing here ...
    else if(index !== this.selected) {
        // Update some properties           
        this.selected = index;
        this.value = this.values[index];
        this.title = this.titles[index];
 
        // Set the value of our hidden input so the rating can be submitted			
        this.input.dom.value = this.value;
 
        // the fillTo() method will fill the stars up until the selected one
        this.fillTo(index, false);
 
        // Lets also not forget to fire our custom event!         
        this.fireEvent('change', this, this.values[index], this.stars[index]);  
}

Filler Up - dom manipulation (adding classes)

 
fillTo : function(index) {
    var cls = 'ux-rating-star-on';
 
    // We add a css class to each star up until the selected one   
    Ext.each(this.stars.slice(0, index+1), function() {
        Ext.fly(this).addClass(cls);
    });
 
    // And then remove the same class from all the stars after this one
    Ext.each(this.stars.slice(index), function() {
        Ext.fly(this).removeClass(cls);
    });      
}

We won't discuss all the details since most of it is pretty straightforward, but the final product should give you a general idea of how to use the basic functionality available in Ext Core to tie together all the missing pieces.

Wrapping it up

In this example we used the following cross-browser compatible functionality available in Ext core:
  • Classical Inheritance Class System
  • Observable Class
  • DomQuery
  • DOM manipulation and traversal
  • Event handling
  • Markup generation

Ext Core makes it fun to write code, and helps you create clean, well-structured classes using a set of cross-browser abstractions on the existing browser API's. For those of you who want to use it or are just interested in seeing the completed work, we have included a version of the widget in the Ext Core Final build. You can see the working widget embedded in the post below:

The example page illustrating this widget can be found here.

Final words

We hope that this library will find its way into many of your dynamic web pages and make your lives as web developers easier and more enjoyable. We are looking forward to seeing the great things you will create using it. We are always looking for ways to make this library better and we think the best way to do that is by listening to your suggestions. So, don't be shy and tell us what you think.

44 Responses to “Building a Rating Widget with Ext Core 3.0 Final and Google CDN”

  1. Sim

    Very nice and exciting.

  2. Jacky

    Air API document will be nice. :)

  3. David Davis

    Great article Tommy!

    Thanks Ben Lisbakken for adding ext-core to the Google Ajax Libraries API.
    Issue 233 can be closed: http://code.google.com/p/google-ajax-apis/issues/detail?id=233

  4. Niko

    Great work! Glad to see Ext Core join in ajax libs family of Google CDN. :)

  5. isoloist

    It’s nice

  6. Nils Dehl

    Thank you Tommy!

    Nice Example and gratulation to the stable version.

  7. Chris Dawes

    Cool… adding it to extjs.tv build two now… nice.. really nice.

  8. Joe

    Yes! What great news – ExtJS Core rocks!

  9. Jay Garcia

    Awesome post Tommy.

  10. Ian

    Looks good but does not seem to work on chrome

  11. tester

    @Ian

    I tested it on Chrome v2.0.181.1, works pretty good!

  12. Boubalou

    I don’t see the details related to Google CDN in this blog post.

    Was it forgotten or do I have to understand what the title mean directly? ;)

    Boub

  13. Jonathan

    @Boubalou – read the part in the beginning of the post – Making a Splash.

    Awesome work Tommy. Very impressive!

  14. Praveen

    Ext rocks as always. Now, it’s part of google API infrastructure, making it even more awesome.
    However, just any good widgets, DOM manipulation is only one part. One must also master CSS concepts thoroughly before creating super slick widgets.

  15. Tommy Maintz

    Thanks for the great response guys! Turns out Safari/Chrome have an opacity 1 issue, so changed the reset el to get opacity 0.99 which fixed it in both browsers.

  16. Ext Core 3.0 Final版がリリースされました。 | Ajaxサンプル開発者ブログ

    [...] Ext JS公式ブログより。 [...]

  17. Donovan

    Was the rating example inspired by the GQuery GWT demo at Google IO? or is there a general example that inspired both?

    Related, I was actually wondering if Ext GWT contained analogous DOM manipulation code present in ext-core (making it a competitor with GQuery mentioned above) or if it was just the widgets.

  18. zc

    功能很好,很高兴看见Ext Core 加入了Google CDN

  19. hb562100

    很不错,接下来我会仔细看看 ext.3 带来的更大便利和性能。

  20. EXTJS.CN

    太强了!

  21. Greg

    The Tabs demo example is broken in FireFox. The tab contents properly switch, but the tabs-button-panel does not. When you click on a tab, the third tab gets the tab-show class no matter which tab you click on. I’m on FF3 on a Mac, but I’ve also seen this happen on FF on a PC.

    It worked O.K in the beta.

  22. Ian

    @tester – on chrome 2.0.172.31 you do not seem to see the reset button but does show on IE & firefox

  23. noel

    This is why open source is so awesome…

  24. Ext-core ready to go | rapid-DEV.net

    [...] Thanks to all for the requests to add ext-core to our Libraries API, and big thanks to Ext JS for providing their library so openly! For more information, head over to their blog post. [...]

  25. myext

    很好!http://www.myext.cn

  26. jeanjean

    hello

  27. kabin

    kabin üretiminde üreticiyiz.

  28. Magnetic Assemblies

    It looks great.thanks

  29. Crysfel

    Thanks for this tut, it is really good indeed!

  30. juziku

    Good!!

  31. 聚资库

    很好!!!

  32. Gary Gilbert

    Great little widget guys, can’t wait to see the final version!

  33. Mike

    I was swindled on craigslist by some jerkwad using a wireless number. Jerkwad was shocked when I got his addy info and paid a visit lol!

    Phone Search

  34. extjs

    구글에서 땡겨쓰게끔 지원?

  35. frank

    Widget连“渐进增强”的概念都涉及了,例子复杂点些,anyway,谢谢~这是专业的widget,谢谢提供!

  36. sikiş

    http://extjs.com/blog/2008/11/24/extplayer-air-and-ext/#comment-71248

  37. sikiş

    I am grateful to you for this great content.

    aöf

  38. tolga

    amazing

    http://www.feneronline.net/video/

  39. Burn Your To Do List

    Awesome. you did Great!

  40. Sap

    I’m found some bug. There should be some check in “enable” function, because if function “enable” calls more then one time the code “this.container.on({click: this.onStarClick,…” execute more than one time too. So if user click on star, script has multiply calls of the click event. I resolve this problem by the use of “if(!this.disabled){return;}”, see code:

    enable : function() {
    if(!this.disabled){return;}
    ……

    P.S. thank you for rating code :)

  41. mj

    hi,
    so to make out the rating system i jus need the codes above and that’s all??

  42. Henry

    Thanks a lot!! ;)

  43. krzysztof

    looks good, need to try it with Chrome.

  44. krzysztof

    Tried with Chrome – works good.

Leave a Reply

To prove that you're not a bot, please answer this question:



© 2006-2009 Ext, LLC