Skip to content

Welcome

Welcome to my blog about the software tools / techniques I have used.

The first posts, were originally hosted in a variety of places,  &  I thought it would be nice to bring them all together.   Its mainly a journal of  some of the things I have worked on, that might be useful to me in the future.  If it helps anyone else, then great.

I have been developing Web Applications with Domino / Lotus Notes since the 90s, mainly in areas where workflow & security are important. Before that I worked on financial systems with Foxpro, Oracle & PL/1.

In parallel with Domino, recently I have used Java , PHP, C# .NET, Sharepoint, JQuery , HTML5 and a bit of  Ruby On Rails.

Security is a bit of a hobby horse of mine – so its bound to crop up once or twice.  If you’ve found this site, please do check out OWASP  The Open Web Application Security Project – it has some great FREE stuff that helps build more secure applications.

Richard in Warwick.

Oauth2 to a custom api with basic authentication in System.Net.WebClient

My .NET app needed to consume a 3rd party REST Api , which had its own Basic Authentication  .NET’s  MVC samples come with predefined libraries for Google or Facebook Authentication, which are not easy to extend to other authenticators.

I also had a look at DotNetOpenAuth,  & that too was problematic.

So I went to the 3rd party’s API web page & used Fiddler to workout what was going on in the API ( actual endpoints, request header values etc)  & reproduced that in a console App using System.Net.WebClient  & Newtonsoft.JSON

  • The system  uses BASIC Authentication to return a JSON web token.
  • That token was then added to the getData url to get data

Sure, this isn’t production grade, but its a started for 10.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Runtime.Serialization;
using System.IO;
using Newtonsoft.Json;

namespace ConsoleOauth
{
 class Program
 {
 
 static void Main(string[] args)
 {
//Set creds
 string uid="MyAppId";
 string upass="MyAppPass";
//set Token Generator URL
 string t_Url="https://remotesite/api/oauth/token?client_id="+uid+"&grant_type=client_credentials";
WebClient Wclient = new WebClient();
WebClient Wclient2 = new WebClient();
string authInfo = uid + ":" + upass;
// credentials are added to the Authorization header as "Basic "plus Base64 encoded Id & password..  over SSL 
// We also need to say we'll accept JSON
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
Wclient.Headers["Authorization"] = "Basic " + authInfo;
Wclient.Headers["Accept"] = "application/json";
//Download the token from the token_generator url
var t_json = Wclient.DownloadString(t_url);
// deserialize the json into an object of type J_Tok ( JSON TOKEN) so its useable
J_tok t_obj = JsonConvert.DeserializeObject<J_tok>(t_json);

// Authorised Now get data

// Build GetData url & add the web token to the end
string P_url = "https://remoteSite/api/getData/123?coll=xyz&access_token="+t_obj.accesstoken;
//Set the headers 
Wclient2.Headers["Accept"] = "application/json";
Wclient2.Headers["Content-type"] = "application/json";

//Get the Data as JSON
var P_String = Wclient2.DownloadString(P_url);
//Deserialize the results 
Person P_obj = JsonConvert.DeserializeObject<Person>(P_String);
}
}

JSON TOKEN Object J_Tok  Derived from pasting json string in JSON2CSharp.

 [DataContract]
 public class J_TOK
 {
 [DataMember]
 public string access_token { get; set; } 
 [DataMember]
 public string token_type { get; set; } 
 [DataMember]
 public string scope { get; set; }
 }

Person Object Person Derived from pasting json string in JSON2CSharp.

[DataContract]
public class Person
{

[DataMember]
public string title { get; set; }
[DataMember]
public string firstNames { get; set; }
[DataMember]
public string lastName { get; set; }
[DataMember]
public string gender { get; set; }
[DataMember]
public DateTime? dateOfBirth { get; set; }
[DataMember]
public bool ageEstimated { get; set; }
[DataMember]
public DateTime? dateOfDeath { get; set; }
[DataMember]
public string ethnicity { get; set; }
[DataMember]
public string maritalStatus { get; set; }
[DataMember]
public string contactAddress { get; set; }
[DataMember]
public List<object> telephoneNumber { get; set; }
[DataMember]
public string emailAddress { get; set; }
[DataMember]
public string relationship { get; set; }
[DataMember]
public List<Person> person { get; set; }
[DataMember]
public List<Person> worker { get; set; }
[DataMember]
public List<Person> gp { get; set; }
}

[DataContract]
public class RootObject
{
public string title { get; set; }
[DataMember]
public string firstNames { get; set; }
[DataMember]
public string lastName { get; set; }
[DataMember]
public string gender { get; set; }
[DataMember]
public DateTime? dateOfBirth { get; set; }
[DataMember]
public bool ageEstimated { get; set; }
[DataMember]
public DateTime? dateOfDeath { get; set; }
[DataMember]
public string ethnicity { get; set; }
[DataMember]
public string maritalStatus { get; set; }
[DataMember]
public string contactAddress { get; set; }
[DataMember]
public List<string> telephoneNumber { get; set; }
[DataMember]
public string emailAddress { get; set; }
[DataMember]
public string relationship { get; set; }
[DataMember]
public List<Person> person { get; set; }
[DataMember]
public List<Person> worker { get; set; }
[DataMember]
public List<Person> gp { get; set; }
}
}

WEB API with attribute routing

My last web API started breaking when I added new functions to it :

eg

public string get()
{}
public string get(int i)
{}
public newFunction (int i)
{}

A partial solution was to add a more specific route to the webapiconfig.cs file

 config.Routes.MapHttpRoute(
 "WithActionApi",
 "api/{controller}/{action}/{id}"
 );

This allowed Routes like Employees/Get & Employees/Get/101 to work but still left Employees/101 failing.

This is where Attribute Routing comes in. You can decorate the function directly with the route to access it

[AppAuthorize(Roles = "APIUser")]
[Route("api/employees/{id}")]
public IEnumerable<EMP> Get(int id)
{
var elist = getEMP(id);
return elist;
}
[AppAuthorize(Roles = "APIUser")]
[Route("api/employees")]
public IEnumerable<Emp> Get()
{
var velist = getAllEmps();
return velist;
}

[Route("api/ByDept/{id}")]
public IEnumerable<Emp> GetDept(int id)
{
var elist = getAllEmpsByDepartment();
return elist;
}

WEB API Custom Unauthorize Filter

Here’s a web api controller in my WEB API application called WINAUTHAPI with a custom Authorization filter.  The default Authorize filter  [Authorize(Roles = “MyRoleList”)] would redirect to a log in screen, if you try to access a function you are not authorised to use. It would assume you have logged in with the wrong id or permissions.

AppAuthorize assumes the user is logged in with the correct credentials, & lets them do whatever they are authorised to do, without interruption.

In this sample, the user has logged in with a role of  “APIUser”  and without the role “NeverAccess“.  So Api/Employees/101 works and Api/Employees doesn’t.  We want Api/Employees to be quietly redirected without an unauthorised error.  Some other approaches use the MVC AuthorizationAttribute which gets confusing.

