Filter dropdown

How to create a custom dropdown filter for your table.

Notes

  • The base table in the example below is a clone of the showcase example to demonstrate that the dropdown keeps the filter state across various table operations such as filtering, sorting, paging.
  • If you are upgrading to version 3.1.0 or higher from a 3.0.x version please pay special attention to step 6 as the method to register the custom component has changed.

Step 1: Inheriting from the base filtering component

All core objects or "classes" in FooTable are derived from the FooTable.Class object. This object exposes a single method called extend which allows you to create a whole new class from a base class (inherit) or to simply override a single method on an existing class. In this example we will be inheriting the base FooTable.Filtering component, modifying it and then replacing it with our modified version.

FooTable.MyFiltering = FooTable.Filtering.extend({ // inherit the base class
	construct: function(instance){ // override the default constructor
		this._super(instance); // call the base constructor
	}
});

Step 2: Add custom properties for the dropdown

To make the code more maintainable going forward and easier to understand we will add some properties to our custom filtering component to hold the available options, the default option, and the jQuery wrapper around our dropdown.

FooTable.MyFiltering = FooTable.Filtering.extend({
	construct: function(instance){
		this._super(instance);
		this.statuses = ['Active','Disabled','Suspended']; // the options available in the dropdown
		this.def = 'Any Status'; // the default/unselected value for the dropdown (this would clear the filter when selected)
		this.$status = null; // a placeholder for our jQuery wrapper around the dropdown
	}
});

Step 3: Create the dropdown and append it to the table

To create the actual dropdown element and append it to the table we will override the $create method and generate our markup as required. The base filtering component exposes the property $form which is the jQuery wrapper for the form that contains the default search input. Using this we can simply create our dropdown and append it as required.

$create: function(){
	this._super(); // call the base $create method, this populates the $form property
	var self = this, // hold a reference to my self for use later
		// create the bootstrap form group and append it to the form
		$form_grp = $('<div/>', {'class': 'form-group'})
			.append($('<label/>', {'class': 'sr-only', text: 'Status'}))
			.prependTo(self.$form);

	// create the select element with the default value and append it to the form group
	self.$status = $('<select/>', { 'class': 'form-control' })
		.on('change', {self: self}, self._onStatusDropdownChanged)
		.append($('<option/>', {text: self.def}))
		.appendTo($form_grp);

	// add each of the statuses to the dropdown element
	$.each(self.statuses, function(i, status){
		self.$status.append($('<option/>').text(status));
	});
}

Step 4: Handing the dropdown change event

At this point we have the dropdown appearing next to the search input but we still need to handle the change event we bound to above and actually apply the filter. To do this we will use the addFilter, removeFilter and filter methods.

_onStatusDropdownChanged: function(e){
	var self = e.data.self, // get the MyFiltering object
		selected = $(this).val(); // get the current dropdown value
	if (selected !== self.def){ // if it's not the default value add a new filter
		self.addFilter('status', selected, ['status']);
	} else { // otherwise remove the filter
		self.removeFilter('status');
	}
	// initiate the actual filter operation
	self.filter();
}

Step 5: Hooking into the draw operation

So now we have the dropdown, the filter is being added or removed in the change event as required but we also need the dropdown to be able to handle having it's value set through the options. To do this we hook into the draw method and check for the 'status' filter and if one exists update the dropdown as required.

draw: function(){
	this._super(); // call the base draw method, this will handle the default search input
	var status = this.find('status'); // find the status filter
	if (status instanceof FooTable.Filter){ // if it exists update the dropdown to reflect the value
		this.$status.val(status.query.val());
	} else { // otherwise update the dropdown to the default value
		this.$status.val(this.def);
	}
}

Step 6: Global or instance specific

The final step is to simply decide whether the custom filtering should be applied to all tables in the page or just those specified by you. To register your custom filtering component for all tables in the page you need to use the FooTable.components.register() method as seen below.

FooTable.components.register('filtering', FooTable.MyFiltering);

If you want to use your custom filtering component for only a single table or specific tables in a multi-table page then you need to make use of the components option. To do this you would provide the option as below when initializing the plugin.

$('.table').footable({
	components: {
		filtering: FooTable.MyFiltering
	}
});

Finished!

That's it! We now have a custom filter dropdown that is fully integrated with the rest of the filter functionality. The full code can be seen below.

FooTable.MyFiltering = FooTable.Filtering.extend({
	construct: function(instance){
		this._super(instance);
		this.statuses = ['Active','Disabled','Suspended'];
		this.def = 'Any Status';
		this.$status = null;
	},
	$create: function(){
		this._super();
		var self = this,
			$form_grp = $('<div/>', {'class': 'form-group'})
				.append($('<label/>', {'class': 'sr-only', text: 'Status'}))
				.prependTo(self.$form);

		self.$status = $('<select/>', { 'class': 'form-control' })
			.on('change', {self: self}, self._onStatusDropdownChanged)
			.append($('<option/>', {text: self.def}))
			.appendTo($form_grp);

		$.each(self.statuses, function(i, status){
			self.$status.append($('<option/>').text(status));
		});
	},
	_onStatusDropdownChanged: function(e){
		var self = e.data.self,
			selected = $(this).val();
		if (selected !== self.def){
			self.addFilter('status', selected, ['status']);
		} else {
			self.removeFilter('status');
		}
		self.filter();
	},
	draw: function(){
		this._super();
		var status = this.find('status');
		if (status instanceof FooTable.Filter){
			this.$status.val(status.query.val());
		} else {
			this.$status.val(this.def);
		}
	}
});

Followed by either the below to register the component globally for all tables.

FooTable.components.register('filtering', FooTable.MyFiltering);

Or the below to register it for just a single table.

$('.table').footable({
	components: {
		filtering: FooTable.MyFiltering
	}
});