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

JavaScript Filtering with JSON and Filter Management

4.80/5 (6 votes)
16 Sep 2010CPOL2 min read 137.9K   1K  
Part 1 of creating a dynamical filtering mechanism in C#
xFilter.jpg

Introduction

This article will explain a simple client-side filtering framework that can be implemented in any page to obtain easy-to-use filters.

Background

I was looking for some controls that could help me create my application. The control which suited me the most was jqGrid - a jQuery plugin. jqGrid displays a JSON data source in tabular format, along with filtering and sorting. For this, we must define a columns model (see example) - in JSON format of course:

JavaScript
colModel:[
   	{name:'id',index:'id', width:60, sorttype:"int"},
   	{name:'name',index:'name', width:100, editable:true},
   	{name:'note',index:'note', width:150, sortable:false}		
  ]

Using the Code

Unfortunately, the filtering capability of the jqGrid is not as complex as I need. A filter is a JSON object with the following format type:

JavaScript
{ "groupOp": "and",
      "rules": [
        { "field": "name", "op": "eq", "data": "Romania" }, 
        { "field": "id", "op": "le", "data": "1"}
      ]
}

It's basically a set of rules grouped together using a logical operator: "OR" or "AND". For a simple search, this filter can be enough, but anything more complex requires a tree of such groups. So let's add an array of groups to this structure. Now we have the tree structure that we need and still maintain the support for normal jqGrid filters. Suppose we would need a filter like this:

JavaScript
(name == "John" AND age > 30) OR (name == "Doe" AND age < 30) OR name == "Joe"

Note first that between groups and also between groups and rules, we must have the same logical operator, otherwise we will have to isolate them in a subgroup. The above formula will map into:

JavaScript
{ "groupOp": "or",
      "groups": [
		{ "groupOp": "AND", 
		  "rules": [
		      { "field": "name", "op": "eq", "data": "John" },
			  { "field": "age", "op": "gt", "data": "30" }
		  ]
		},
		{ "groupOp": "AND", 
		  "rules": [
		      { "field": "name", "op": "eq", "data": "Doe" },
			  { "field": "age", "op": "lt", "data": "30" }
		  ]
		}
	  ]
      "rules": [{ "field": "name", "op": "eq", "data": "Joe" }]
}

As I said, we have two types of operators: group operators and rule operators. The only group operators are "OR" and "AND". As rule operators, we have: eq (equal to), us (different to), lt (lower than), etc. as well as string operators: cn (contains), sw (starts with), etc. The above can be easily converted into a syntax closer to the JavaScript, by using functions:

JavaScript
(eq(item.name, "John") && gt(item.age, "30")) || 
	(eq(item.name, "Doe") && lt(item.age, "30")) || eq(item.name, "Joe")

Note that all entity fields have are prefixed with "item.". Let's define the necessary JavaScript functions:

JavaScript
function eq(d1, d2) { return d1 == d2; }
function lt(d1, d2) { return d1 < d2; }
function gt(d1, d2) { return d1 > d2; }

Now we can loop through a JSON object and evaluate the above formula.

JavaScript
var jsonItems = [
	{"name": "John", "age": "18"},
	{"name": "Amanda", "age": "21"},
	{"name": "Dave", "age": "31"}
];

var expression = '(eq(item.name, "John") && gt(item.age, "30")) || 
	(eq(item.name, "Doe") && lt(item.age, "30")) || eq(item.name, "Joe")';

var newJsonItems = [];
for(var index = 0; index < jsonItems.length; index++){
	var item = jsonItems[index]; // define the item that will be evaluated
	if(eval(expression) == true){
		newJsonItems.push(item);
	}
}

So far we have logic, but we need a web user interface to cofigure the filter. Luckily I wrote a JS class that does this: xFilter (see attachment). To use it, you only need to provide the DOM parent element and columns model.

JavaScript
var colModel = [
	{ "name": "id"},// we only need this field, jqGrid requires the rest
	{ "name": "name"},
	{ "name": "note"}
];
var items = [
	{ "name": "Romania", "id": "1", "note": "ro" },
	{ "name": "Franta", "id": "2", "note": "fr"},
	{ "name": "Anglia", "id": "3", "note": "uk"},
	{ "name": "Italia", "id": "4", "note": "it" },
	{ "name": "Germania", "id": "5", "note": "ge" }
];
	
var f = new xFilter(document.getElementById("fil"),
{
	columns: colModel,
	onchange: function() {
		document.getElementById("message").innerHTML =	
			this.toUserFriendlyString();

		var resItems = f.Apply(items);
		var s = "";
		for (var i = 0; i < resItems.length; i++) {
			s += "," + resItems[i].name;
		}
		document.getElementById("message").innerHTML = s;
	}
});

As you can see, the class has the "onchange" event that is triggered whenever a change to the filter occurs. Of course, one can use the method "Apply" - whose logic is presented above - to filter a JSON object.

Conclusion

Hope you enjoyed this. The next article will take this model on the server side (C#), where the filters we've created will become expression trees that can be used with LINQ implementations.

Link

Part 2 of creating a dynamical filtering mechanism in C#

License

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