Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Create a Plugin to Sencha Touch Ext.form.Slider Class to Show More Info

4.09/5 (4 votes)
25 Aug 2011CPOL3 min read 25.7K  
It adds features to the Sencha Touch standard Ext.form.Slider Class

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Introduction

Sencha Touch Mobile Framework has a Ext.form.Slider class to show a slider for user to pick a value. The interface looks like this in the Height field (click here for demo, you may have to use mobile device, or Chrome or Safari browsers on computers):

1.png

It works great, but I want to to show more information like the minimum value and the maximum value when it is setup, the current value when a user drags the thumb, and the value when a user stops dragging, just like this in fields Price and Weight:

0.png

Background

DevGuyDotOrg did a great job to show the current value when a user drags the thumb (see the following image):

Image 3

Based on this idea, I made an Ext.form.Slider class extension like this:

CSS
Ext.ns('Ext.ux');
Ext.ux.SliderTooltip = new Ext.Panel({
    floating: true,
    height: 30,
    styleHtmlContent: true,
    style: "background-color: #FFF; text-align: center"
});

Ext.ux.Slider = Ext.extend(Ext.form.Slider, {
    listeners: {
        drag: function (theSlider, theThumb, ThumbValue) {
            Ext.ux.SliderTooltip.setWidth(ThumbValue.length * 1.5);
            Ext.ux.SliderTooltip.showBy(theThumb);
            Ext.ux.SliderTooltip.el.setHTML(ThumbValue);
        },
        dragend: function (theSlider, theThumb, ThumbValue) {
            Ext.ux.SliderTooltip.hide();
        },
        scope: this
    }
});

Ext.reg('uxSlider', Ext.ux.Slider);

Though it works well in terms of the tooltip, I prefer a plugin in this case. (There are only a few differences in some respects, see here.)

The Codes

For those who are new to Sencha plugins, please refer to Ref 1, Ref 2 and Ref 3.

First, let's take a look at all the codes for the plugin:

JavaScript
Ext.ns('Ext.ux');
Ext.ux.CustomSlider = Ext.extend(Object, {
    valueTextClass: 'x-slider-value-text',
    valueUnit: '',
    valueUnitPos: 'before',
    tooltipStyle: 'background-color: #FFF; text-align: center',
    showSliderBothEndValue: true,
    sliderEndValuePos: 'under',
    sliderEndValueStyle: 'color: green',

    constructor: function(config){
        Ext.apply(this, config);
        Ext.ux.CustomSlider.superclass.constructor.apply(this, arguments);
    },
    init: function(parent) {
        var me = this;
        parent.on({
            drag: {
                fn: function(slider, thumb, value) {
                    me.sliderTooltip.setWidth(value.length * 1.5);
                    me.sliderTooltip.showBy(thumb);
                    me.sliderTooltip.el.setHTML(value);
                }
            },
            dragend: {
                fn: function (slider, thumb, value) {
                    me.sliderTooltip.hide();
                    me.showSliderValue(this.valueTextEl, slider, thumb, value);
                }
            },
            afterrender: {
                fn: function(component) {
                    me.createSliderToolTip();
                    if (me.showSliderBothEndValue) me.showSliderEndValue(this);
                    if (!this.valueTextEl) {
                        this.valueTextEl = component.getEl().createChild({
                            cls: me.valueTextClass
                        });
                    }
                }
            }
        });
    },
    showSliderValue: function(valueTextEl, slider, thumb, value) {
        if (this.valueUnitPos == 'before') {
            valueTextEl.setHTML(this.valueUnit + value);
        } else {
            if (parseFloat(value) > 1) {
                valueTextEl.setHTML(value + '&nbsp' + this.valueUnit + 's');
            } else {
                valueTextEl.setHTML(value + '&nbsp' + this.valueUnit);
            }
        }
        
        var left = thumb.getEl().getX(),
        thumbWidth = thumb.getEl().getWidth(),
        thumbHeight = thumb.getEl().getHeight(),
        top = thumbHeight / 2,
        textWidth = valueTextEl.getWidth(),
        sliderLength = slider.getWidth();
        
        if (left > sliderLength - textWidth - thumbWidth) {
            left = left - textWidth - thumbWidth / 2;
        } else {
            left = left + thumbWidth / 2;
        }

        valueTextEl.setLeft(left);
        valueTextEl.setTop(top);        
    },
    showSliderEndValue: function(slider) {
        var sliderPosX = slider.getThumb().getEl().getX();
        var minValueEl = slider.getEl().createChild();
        minValueEl.setHTML(slider.minValue);
        minValueEl.applyStyles('overflow:hidden;position:absolute');
        minValueEl.applyStyles(this.sliderEndValueStyle);
        
        var thumbHeight = slider.getThumb().getEl().getHeight();
        minValueEl.setLeft(sliderPosX - 2);
        if (this.sliderEndValuePos == 'above') {
            minValueEl.setTop(-4);                            
        } else {
            minValueEl.setTop(thumbHeight + 2);
        }
        
        var maxValueEl = slider.getEl().createChild();
        maxValueEl.setHTML(slider.maxValue);
        maxValueEl.applyStyles('overflow:hidden;position:absolute');
        maxValueEl.applyStyles(this.sliderEndValueStyle);
        
        var sliderLength = slider.getEl().getWidth();
        var maxTextWidth = maxValueEl.getWidth();
        maxValueEl.setLeft(sliderLength - maxTextWidth - 25);
        if (this.sliderEndValuePos == 'above') {
            maxValueEl.setTop(-4);
        } else {
            maxValueEl.setTop(thumbHeight + 2);
        }
    },
    createSliderToolTip: function() {
        if (! this.sliderTooltip) {
            this.sliderTooltip = new Ext.Panel({
                floating: true,
                height: 30,
                styleHtmlContent: true,
                style: this.tooltipStyle
            });
        }   
    }
});

