New version Angular 9

Angular JQueryLess Server Grid Filter and Combo Directive

In this section we will discover

The samples can be run with Firefox, in general or by running, into the directory,


The source for this part is here:



Data service

We should add the handling of filters on the customersDataService. Since our API accept queryes of type "field=value & field1=value1" we willl consider a filter with the following values

var filter = {

When a filter value is empty or undefined we will do nothing! The "for prop in filter" seeks all of the properties of the filter and loads only the defined and not empty properties. Then the filter is converted to JSON to be added to the query string.

        var realFilter = {};
        var didSomething = false;
        for(var prop in filter){
            var value=filter[prop];
            if(value!=undefined && value !==null && value.length>0){
                realFilter[prop] = value;
                didSomething = true;
        var start = currentPage * pageSize;
        var end = start + count;
        var result = "/customers?range=["+start+","+end+"]";
        return result;

The generic controller

We will add a new variable for the list controller

    $scope.filter = {};

We will need to add two functions on the service, to reset the search and to do the search

        $scope.resetSearch = function(){
            $scope.filter = {};
        $ = function(){

At the same time we will add to the data service function the filter, as parameter

    var address= dataService.list(requiredPage,$scope.pageSize,$scope.pageSize+1,$scope.filter);

The list template

On the list template we will add two input fields connected to the $scope.filter. a reset button and a search button:

        <div class="form-group">
                <input class="form-control input-sm" type="text" ng-model="filter.first_name" placeholder="First Name"/>
        <div class="form-group">
                <input class="form-control input-sm" type="text" ng-model="filter.last_name" placeholder="Last Name"/>
        <div class="form-group">
            <div class="input-group">
                <a class="btn btn-default btn-sm" ng-click="search()"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></a>
                <a class="btn btn-default btn-sm" ng-click="resetSearch()"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span></a>

At this point

The combo directive

One thing that is often seen is a way and apparatus to change the page size of the grid. We setted a scope variable for that: the pageSize, with two-ways binding.

A combo box/select exist for angular-ui but is dependant on JQuery, here is a pure angular implementation

Directive css

We will add to the common css a class to hide the buttons

.sgDropdownVisible{ display:block; }

Directive Template

To draw the combo we will use the standard bootstrap css, creating a "app/common/dropdown.html" template.

We use the additional "inDropdown" class to check if a click was made inside the dropdown. We will show a specific placeholder defined on the directive. The class "menuVisible" described befor will be used for when the dropdown is open. When selecting an item the dropdown will call the "select(item)" callback.

The check with the item[value] is used to allow the usage of index/label pairs inside the combo, when we have an index for the item and a descriptive label.

<div class="dropdown inDropdown">
    <button class="btn btn-default btn-sm dropdown-toggle inDropdown" type="button" aria-expanded="true"
        <span class="inDropdown" 
            ng-if="isPlaceholder" class="placeholder inDropdown" >{{placeholder}}</span>
        <span class="caret inDropdown"></span>
    <ul class="dropdown-menu inDropdown" role="menu" ng-class="{'sgDropdownVisible':listVisible}">
        <li ng-repeat="item in list" role="presentation" class="inDropdown"><a role="menuitem" class="inDropdown" tabindex="-1" ng-click="select(item)" >
            {{value !== undefined ? item[value] : item}}</a></li>

### Dropdown directive 

First we define a way to use a specific template for the dropdown as we did for the dialog. In which we 
add a flag to verify if a opened dropdown exists. Is normal that only one dropdown is opend at a given

    return {
        //The default dropdown template
        dropdownTemplate : "sgDialogTemplate.html",

Then goes the directive. We use an element directive, with a placeholder, a list of items to show and a binded selected variable. Optionally we could select a key and a value. If they are not present the list[index] item is taken both as key and value.

Finally we add an optional function to invoke when the selection is changed (note the & and ? markers on the binding.

We get the template as we did for the dialog with $http.get and $compile, association to the scope, Then we add an event handler on the root scope that will hide the dropdown when invoked.

$rootScope.$on("documentClicked", function(inner, target)

Other than this we will watch the "selected" variable and when it changes we change the value of the current item and invoke the eventual "on change" callback.

We have sevarl internal variables,

The majority of the code is made to handle the two ways to use the combo:

We will create an "E" (element) item. The parameters will be

The item will require the presence on the directive itself of the directive "ng-model"

        function($rootScope,$http,sgDropdownConfig,$templateCache,$compile) {
    return {
        restrict: "E",
        //Create an isolated scope
        scope: {
            //When nothing is selected
            placeholder: "@sgPlaceholder",
            //Items to show
            list: "=sgList",
            //Allow null
            nullable: "=?sgNullable",
            //Optional key field
            key: "@?sgKey",
            //Optinal value field
            value: "@?sgValue",
            //To call on selecton changed
        require: 'ngModel',

We use an isolated scope. We can then declare every variable we need on the scope. The object passed into the require is passed as the 4th parameter on the link function.

NOTE: When requiring more than one thing (like both form and ngModel) we will declare like this
    require: ['form','ngModel'],
And the 4th parameter will be of the format

First the parameters are checked, then the various functions are created:

        link: function(scope,element, attrs,model) {
            scope.selectedItem = undefined;
            scope.selectedValue = undefined;
            scope.listVisible = false;
            scope.isPlaceholder = true;
            //Initialize the required attribute
            var required = false;
                required = true;
            var nullable = false;
                nullable = scope.nullable;
            // If key is specified even value must be specified and vice-versa
            if( (scope.key && !scope.value) || (!scope.key && scope.value) ){
                throw "sg-key and sg-value must be both/none declared! in sg-dropdown element!";
            //When selection 
   = function(item) {
                scope.listVisible = false;
                var newValue = undefined;
                    newValue = scope.key?item[scope.key]:item;
            //Verify that the item is selected
            scope.isSelected = function(item) {
                if(scope.key) return item[scope.key] === scope.selectedItem[scope.key];
                return item === scope.selectedItem;

            //Show the dropdown list
   = function() {
                scope.listVisible = !scope.listVisible;
                sgDropdownConfig.dropdownOpened = scope.listVisible;

We will need then to watch for changes on the currently selected item, or better on the model.$modelValue.

This function will handle setting the dirty flag on the model (with model.$dirty), then if the field is required, the validity of the model will be change through the model.$setValidity.

The parameter of setValidity are the validity constraint name, and a boolean telling if the value is correct or not

Finally the connected values are changed, for example the content of the currently selected item, and the optional on change function is called.

            //Watcher function for the selected value
            var selectValue = function(newValue,prevValue) {
                var matching = undefined;
                //Set the model direty
                //Set the validation option
                    if(!newValue && !nullable){
                //If no list is defined no other operations are needed
                if(!scope.list) return;
                //Find matching element
                for(var i=0;i<scope.list.length;i++){
                    var item = scope.list[i];
                            matching = item; 
                        if(item == newValue){
                            matching = item;
                //Select the items
                    scope.isPlaceholder = true;
                    scope.selectedItem = undefined;
                    scope.selectedValue = undefined;
                    scope.isPlaceholder = false;
                    scope.selectedItem = item;
                    scope.selectedValue = scope.value?item[scope.value]:item;
                //Invoke the selChange
                if(scope.selChange && attrs['sgChange']){

Another thing we need is closing the dropdown when someone click outside the dropdown anywhere. If a dropdown is opened the documentClicked event is broadcasted to all the scopes of the application and is intercepted by the directive that closes the dropdown.

Inside the directive this will be handled by an event handler

            //Clicking anywhere out of the item will close the dropdown
            $rootScope.$on("documentClicked", function(inner, target) {
                if(!scope.listVisible) return;
                scope.listVisible = false;
                if (!scope.$$phase) {

While a global service will handle the registration of the event

    //loosely based on
    //Handle the click on the rest of document to close the dropdown
    angular.element(document).on("click", function(e) {
        if(!sgDropdownConfig.dropdownOpened) return;
        if('inDropdown')>0) return;
        $rootScope.$broadcast("documentClicked", angular.element(;

Using the combo

Now into the list.html we will add after the filters section the following part. Into the sg-list we could use a variable or a function or directly an array. This way changing the combo value will change the pageSize with a combo separated from the standard one in ngGrid

    <div class="form-group">
        <sg-dropdown sg-placeholder="Size..." sg-list="[10,25,50,100]" ng-model="pageSize"></sg-dropdown>

The grid directive

In the grid directive we setted the watch on pageSize, now it will become useful!! It would reload the data when the page size changes!!

        return scope.sgPageSize;

Last modified on: May 02, 2015