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 focused more on C# .NET, Sharepoint, JQuery , Javascript, CSS  & HTML5 .

Richard

Dynamic URL links from web.config which are rewritten in jquery to reflect a querystring parameter from the current page

The need for this came because we needed an extendable menu (hence the web config settings) for a page, which had a  querystring parm of  ‘ID = nnnn’, was to be injected into those menus at run time. In standard MVC you could do this in the controller. But the previous developer had written this as a single page application, & also got the system to dynamically overwrite the querystring & store some of those values as cookies as the user navigated the system!  Throughout this example {IDVAR} is the value of id=parameter

So we needed to use the controller & jquery to address this.

Web.Config values for url template, title, & which button to mark as active :

<add key="RemotelinksUtls" value="http://app1:1234/#/dash?ID={IDVAR}/&z=m|http://app3/?q={IDVAR}&vv=po|http://app2:999/Home/Index?ID={IDVAR}&=ff" />
<add key="RemotelinksTitle" value="Application One | App3 | app Two " />
<add key="RemotelinksActive" value="1" />

Snippet from the home controller

 public ActionResult Index()
 {
 Regex pattern = new Regex("[:.0123456789]");
// get config values into memory variables
 string SLinks = (ConfigurationManager.AppSettings["RemotelinksUtls"]);
 string Titles = ConfigurationManager.AppSettings["RemotelinksTitle"];
 string ActiveString = ConfigurationManager.AppSettings["RemotelinksActive"];
// Convert the ActiveString into an int so we can compare the loop position with it.
 int ActiveElement = 0;
 int.TryParse(ActiveString, out ActiveElement);
 // Split the url &title strings at | into lists
 List<string> RemoteList = new List<string>();
 List<String> LList = SLinks.Split('|').ToList();
 List<String> TList = Titles.Split('|').ToList();
 int LinkCnt = LList.Count;
// Use the lists to create a link button for each element & set one to be active 
 string templink = "";
 for (int i = 0; i < LinkCnt; i++)
 {
 templink = "<span ><a href=\"http://" + LList[i] + "\" class=";

 if (i == ActiveElement)
 { // if the current element is the one which is set to be active, set its class to primary
 templink += "\"btn btn-primary\" >";
 }
 else
 {// Or set its class to default
 templink += "\"btn btn-default\" >";
 }
 templink += TList[i] + "</a></span> ";
//add the link to the RemoteList list 
 RemoteList.Add(templink);
 }
// Put the RemoteList into the Remotelinks viewbag item
 ViewBag.Remotelinks = RemoteList;
return View();
 }

In the index.cshtml loop through viewbag.Remotelinks, nested under the div remlinks to render the HTML link buttons

At this point, the hrefs will all include the static value  q={IDVAR}. In an ordinary MVC program we’d have reset {IDVAR} in the controller.  But we aren’t going back to the controller because this is a single page application.  Additionally as the querystring gets rewritten,  we need to set / check the cookie values.  It would be more efficient to have reused some of the JS variables, but for the sake of clarity I have kept them distinct

This is done using JQUERY & javascript:

$( document ).ready(function() {
// if there's an id parm in the querystring , use that , otherwise use the existing cookie
var qparm = "";
//Set scope for in memory variable
var idno = qs("id");
if (!$.isEmptyObject(idno))
{//Set the cookie
setCookie("IDNumber",idno)
qparm = idno;
}
else {//Read the Cookie
var Lastno=getCookie("IDNumber")
qparm = Lastno;
}

//set all of the hrefs under the 'remlinks' div, so that the relevant id value 
// ( from query string if its there, from the cookie if its not)  replaces
// {idvar} - some websamples show you concatenating this, 
//but suppose we have hash variables too...

$('#remlinks a').each(function () {
var href = $(this).attr('href');
if (href) {
href = href.replace('{IDVAR}',qparm)
$(this).attr('href', href);
}
});
// end remlinks each loop
});
//end document ready fn

function qs(key) {
//parse querystring for key value
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); 
// escape RegEx control chars
var match = location.search.match(new RegExp("[?&]" + key + "=([^&]+)(&|$)"));
return match && decodeURIComponent(match[1].replace(/\+/g, " "));
}

function setCookie(key, value) {
var expires = new Date();
//if needed, expire cookie tomorrow
//expires.setTime(expires.getTime() + (1 * 24 * 60 * 60 * 1000));
// document.cookie = key + '=' + value + '; path =/;expires=' + expires.toUTCString();
// or leave expires off, so we loose cookie when browser closes 
document.cookie = key + '=' + value + '; path =/;';
}

function getCookie(key) {
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
return keyValue ? keyValue[2] : null;
}

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; }
}}}
Follow

Get every new post delivered to your Inbox.