Sample EmployeesController.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Web.Http;
 using WINAUTHAPI.Models;
 using WINAUTHAPI.Common;
 namespace WINAUTHAPI.Controllers
 {
 [Authorize]
 public class EmployeesController : ApiController
 {
[AppAuthorize(Roles = "APIUser")]
 public IEnumerable<Employee> Get(int id)
 {var elist = ReadQueryable("select * from Employees where EMPLOYEE_ID=" + id);
 return elist;
 }

[AppAuthorize(Roles = "NeverAccess")]
 public IEnumerable<Employee> Get()
 {var velist = ReadQueryable("select * from HR.Employees");
 return velist;
 }
private IEnumerable<Employee> ReadQueryable(string qry)
 {// generic function to get a list of Employees from somewhere...
 return tmpemployees.AsEnumerable();
 }}}

AppAuthorize.cs

The WinAuthAPI / Common / AppAuthorize class overrides the base HandleUnAuthorizedRequest to redirect to the Base Uri of the API.  Returning a 200 status code & bypassing the code which was protected by the filter.  We could log this failure if we wanted.

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Web.Http;
 using System.Web.Http.Controllers;
 using System.Net.Http;
 namespace WINAUTHAPI.Common
 {
 public class AppAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
 {
 protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
 {
 if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
 {
 filterContext.Response = new HttpResponseMessage();
 string fullyQualifiedUrl = filterContext.Request.RequestUri.GetLeftPart(UriPartial.Authority);
// or we could redirect to somewhere else....
 filterContext.Response.Headers.Location = new Uri(fullyQualifiedUrl);
 }
 else
 {
 base.HandleUnauthorizedRequest(filterContext);
 }
 } } }

Angular JS pagination in IE8

I had some issues with the Angular in my previous post running under IE 8, which is no longer supported.  The following steps fixed it for me :  
 

Download Angular.js   v1.2.28  (Apparantly this was the last version to support IE 8) to a local folder – to avoid proxy authentication issues. 

Ditto for the following  to ensure Jquery runs & the HTML 5 references work 

jquery-1.8.3.js

css3-mediaqueries.js

Html5shiv.js

Respond.min.js

Add the the following to my jquery  $function()
 $('#dynbut').click(function () {
            $(".btn-group-vertical > .btn").removeClass("active");
  });
Dynbut is a wrapper div for my dynamic button group. Without this code, every button clicked stays active – so instead of only highlighting the current button, all those ever clicked are highlighted.  This deals with this.
Add the following functions & filters to my angulargetdata.js script:
// This filter slices my json list into chunks 10 rows long.  Probably better to have a Rowcnt parameter, but 10 is a reasonable figure, keeps things simple  & avoids having to validate user input .
myApp.filter('slice', function () {
    return function (arr, start, end) {       
        this.displayed = arr.length;     
    if (arr.length + 10 < end) {
            this.First();        
}
        return (arr || []).slice(start, end);
    };
});
This code moves forward by 10 rows ( IE displays the next page). This could have been ‘DRY’er, but its constructed for clarity:
 $scope.Forward = function (value) {
        if (value + 10 >= $scope.filtered.length) {
            var pages = Math.floor($scope.filtered.length / 10);
            $scope.startFrom = (10 * pages);            
$scope.goTo = $scope.filtered.length;
        }
        else {
            $scope.startFrom = value + 10;
            $scope.goTo = value + 20;
        }     
        return $scope.startFrom;
   }
Move to the nth row , showing upto 10  rows after ( IE go to page ‘n’) 
    $scope.MoveTo = function (value) {
        if (value + 10 >= $scope.filtered.length) {
            var pages = Math.floor($scope.filtered.length / 10);
             $scope.startFrom = (10 * pages);
            $scope.goTo = $scope.filtered.length;
        }
        else if (value - 10 < 0) {
            $scope.startFrom = 0;
            $scope.goTo = 10;
        }
        else {
            $scope.startFrom = value;
            $scope.goTo = value + 10;
        }
        return $scope.startFrom;
    }
 Move back by 10 rows ( IE displays the previous page) 
    $scope.Back = function (value) {
        if (value - 10 < 0) {
            $scope.startFrom = 0;
            $scope.goTo = 10;
        }
        else {
            $scope.startFrom = value - 10;
            $scope.goTo = value;
        }
        return $scope.startFrom;
    }
 Move to the last set of rows ( IE displays the last page) 
$scope.Last = function () {
       var pages = Math.floor($scope.filtered.length / 10);
       $scope.startFrom = (10 * pages);
       $scope.goTo = $scope.filtered.length;
        return $scope.startFrom;
    }
 Move to row 0  – the first page 
    $scope.First = function (value) {
        $scope.startFrom = 0;
        $scope.goTo = 10;
        return $scope.startFrom;
    }
The ceil function makes Math.Ceil available to angular.  Its used for the pagination calculation
myApp.filter('ceil', function () {
    return function (input) {
        var output = "";
        output = Math.ceil(input);
        return (output);
    }
});
In the html page the following buttons set the search category  with the following html
< button ng-click="search.categoryheading='Attendance'" class="btn btn-primary" > Attendance </ button >

This was built using razor.  ViewBag.categorylist having been built in the controller  :
 @foreach (var item in @ViewBag.categorylist)
 {                 
 @item  
  }
The pagination is set as follows
The Ng-hide only shows when there are relevant pages : 10 records or less require no buttons:   EG Only show page 3 , if we are displaying more than 20 rows….
The ng-disabled stops buttons from being clicked when they are not relevant  :  They are the current page.  or  Start / Prev when on page 1 ; End or Next when on last page….
 < span id="pagebutton"  >   
< button class="btn" ng-disabled="startFrom==0" ng-click="First()"  ng-hide="displayed <= 10"><< </button>
< button class="btn" ng-disabled="startFrom==0" ng-click="Back(startFrom)"  ng-hide="displayed <= 10" >< </button>
< button id= "btn1" ng-disabled="startFrom==0" class="btn" ng-click="MoveTo(0)"  ng-hide="displayed <= 10">1 </button>
< button id= "btn2" ng-disabled="startFrom==10" class="btn" ng-click="MoveTo(10)"  ng-hide="displayed <= 10">2 </button>
< button id= "btn3" ng-disabled="startFrom==20" class="btn" ng-click="MoveTo(20)" ng-show="displayed > 20">3 </button>
etc….
< button id= "btn11" ng-disabled="startFrom==100" class="btn" ng-click="MoveTo(100)" ng-show="displayed > 100">11 </button>
< button  class="btn" ng-disabled="goTo>=displayed" ng-click="Forward(startFrom)"  ng-hide="displayed <= 10"> > </button>
< button  class="btn" ng-disabled="goTo>=displayed" ng-click="Last()" ng-hide="displayed <= 10"> >> </button>
< /span>
Where you are in the list is displayed in the following details
  Page {{(startFrom/10)+1|ceil}} : rows {{startFrom+1 }} to {{displayed > goTo ? goTo : displayed }} of {{displayed}}
EG   Page 1 : rows 1 to 10 of 98
Updated code
HR Employee Record

Sample Record For Fred Bloggs ( 9999 )

Logged in as: myOrg\myId