Lines 1 through 7 are default configurations and can be overwritten when this plugin is plugged into its container and if its constructor is set up in the above way.

All the features the plugin will add or modify lie in the function init(). In this function, it is very important to know that in the parent.on({...}) code block, pronoun this refers to the plugin's parent (which is the Slider class), while me refers to the plugin itself.

In the afterrender event handler, a tooltip panel is prepared by function createSliderToolTip for the drag event to use. Function createSliderToolTip creates different instances of tooltip for different plugin instances, and the tooltip style can be changed by changing the plugin's tooltipStyle option, see screenshots below:

2.png    3.png

If we don't want different tooltip styles, then we can re-write the function to create only one tooltip panel on one page and make a little bit gain in performance, like this:

JavaScript
createSliderToolTip: function() {
	var sliderTooltip = Ext.getCmp('sliderTooltip');
	if (! sliderTooltip) {
		sliderTooltip = new Ext.Panel({
			floating: true,
			height: 30,
			id: 'sliderTooltip',
			styleHtmlContent: true,
			style: this.tooltipStyle
		});
	}
	this.sliderTooltip = sliderTooltip;        
}

When changed in this way, the tooltip panel will take the first plugin's style configuration (in this case, only the tooltipStyle option in Price field takes effect. That in Weight field will be simply ignored).

It is also very important that the CSS class for the slider value text should be defined at least with the position attribute being absolute, so that we can move and align the text to where we want it to be. My definition is:

CSS
.x-slider-value-text {
	overflow:hidden;
	position:absolute;
	z-index:9999;
	color:rgb(255,55,55); 
}

With this in place, we can position those elements that are created in the afterrender event handler, such as valueTextEl, minValueEl and maxValueEl. Functions showSliderValue and showSliderEndValue deal with the positioning and styling hassles involved.

Using the Code

We can use the plugin in this way:

CSS
items: [{
		xtype: 'sliderfield',
		minValue: 0,
		maxValue: 10000,
		increment: 0.5,
		id: 'price',
		plugins: [new Ext.ux.CustomSlider({
			valueUnit: '$',
			tooltipStyle: 'background-color: #AAA; text-align: center',
			showSliderBothEndValue: true,
			sliderEndValuePos: 'above'
		})],
		label: 'Price ($)'
	}, {
		xtype: 'sliderfield',
		increment: 0.5,
		id: 'weight',
		plugins: [new Ext.ux.CustomSlider({
			valueUnit: 'carat',
			tooltipStyle: 'background-color: #FFF; text-align: center',
			valueUnitPos: 'after',
			showSliderBothEndValue: true
		})],
		label: 'Weight (carat)'
	},
	...
}]
...

From this example, we can see that we can customize the options and style, like value unit and its position, tooltip style, show or not show the slider's minimum and maximum values.

If we don't like the plugin to work, just remove it from the plugins option if we have more than one plugin in place, or take away the plugins option completely.

Points of Interest

Writing a plugin is fun. It is not so difficult as one may think.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)