// .btn”).removeClass(“active”);
});
var uagent = detectIE();
if (uagent == false) {
toastr.options = {
“closeButton”: false,
“debug”: false,
“positionClass”: “toast-top-full-width”,
“onclick”: null,
“showDuration”: “300”,
“hideDuration”: “9999”,
“timeOut”: “9000”,
“extendedTimeOut”: “9000”,
“showEasing”: “swing”,
“hideEasing”: “linear”,
“showMethod”: “fadeIn”,
“hideMethod”: “fadeOut”
}
toastr.error(“This application is currently only supported under Internet Explorer.
It uses features such as as automatically logging in with your network id, that have not been switched on in other browsers. You may experience some unexpected results. “)
}
})
function detectIE() {
var ua = window.navigator.userAgent;
// test values
// IE 10
//ua = ‘Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';
// IE 11
//ua = ‘Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
// IE 12 / Spartan
//ua = ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0′;
var msie = ua.indexOf(‘MSIE ‘);
if (msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf(‘.’, msie)), 10);
}
var trident = ua.indexOf(‘Trident/’);
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf(‘rv:’);
return parseInt(ua.substring(rv + 3, ua.indexOf(‘.’, rv)), 10);
}
var edge = ua.indexOf(‘Edge/’);
if (edge > 0) {
// IE 12 => return version number
return parseInt(ua.substring(edge + 5, ua.indexOf(‘.’, edge)), 10);
}
// other browser
return false;
}

// ]]> // <![CDATA[
$(document).ready(function () {
var dstring = $(‘#LastDbUpdate’).val();
var maxdiffs = $(‘#maxdiff’).val();
var maxdiff = 10000;
try {
maxdiff = Number(maxdiffs);
}
catch (e) {
maxdiff = 30000;
}
var updateVal = $(‘#update’).val();
var diff = maxdiff + 90;
var currTime = new Date();
var lastupdateDate = new Date();
lastupdateDate.setDate(lastupdateDate.getDate() – 1);
if (dstring != “”)
{
var dstr = dstring.split(“:”);
for (var i in dstr)
{
dstr[i] = parseInt(dstr[i], 10);
}
lastupdateDate = new Date(dstr[0], dstr[1]-1,dstr[2], dstr[3], dstr[4], dstr[5],0);
diff = currTime.getTime() – lastupdateDate.getTime();
var displdate = lastupdateDate.toTimeString().substr(0, 5);
if ((updateVal == ‘upload’) && ( diff<maxdiff)) {
toastr.options = {
“closeButton”: false,
“debug”: false,
“positionClass”: “toast-top-right”,
“onclick”: null,
“showDuration”: “1000”,
“hideDuration”: “1000”,
“timeOut”: “9000”,
“extendedTimeOut”: “9000”,
“showEasing”: “swing”,
“hideEasing”: “linear”,
“showMethod”: “fadeIn”,
“hideMethod”: “fadeOut”
}
toastr.success(”
File uploaded at ” + displdate + “.

The list is sorted with the most recent uploads at the top.”, “File upload successful”)
}
else if ((updateVal == ‘delete’) && (diff

Loading.
Loading.
Search: {{(filtered |filter:search).length}} document(s) match search criteria : Category : All and matches text:’ {{search.$}} ‘ . Sort order: {{decodePredicate(predicate)}}
All Category A Category B

<< < 1 2 3 4 5 6 7 8 9 10 11 > >> Page {{(startFrom/10)+1|ceil}} : rows {{startFrom+1 }} to {{displayed > goTo ? goTo : displayed }} of {{displayed}}

Angular Code var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']); myApp.controller('dataController', function ($scope, $http) { var id = $('#employeeid').val(); var serviceurl = $('#sourceURL').val(); $scope.baseURLvalue = serviceurl.replace('{0}', id) + "&callback=JSON_CALLBACK"; $scope.isloading = "true"; $scope.currentPage = 1; $scope.pageSize = 10; $scope.rows = []; $scope.predicate = 'createddate'; $scope.reverse = 'true'; $scope.displayed = 0; $scope.startFrom = 0; $scope.goTo = 10; $scope.catfilter = ""; $scope.filter2 = ""; $http({ method: 'JSONP', url: $scope.baseURLvalue }).success(function (data, status, header, config) { $scope.filtered = data.rows; if (data.rows) { $scope.displayed = data.rows.length; } $scope.rows = data.rows; $scope.isloading = "false"; }) .error(function (data, status, header, config) { $scope.isloading = "false"; $scope.displayed = 0; }); $scope.Forward = function (value) { if (value + 10 >= $scope.filtered.length) { var pages = Math.floor($scope.filtered.length / 10); $scope.startFrom = (10 * pages); $scope.goTo = $scope.filtered.length; } else { $scope.startFrom = value + 10; $scope.goTo = value + 20; } return $scope.startFrom; } $scope.MoveTo = function (value) { if (value + 10 >= $scope.filtered.length) { var pages = Math.floor($scope.filtered.length / 10); $scope.startFrom = (10 * pages); $scope.goTo = $scope.filtered.length; } else if (value - 10 < 0) { $scope.startFrom = 0; $scope.goTo = 10; } else { $scope.startFrom = value; $scope.goTo = value + 10; } return $scope.startFrom; } $scope.Back = function (value) { if (value - 10 < 0) { $scope.startFrom = 0; $scope.goTo = 10; } else { $scope.startFrom = value - 10; $scope.goTo = value; } return $scope.startFrom; } $scope.Last = function () { var pages = Math.floor($scope.filtered.length / 10); $scope.startFrom = (10 * pages); $scope.goTo = $scope.filtered.length; return $scope.startFrom; } $scope.First = function (value) { $scope.startFrom = 0; $scope.goTo = 10; return $scope.startFrom; } $scope.decodePredicate = function (predicate) { var retval = predicate; if (predicate == "-createddate") retval = "Created date"; if (predicate == "title") retval = "Title"; if (predicate == "categoryheading") retval = "Category Heading"; if (predicate == "createdby") retval = "Created by"; if (predicate == "createddate") retval = "Created date"; if (predicate == "effectivedate") retval = "Effective Date"; return retval } }) myApp.filter('ceil', function () { return function (input) { var output = ""; output = Math.ceil(input); return (output); } }); myApp.filter('nonNumstrip', function () { return function (input) { var output = ""; output = input.replace(/[^0-9]+/g, ""); return (output); } }); myApp.filter('slice', function () { return function (arr, start, end) { this.displayed = arr.length; if (arr.length + 10 < end) { this.First(); } return (arr || []).slice(start, end); }; }); function OtherController($scope) { $scope.pageChangeHandler = function (num) { }; }

Angular JS table header to persist sort order display

An  angular controller can define the default sort order of a table as the page loads with .

$scope.predicate = 'sortfield_name';

You can use ng-show=”predicate ==’sortfield_name'”  in a span to show text when this is the sort order.  For example

<span ng-show="predicate == 'title'"> * </span>

shows an asterisk when the sort order is title.  Css based on focus or active attributes only works for the last object you click on, so it shows nothing when a page loads & loses the decoration if you click else where on the page.  It also gets quite complex , quite quickly.

You can also distinguish between sort orders by nesting another ng-show or ng-hide span inside the ‘parent’ span with up & down arrows:

//Show this code block if the predicate (sort order is 'title') 
<span ng-show="predicate == 'title'">

//SHOW a down arrow if the sort order is reversed
         <span ng-show="reverse" class="glyphicon glyphicon-arrow-down"></span>

//HIDE an up arrow if the sort order is reversed
         <span ng-hide="reverse" class="glyphicon glyphicon-arrow-up"></span>

</span>

This is how the code looks in a table header, along with the ng-click command to set the predicate to ‘title’ and let it swap sort orders.  The text ‘Title’ is visible all the time , along with an appropriate up arrow as required:

<th>
 <a href="" ng-click="predicate = 'title'; reverse=!reverse;" ng-class="SelectorHdr">Title 
 <span ng-show="predicate == 'title'">
 <span ng-show="reverse" class="glyphicon glyphicon-arrow-down"></span>
 <span ng-hide="reverse" class="glyphicon glyphicon-arrow-up"></span>
 </span></a>
</th>

Deserializing very nested hierarchical JSON in C#

Most JSON to .C# tutorials process basic flat data:

Below is some really hierarchical realworld JSON , which was originally based on some production XML ,  This is a lot more complex than the tutorial samples, but it can be easily processed using :

System.Runtime.Serialization;
&
System.Runtime.Serialization.Json;

Step 1 I took a copy of the XML & converted it to JSON at http://www.utilities-online.info/xmltojson

{"Response": {
"MsgHeader": {"MsgVersion": "1.0","MsgType": "_Per_DETAILS","MsgId": "MP1234","SourceApplication": "MySys"},
"MsgBody": {
"S_PerDetails": {"S_PerDetailsRow": {"num": "1","CitizenDetails": {"CitizenName": {"CitizenNameTitle": "Mr","CitizenNameForename": "Fred","CitizenNameSurname": "Bloggs"}
},
"CitizenAddress": {"SAddressesRow": [{"num": "1","AddressId": "G1234","SubjectInd": "P","SubjectId": "PerId","AddressChoice": "NONSTD","NonStandardAddress": {"NSHouseName": "12 the house","NSStreet": "THE street","NSArea": "area","NSTown": "Anytown","NSCounty": "Lincs","NSPostCode": "CV1234","NSWithinAuthority": "Y","NSMainAddress": "Y","NSCorrespondence": "N",
"NSInvoice": "N","NSStartDate": "2013-06-25"}
},
{"num": "2","AddressId": "2133","SubjectInd": "P","SubjectId": "PerId","AddressChoice": "NONSTD","NonStandardAddress": {"NSHouseNumber": "35","NSStreet": "any street CLOSE",
"NSTown": "REDDITCH","NSCounty": "WORCS","NSPostCode": "B5678","NSWithinAuthority": "N","NSMainAddress": "N","NSCorrespondence": "N","NSInvoice": "N","NSStartDate": "2012-12-11"
}},
{
"num": "3","AddressId": "G9876","SubjectInd": "P","SubjectId": "PerId","AddressChoice": "NONSTD","NonStandardAddress": {"NSHouseNumber": "21","NSStreet": "Addr GROVE","NSTown": "London",
"NSPostCode": "n1234","NSWithinAuthority": "N","NSMainAddress": "N","NSCorrespondence": "N","NSInvoice": "N","NSStartDate": "2013-06-24"}}]
},
"_PerId": "PerId","Title": "MR","Name": "Sur, Mr FNam","MainAddress": "1,Street,location , Town, CV1 1AA","DateOfBirth": "2010-04-08","Gender": "M","GenderDesc": "Male","WarningIndicator": "N","ActiveService": "Y","ContactDetails": {"ContactDetailsRow": {"num": "1","ContactCode": "CARER","ContactCodeDesc": "Carer's Phone","ContactValue": "CARER - FUZZY"}},
"Roles": {
"RolesRow": [{"num": "1","Role": "C","RoleDesc": "Client","RoleId": "ABCD1","RoleStartDate": "2011-11-09"},
{
"num": "2","Role": "O","RoleDesc": "Other","RoleId": "zxc1","RoleStartDate": "2010-08-26"}]
},
"OtherRefNumbers": {"OtherRefNumbersRow": {"num": "1","RefNumberCode": "NHS","RefNumberCodeDesc": "NHS REF. No.","RefNumber": "1234567899"}}}}}}}

Step 2 Take this JSON & create the C# classes required using http://json2csharp.com/

This creates, basic C# classes eg :

public class CitizenName
{
    public string CitizenNameTitle { get; set; }
    public string CitizenNameForename { get; set; }
    public string CitizenNameSurname { get; set; }
}

Step 3 This is helpful but not quite what we need for the deserializing process. The class needs a [DataContract] attribute, & each member needs the [DataMember] attribute

[DataContract]
public class CitizenName
{
 [DataMember]
 public string CitizenNameTitle { get; set; }
 [DataMember]
 public string CitizenNameForename { get; set; }
 [DataMember]
 public string CitizenNameSurname { get; set; }
}

Step 4  Build the console app.

////ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;  
//Commented out code to allow SSL Connection .  
//From url www.avivroth.com/2013/05/02/rest-calls-in-net-c-over-ssl-https
// Title is "REST calls in .NET (C#) over SSL (HTTPS)"
//
using (WebClient client = new WebClient())
 {
//Define URL for webservice API
 string url = "http://myRemote/webserviceAPI?dentifier=ABC&parm1=1234";

//add HTTP headers for content type, and others
 client.Headers["Content-type"] = "application/json";

//add HTTP header for remote authentication credentials for web service 
//( in this case the Default Network Credentials, because the service is ours)
client.UseDefaultCredentials = true;
client.Credentials = CredentialCache.DefaultNetworkCredentials;

//add HTTP headers to authenticate to our web proxy 
//( in this case the Default Network Credentials)
client.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

// call the service & return the results to a byte array.
// (Some error handling might be good here)
byte[] data1 = client.DownloadData(url);

// load this to a memory stream
 MemoryStream ms = new MemoryStream();
 ms = new MemoryStream(data1);

//Now Deserialize to a c# objects....
 var serializer = new DataContractJsonSerializer(typeof(RootObject));
 RootObject library = (RootObject)serializer.ReadObject(ms);
 ms.Position = 0;
}

Here’s a screenshot of the locals in Visual Studio, & the resultant hierarchy

locals

Full Simple Console Application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace restremote
{
 class Program
 { static void Main(string[] args)
 {
////ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
  using (WebClient client = new WebClient()) {
  string url = "http://myRemote/webserviceAPI?dentifier=ABC&parm1=1234";
  client.Headers["Content-type"] = "application/json";
  client.UseDefaultCredentials = true;
  client.Credentials = CredentialCache.DefaultNetworkCredentials;
  client.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
  byte[] data1 = client.DownloadData(url);
  MemoryStream ms = new MemoryStream();
  ms = new MemoryStream(data1);
  var serializer = new DataContractJsonSerializer(typeof(RootObject));
  RootObject library = (RootObject)serializer.ReadObject(ms); 
 }
}
// classes

[DataContract]
public class MessageHeader
{[DataMember]
public string MessageVersion { get; set; }
[DataMember]
public string MessageType { get; set; }
[DataMember]
public string MessageId { get; set; }
[DataMember]
public string SourceApplication { get; set; }
}

[DataContract]
public class CitizenName
{[DataMember]
public string CitizenNameTitle { get; set; }
[DataMember]
public string CitizenNameForename { get; set; }
[DataMember]
public string CitizenNameSurname { get; set; }
}

[DataContract]
public class CitizenDetails
{[DataMember]
public CitizenName CitizenName { get; set; }
}

[DataContract]
public class NonStandardAddress
{[DataMember]
public string NSHouseName { get; set; }
[DataMember]
public string NSStreet { get; set; }
[DataMember]
public string NSArea { get; set; }
[DataMember]
public string NSTown { get; set; }
[DataMember]
public string NSCounty { get; set; }
[DataMember]
public string NSPostCode { get; set; }
[DataMember]
public string NSWithinAuthority { get; set; }
[DataMember]
public string NSMainAddress { get; set; }
[DataMember]
public string NSCorrespondence { get; set; }
[DataMember]
public string NSInvoice { get; set; }
[DataMember]
public string NSStartDate { get; set; }
[DataMember]
public string NSHouseNumber { get; set; }
}

[DataContract]
public class CFAddressesRow
{[DataMember]
public string num { get; set; }
[DataMember]
public string AddressId { get; set; }
[DataMember]
public string SubjectInd { get; set; }
[DataMember]
public string SubjectId { get; set; }
[DataMember]
public string AddressChoice { get; set; }
[DataMember]
public NonStandardAddress NonStandardAddress { get; set; }}

[DataContract]
public class CitizenAddress
{[DataMember]
public List CFAddressesRow { get; set; }
}

[DataContract]
public class ContactDetailsRow
{[DataMember]
public string num { get; set; }
[DataMember]
public string ContactCode { get; set; }
[DataMember]
public string ContactCodeDesc { get; set; }
[DataMember]
public string ContactValue { get; set; }
}

[DataContract]
public class ContactDetails
{[DataMember]
public ContactDetailsRow ContactDetailsRow { get; set; }
}

[DataContract]
public class RolesRow
{[DataMember]
public string num { get; set; }
[DataMember]
public string Role { get; set; }
[DataMember]
public string RoleDesc { get; set; }
[DataMember]
public string RoleId { get; set; }
[DataMember]
public string RoleStartDate { get; set; }
}

[DataContract]
public class Roles
{[DataMember]
public List RolesRow { get; set; }
}

[DataContract]
public class OtherRefNumbersRow
{[DataMember]
public string num { get; set; }
[DataMember]
public string RefNumberCode { get; set; }
[DataMember]
public string RefNumberCodeDesc { get; set; }
[DataMember]
public string RefNumber { get; set; }
}

[DataContract]
public class OtherRefNumbers
{[DataMember]
public OtherRefNumbersRow OtherRefNumbersRow { get; set; }
}

[DataContract]
public class Per_DetailsRow
{[DataMember]
public string num { get; set; }
[DataMember]
public CitizenDetails CitizenDetails { get; set; }
[DataMember]
public CitizenAddress CitizenAddress { get; set; }
[DataMember]
public string PersonId { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string MainAddress { get; set; }
[DataMember]
public string DateOfBirth { get; set; }
[DataMember]
public string Gender { get; set; }
[DataMember]
public string GenderDesc { get; set; }
[DataMember]
public string WarningIndicator { get; set; }
[DataMember]
public string ActiveService { get; set; }
[DataMember]
public ContactDetails ContactDetails { get; set; }
[DataMember]
public Roles Roles { get; set; }
[DataMember]
public OtherRefNumbers OtherRefNumbers { get; set; }
}

[DataContract]
public class Per_Details
{[DataMember]
public Per_DetailsRow Per_DetailsRow { get; set; }
}

[DataContract]
public class MessageBody
{[DataMember]
public Per_Details Per_Details { get; set; }
}

[DataContract]
public class Response
{[DataMember]
public MessageHeader MessageHeader { get; set; }
[DataMember]
public MessageBody MessageBody { get; set; }
}

[DataContract]
public class RootObject
{[DataMember]
public Response Response { get; set; }
}}}

Apply multiple Angular filters. Angular filter populated by buttons. WebConfig / razor to build btn_group for Angular function. Bootstrap buttons to fill href link.

This is a modified extension of my previous angular post :and covers a bit too.  Thanks to the numerous people whose work this is built on

The Navigation option is populated from a viewbag category list & launches applyCategory() in the Index.cshtml 

<div class=”span3″>

<div id=”dynbut” >

<div class=”btn-group-vertical” data-toggle=”buttons-radio”>
<button id=”defaultCat” ng-click=”applyCategory()” class=”btn btn-primary”>All </button>
@foreach (var item in @ViewBag.categorylist)
{
<button ng-click=”applyCategory(‘categoryheading’,’@item’)” class=”btn btn-primary”>@item</button>
}
</div>

</div>

</div>

ViewBag.categorylist  is defined in the Index controller from a web config value( Additional code is there to strip out category numbers , separators & resultant nulls from the list 

Regex pattern = new Regex(“[:.0123456789]”);

var catlist = pattern.Replace((ConfigurationManager.AppSettings[“CategorySettingsInWebConfig”]), “”);

List<string>categories2 = catlist.Replace(“, “,”,”).Split(‘,’).ToList();

categories2.RemoveAll(item => item.Length==0);

ViewBag.categorylist = categories2;

return View();
}

In order to make sure that only one button is selected in this navigation list & to ensure that the data is filtered through the button AND the free text filter angular myApp dataController now includes Jquery code to remove the ACTIVE class from all the buttons in the .btn-group-vertical navigation group .  It also tests to see if the All category is selected, then it removes the actual filter value( there is no category – All) 

$scope.applyCategory = function (prop, value) {

$(“.btn-group-vertical > .btn”).removeClass(“active”);

if (!prop || !value) {

$(‘#CurrentFilter’).text(‘Category : All’);

$scope.filtered = $scope.rows;

return;

}

$scope.filtered = $scope.rows.filter(function (ritem) {

$(‘#CurrentFilter’).text(‘Category : ‘+value);

return ritem[prop] === value;

});

};

To apply both filters ( free text &  the navigation buttons)  to the data , the table is built with 

<tr dir-paginate=”item in filtered | filter:search   | orderBy:predicate:reverse | itemsPerPage: pageSize” current-page=”currentPage”>

To make the pagination buttons fully fit the href, so that whereever you click, the comman is executed, DirPagination.tpl.html now has a class of “btn btn-small” in each href: 

<ul class=”btn-group inline” ng-if=”1 < pages.length” >

<li ng-if=”boundaryLinks” ng-class=”{ disabled : pagination.current == 1 }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(1)”>&laquo; </a>
</li>

<li ng-if=”directionLinks” ng-class=”{ disabled : pagination.current == 1 }”>
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.current – 1)” class=”ng-binding”>‹</a>
</li>
<li ng-repeat=”pageNumber in pages track by $index” ng-class=”{ active : pagination.current == pageNumber, disabled : pageNumber == ‘…’ } “>
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pageNumber)”> {{pageNumber}} </a>
</li>

<li ng-if=”directionLinks” ng-class=”{ disabled : pagination.current == pagination.last }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.current + 1)” class=”ng-binding”>›</a>
</li>
<li ng-if=”boundaryLinks” ng-class=”{ disabled : pagination.current == pagination.last }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.last)”>&raquo;</a>
</li>

</ul>

 Bootstrap min default didnt want to play ball, so I added the following to the bottom of dirPagination.css to format the navigation buttons & make sure that they filled up the href

.btn-group inline {
}

.btn {
background-color:lightgray;
margin: 0px;
display: inline-block;
width: 100%;
height: 100%;
border-color:dimgrey;
border-width:thin;

}
.disabled .btn {
color:lightgrey;
font-weight: bold;
background-color:white;
border-color:lightgrey;
}
.active .btn {
color:white;
background-color:darkgray;
border-color:black;
border-width:thin;
}

Full Final versions :

Index.cshtml start

@model my_frontapp.Models.AdvancedSearch
<!DOCTYPE html>
<html lang=”en” ng-app=”myApp”>
<head>
<script src=”http://code.jquery.com/jquery-2.0.3.min.js”&gt; </script>
<script src=”https://code.angularjs.org/1.3.0/angular.js”></script&gt;
<script src=”//code.jquery.com/ui/1.11.4/jquery-ui.js”></script>
<script src=”../../Scripts/dirPagination.js”></script>
<script src=”../../Scripts/angulargetdata.js”></script>
<script data-require=”bootstrap@3.1.1″ data-semver=”3.1.1″ src=”//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js”></script>

<link rel=”stylesheet” href=”style.css” />
<link href=”../../Content/bootstrap.min.css” rel=”stylesheet” />
<link rel=”stylesheet” href=”//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css”>
<link href=”../../Content/bootstrap-responsive.min.css” rel=”stylesheet” />
<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css”&gt;
<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css”&gt;
<link href=”../../Scripts/dirPagination.css” rel=”stylesheet” />
</head>

<body>
<input type=”hidden” id=”sourceURL” value=”http://localhost:1234/searchdocs?identifier=xx&empid={0}&library=A”>
<input type =”hidden” id=”Idno” value=”@ViewBag.Id_value”>

<div ng-controller=”dataController” >
<h1>Records for @ViewBag.Id_value</h1>

<div class=”form-group”>
<label for=”search” class=”labeltext”>Search: </label>
<input type=”text” ng-model=”search.$” id=”search” class=”search-query” placeholder=”Filter text”>

<label for=”pageSize” class=”labeltext”>items per page:</label>
<input class=”IntSpinner” type=”number” min=”1″ max=”99″ id=”dspinner” ng-model=”pageSize”>
<br />
Page : {{ currentPage }} <span class-“infotext” id =”CurrentFilter”>Category : All</span> <span class-“infotext” id =”AdditionalFilter” ng-show=”search.$”>. Addititional text filter : {{search.$}}</span>
</div>

<div class=”span3″>
<div id=”dynbut” >
<div class=”btn-group-vertical” data-toggle=”buttons-radio”>
<button id=”defaultCat” ng-click=”applyCategory()” class=”btn btn-primary”>All </button>
@foreach (var item in @ViewBag.categorylist)
{
<button ng-click=”applyCategory(‘categoryheading’,’@item’)” class=”btn btn-primary”>@item</button>
}
</div>
</div>
</div>

<div class=”span9″ min-height: 100 px;>
<table>
<tr>
<th style=”width: 42%;”><a href=”” ng-click=”predicate = ‘title'; reverse=!reverse”>Title</a></th>
<th style=”width: 30%;”><a href=”” ng-click=”predicate = ‘categoryheading'; reverse=!reverse”>Category</a></th>
<th style=”width: 12%;”><a href=”” ng-click=”predicate = ‘createdby'; reverse=!reverse”>Created by</a></th>
<th style=”width: 12%;”><a href=”” ng-click=”predicate = ‘createddate'; reverse=!reverse”>On</a></th>
<th style=”width: 12%;”><a href=”” ng-click=”predicate = ‘effectivedate'; reverse=!reverse”>Effective Date</a></th>
<th style=”width: 12%;”></th>
</tr>
<tr dir-paginate=”item in filtered | filter:search | orderBy:predicate:reverse | itemsPerPage: pageSize” current-page=”currentPage”>
<td><a href=”/Docs/openDoc?identifier=xx&cat_ref={{item.cat_ref}}&empid={{item.id}}&filename={{item.linknam}} “>{{item.title }} </a></td>
<td><a href=”/Docs/openDoc?identifier=xx&cat_ref={{item.cat_ref}}&empid={{item.id}}&filename={{item.linknam}} “>{{item.categoryheading}} </a></td>
<td><a href=”/Docs/openDoc?identifier=xx&cat_ref={{item.cat_ref}}&empid={{item.id}}&filename={{item.linknam}} “>{{item.createdby}} </a></td>
<td><a href=”/Docs/openDoc?identifier=xx&cat_ref={{item.cat_ref}}&empid={{item.id}}&filename={{item.linknam}} “>{{item.createddate|nonNumstrip|date:”dd/MM/yyyy”}} </a></td>
<td><a href=”/Docs/openDoc?identifier=xx&cat_ref={{item.cat_ref}}&empid={{item.id}}&filename={{item.linknam}} “>{{item.effectivedate|nonNumstrip|date:”dd/MM/yyyy”}} </a></td>
<td><a class=”btn btn-mini btn-warning btn-mini” style=”FONT-WEIGHT: bold; COLOR: white” href=”/Docs/Delete?identifier=xx&amp;cat_ref={{item.cat_ref}}&amp;empid={{item.id}}&amp;filename={{item.linknam}}”>Delete</a>
</td>
</tr>
</table>
<br />
</div>
</div>

<div ng-controller=”OtherController” class=”other-controller”>
<br /> <br /> <br />
<div class=”span11″>
<div class=”text-center”>
<dir-pagination-controls boundary-links=”true”></dir-pagination-controls>
</div></div>
</div>
</body>
</html>

Index.cshml end

dirPagination.tpl.html
<ul class=”btn-group inline” ng-if=”1 < pages.length” >
<li ng-if=”boundaryLinks” ng-class=”{ disabled : pagination.current == 1 }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(1)”>&laquo; </a>
</li>
<li ng-if=”directionLinks” ng-class=”{ disabled : pagination.current == 1 }”>
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.current – 1)” class=”ng-binding”>‹</a>
</li>
<li ng-repeat=”pageNumber in pages track by $index” ng-class=”{ active : pagination.current == pageNumber, disabled : pageNumber == ‘…’ } “>
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pageNumber)”> {{pageNumber}} </a>
</li>

<li ng-if=”directionLinks” ng-class=”{ disabled : pagination.current == pagination.last }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.current + 1)” class=”ng-binding”>›</a>
</li>
<li ng-if=”boundaryLinks” ng-class=”{ disabled : pagination.current == pagination.last }” >
<a class=”btn btn-small” href=”” ng-click=”setCurrent(pagination.last)”>&raquo;</a>
</li>
</ul>

dirPagination.tpl.html end

dirPagination.js

/**
* dirPagination – AngularJS module for paginating (almost) anything.
*
*
* Credits
* =======
*
* Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ
* for the idea on how to dynamically invoke the ng-repeat directive.
*
* I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project:
* https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js
*
* Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk>
*/

(function () {

/**
* Config
*/
var moduleName = ‘angularUtils.directives.dirPagination';
var DEFAULT_ID = ‘__default';

/**
* Module
*/
var module;
try {
module = angular.module(moduleName);
} catch (err) {
// named module does not exist, so create one
module = angular.module(moduleName, []);
}

module.directive(‘dirPaginate’, [‘$compile’, ‘$parse’, ‘$timeout’, ‘paginationService’, function ($compile, $parse, $timeout, paginationService) {

return {
terminal: true,
multiElement: true,
priority: 5000, // This setting is used in conjunction with the later call to $compile() to prevent infinite recursion of compilation
compile: function dirPaginationCompileFn(tElement, tAttrs) {

var expression = tAttrs.dirPaginate;
// regex taken directly from https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L211
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

var filterPattern = /\|\s*itemsPerPage\s*:[^|]*/;
if (match[2].match(filterPattern) === null) {
throw ‘pagination directive: the \’itemsPerPage\’ filter must be set.';
}
var itemsPerPageFilterRemoved = match[2].replace(filterPattern, ”);
var collectionGetter = $parse(itemsPerPageFilterRemoved);

// If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
// dir-pagination-controls directives that may be looking for this ID.
var rawId = tAttrs.paginationId || DEFAULT_ID;
paginationService.registerInstance(rawId);

return function dirPaginationLinkFn(scope, element, attrs) {

// Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
// potentially register a new ID if it evaluates to a different value than the rawId.
var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
paginationService.registerInstance(paginationId);

var repeatExpression;
var idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);
if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:[^|]*)/, “$1 : ‘” + paginationId + “‘”);
} else {
repeatExpression = expression;
}

// Add ng-repeat to the dom element
if (element[0].hasAttribute(‘dir-paginate-start’) || element[0].hasAttribute(‘data-dir-paginate-start’)) {
// using multiElement mode (dir-paginate-start, dir-paginate-end)
attrs.$set(‘ngRepeatStart’, repeatExpression);
element.eq(element.length – 1).attr(‘ng-repeat-end’, true);
} else {
attrs.$set(‘ngRepeat’, repeatExpression);
}

var compiled = $compile(element, false, 5000); // we manually compile the element again, as we have now added ng-repeat. Priority less than 5000 prevents infinite recursion of compiling dirPaginate

var currentPageGetter;
if (attrs.currentPage) {
currentPageGetter = $parse(attrs.currentPage);
} else {
// if the current-page attribute was not set, we’ll make our own
var defaultCurrentPage = paginationId + ‘__currentPage';
scope[defaultCurrentPage] = 1;
currentPageGetter = $parse(defaultCurrentPage);
}
paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);

if (typeof attrs.totalItems !== ‘undefined’) {
paginationService.setAsyncModeTrue(paginationId);
scope.$watch(function () {
return $parse(attrs.totalItems)(scope);
}, function (result) {
if (0 <= result) {
paginationService.setCollectionLength(paginationId, result);
}
});
} else {
scope.$watchCollection(function () {
return collectionGetter(scope);
}, function (collection) {
if (collection) {
paginationService.setCollectionLength(paginationId, collection.length);
}
});
}

// Delegate to the link function returned by the new compilation of the ng-repeat
compiled(scope);
};
}
};
}]);

module.directive(‘dirPaginationControls’, [‘paginationService’, ‘paginationTemplate’, function (paginationService, paginationTemplate) {

var numberRegex = /^\d+$/;

/**
* Generate an array of page numbers (or the ‘…’ string) which is used in an ng-repeat to generate the
* links used in pagination
*
* @param currentPage
* @param rowsPerPage
* @param paginationRange
* @param collectionLength
* @returns {Array}
*/
function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
var pages = [];
var totalPages = Math.ceil(collectionLength / rowsPerPage);
var halfWay = Math.ceil(paginationRange / 2);
var position;

if (currentPage <= halfWay) {
position = ‘start';
} else if (totalPages – halfWay < currentPage) {
position = ‘end';
} else {
position = ‘middle';
}

var ellipsesNeeded = paginationRange < totalPages;
var i = 1;
while (i <= totalPages && i <= paginationRange) {
var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);

var openingEllipsesNeeded = (i === 2 && (position === ‘middle’ || position === ‘end’));
var closingEllipsesNeeded = (i === paginationRange – 1 && (position === ‘middle’ || position === ‘start’));
if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
pages.push(‘…’);
} else {
pages.push(pageNumber);
}
i++;
}
return pages;
}

/**
* Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
*
* @param i
* @param currentPage
* @param paginationRange
* @param totalPages
* @returns {*}
*/
function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
var halfWay = Math.ceil(paginationRange / 2);
if (i === paginationRange) {
return totalPages;
} else if (i === 1) {
return i;
} else if (paginationRange < totalPages) {
if (totalPages – halfWay < currentPage) {
return totalPages – paginationRange + i;
} else if (halfWay < currentPage) {
return currentPage – halfWay + i;
} else {
return i;
}
} else {
return i;
}
}

return {
restrict: ‘AE’,
templateUrl: function (elem, attrs) {
return attrs.templateUrl || paginationTemplate.getPath();
},
scope: {
maxSize: ‘=?’,
onPageChange: ‘&?’,
paginationId: ‘=?’
},
link: function dirPaginationControlsLinkFn(scope, element, attrs) {

// rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
// not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is
// no corresponding dir-paginate directive and wrongly throwing an exception.
var rawId = attrs.paginationId || DEFAULT_ID;
var paginationId = scope.paginationId || attrs.paginationId || DEFAULT_ID;

if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
var idMessage = (paginationId !== DEFAULT_ID) ? ‘ (id: ‘ + paginationId + ‘) ‘ : ‘ ‘;
throw ‘pagination directive: the pagination controls’ + idMessage + ‘cannot be used without the corresponding pagination directive.';
}

if (!scope.maxSize) { scope.maxSize = 9; }
scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;

var paginationRange = Math.max(scope.maxSize, 5);
scope.pages = [];
scope.pagination = {
last: 1,
current: 1
};
scope.range = {
lower: 1,
upper: 1,
total: 1
};

scope.$watch(function () {
return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
}, function (length) {
if (0 < length) {
generatePagination();
}
});

scope.$watch(function () {
return (paginationService.getItemsPerPage(paginationId));
}, function (current, previous) {
if (current != previous) {
goToPage(scope.pagination.current);
}
});

scope.$watch(function () {
return paginationService.getCurrentPage(paginationId);
}, function (currentPage, previousPage) {
if (currentPage != previousPage) {
goToPage(currentPage);
}
});

scope.setCurrent = function (num) {
if (isValidPageNumber(num)) {
paginationService.setCurrentPage(paginationId, num);
}
};

function goToPage(num) {
if (isValidPageNumber(num)) {
scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
scope.pagination.current = num;
updateRangeValues();

// if a callback has been set, then call it with the page number as an argument
if (scope.onPageChange) {
scope.onPageChange({ newPageNumber: num });
}
}
}

function generatePagination() {
var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;

scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
scope.pagination.current = page;
scope.pagination.last = scope.pages[scope.pages.length – 1];
if (scope.pagination.last < scope.pagination.current) {
scope.setCurrent(scope.pagination.last);
} else {
updateRangeValues();
}
}

/**
* This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
* template to display the current page range, e.g. “showing 21 – 40 of 144 results”;
*/
function updateRangeValues() {
var currentPage = paginationService.getCurrentPage(paginationId),
itemsPerPage = paginationService.getItemsPerPage(paginationId),
totalItems = paginationService.getCollectionLength(paginationId);

scope.range.lower = (currentPage – 1) * itemsPerPage + 1;
scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
scope.range.total = totalItems;
}

function isValidPageNumber(num) {
return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
}
}
};
}]);

module.filter(‘itemsPerPage’, [‘paginationService’, function (paginationService) {

return function (collection, itemsPerPage, paginationId) {
if (typeof (paginationId) === ‘undefined’) {
paginationId = DEFAULT_ID;
}
if (!paginationService.isRegistered(paginationId)) {
throw ‘pagination directive: the itemsPerPage id argument (id: ‘ + paginationId + ‘) does not match a registered pagination-id.';
}
var end;
var start;
if (collection instanceof Array) {
itemsPerPage = parseInt(itemsPerPage) || 9999999999;
if (paginationService.isAsyncMode(paginationId)) {
start = 0;
} else {
start = (paginationService.getCurrentPage(paginationId) – 1) * itemsPerPage;
}
end = start + itemsPerPage;
paginationService.setItemsPerPage(paginationId, itemsPerPage);

return collection.slice(start, end);
} else {
return collection;
}
};
}]);

module.service(‘paginationService’, function () {

var instances = {};
var lastRegisteredInstance;

this.registerInstance = function (instanceId) {
if (typeof instances[instanceId] === ‘undefined’) {
instances[instanceId] = {
asyncMode: false
};
lastRegisteredInstance = instanceId;
}
};

this.isRegistered = function (instanceId) {
return (typeof instances[instanceId] !== ‘undefined’);
};

this.getLastInstanceId = function () {
return lastRegisteredInstance;
};

this.setCurrentPageParser = function (instanceId, val, scope) {
instances[instanceId].currentPageParser = val;
instances[instanceId].context = scope;
};
this.setCurrentPage = function (instanceId, val) {
instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
};
this.getCurrentPage = function (instanceId) {
var parser = instances[instanceId].currentPageParser;
return parser ? parser(instances[instanceId].context) : 1;
};

this.setItemsPerPage = function (instanceId, val) {
instances[instanceId].itemsPerPage = val;
};
this.getItemsPerPage = function (instanceId) {
return instances[instanceId].itemsPerPage;
};

this.setCollectionLength = function (instanceId, val) {
instances[instanceId].collectionLength = val;
};
this.getCollectionLength = function (instanceId) {
return instances[instanceId].collectionLength;
};

this.setAsyncModeTrue = function (instanceId) {
instances[instanceId].asyncMode = true;
};

this.isAsyncMode = function (instanceId) {
return instances[instanceId].asyncMode;
};
});

module.provider(‘paginationTemplate’, function () {

var templatePath = ‘Scripts/dirPagination.tpl.html';

this.setPath = function (path) {
templatePath = path;
};

this.$get = function () {
return {
getPath: function () {
return templatePath;
}
};
};
});
})();

end dirPagination.js

dirPagination.css start

/* Styles go here */
input.search-query {
padding-left:26px;
}

form.form-search {
position: relative;
}

form.form-search:before {
content:”;
display: block;
width: 14px;
height: 14px;
background-image: url(http://getbootstrap.com/2.3.2/assets/img/glyphicons-halflings.png);
background-position: -48px 0;
position: absolute;
top:8px;
left:8px;
opacity: .5;
z-index: 1000;
}

.labeltext {
display: inline-block;
margin:5px;
}

.IntSpinner.ng-valid {

width: 50px;
height: 30px;

}
.search-query.ng-valid {
height: 30px;

}

table, th , td {
text-align:left;
border: none;
border-collapse: collapse;
padding: 2px;

font-weight:none;
}
table tr:nth-child(odd) {
background-color: #f1f1f1;
}
table tr:nth-child(even) {
background-color: #ffffff;

}

a {
color:black;
}
/* experimental*/
/*.infotext {
margin:5px;
width: 100px;
}*/

.btn-group inline {
}
.btn {
background-color:lightgray;
margin: 0px;
display: inline-block;
width: 100%;
height: 100%;
border-color:dimgrey;
border-width:thin;

}
.disabled .btn {
color:lightgrey;
font-weight: bold;
background-color:white;
border-color:lightgrey;
}
.active .btn {
color:white;
background-color:darkgray;
border-color:black;
border-width:thin;
}

dirPagination.css end 

angulargetdata.js start 

var myApp = angular.module(‘myApp’, [‘angularUtils.directives.dirPagination’]);

myApp.controller(‘dataController’, function ($scope, $http) {

var id = $(‘#Idno’).val();
var serviceurl = $(‘#sourceURL’).val();
$scope.baseURLvalue = serviceurl.replace(‘{0}’,id);
$scope.currentPage = 1;
$scope.pageSize = 10;
$scope.rows = [];
$scope.predicate = ‘title';
$scope.catfilter = “”;
$http({
method: ‘JSON’,
url: $scope.baseURLvalue
}).success(function (data, status, header, config) {
$scope.filtered = data.rows;
$scope.rows = data.rows;
})
.error(function (data, status, header, config) {

});

$scope.applyCategory = function (prop, value) {

$(“.btn-group-vertical > .btn”).removeClass(“active”);

if (!prop || !value) {
$(‘#CurrentFilter’).text(‘Category : All’);
$scope.filtered = $scope.rows;
return;
}
$scope.filtered = $scope.rows.filter(function (ritem) {
$(‘#CurrentFilter’).text(‘Category : ‘+value);
return ritem[prop] === value;
});
};

})

myApp.filter(‘nonNumstrip’, function () {
return function (input) {
var output = “”;
output = input.replace(/[^0-9]+/g, “”);
return (output);
}

});

function OtherController($scope) {
$scope.pageChangeHandler = function (num) {
console.log(‘going to page ‘ + num);
};
}

//myApp.controller(‘dataController’, dataController);
myApp.controller(‘OtherController’, OtherController);

angulargetdata.js end 

Follow

Get every new post delivered to your Inbox.