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.

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 

This takes  Michael Bromley’s how to paginate anything in angular js  & adds  some extra stuff, the goal being to combine an MVC Index Page & search page into one:

Additions include

  • Data loaded via a web service
  • Run time parameter to point the web service at a new back end record
  • Run time categorisation passed from ..net MVC controller
  • Dynamically poulated radio button for additional filtering
  • Bootstrap button to launch other link

dirPagination.js is as is on Michaels’ site http://www.michaelbromley.co.uk/blog/108/paginate-almost-anything-in-angularjs

EXCEPT that I tweaked the location of the templatePath  :

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

This is the MVC controller so I can launch the system with

http://localhost:1234/AngularExperiment?id=1234

/////////////////////////////////////////// start of  AngularExperimentController.cs

public class AngularExperimentController : Controller
{
public ActionResult Index(int id=1234)

{
ViewBag.Id_value = id.ToString();
var categories = new List<string>
{
“category one”,” Cat 2″,”etc”,”Termination”
};

// This should be strongly typed,  & come from external db or config file  but this is a Proof of concept

ViewBag.categorylist = categories;

return View();
}

}}

/////////////////////////////////////////// end of  AngularExperimentController.cs

///////////////////////////////////////////  start of index.cshtl

@model mySystem.Models.Basic
<!DOCTYPE html>
<html lang=”en” ng-app=”myApp”>

<head>
@* Javascripts*@
<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 data-require=”bootstrap@3.1.1″ data-semver=”3.1.1″ src=”//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js”></script>
<script src=”../../Scripts/dirPagination.js”></script>
<script src=”../../Scripts/angulargetdata.js”></script>
@*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 href=”../../Scripts/dirPagination.css” rel=”stylesheet” />

@* JQuery to set default value of Category Radio Button *@
<script>
$(document).ready(function () {

radiobtn = document.getElementById(“defaultCat”);
radiobtn.checked = true;
});
</script>

</head>

<body>

@* Sourceurl should be externally defined by external config similar to category values  *@

<input type=”hidden” id=”sourceURL” value=”http://localhost:1234/searchfordocuments?identifier=X&employeeid={0}”>
<input type =”hidden” id=”Idno” value=”@ViewBag.Id_value”>

<div ng-controller=”dataController” >
@*Build Free text search search.$ is used to filter json object according to any field *@

<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”>
Page : {{ currentPage }}
</div>
<br>

@*Build category filter – we’ll always want an All category that clears this filter *@

<div class=”span3″>
<div id=”dynbut”>
<input type=’radio’ id=”defaultCat” ng-model=’search.categoryheading’ name=’group1′ value=’ ‘ > All Items

@*Other categories are  built from the categories passed in from the viewbag.categorylist*@

@foreach (var item in @ViewBag.categorylist)
{
<br /> <input type=”radio” ng-model=”search.categoryheading” name=”group1″ value=”@item” /> @item
}
</div>
</div>

@*Build clickable / sortable table *@

<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 = ‘cathead'; 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%;”></th>
</tr>
@*populate table with json stream passed into angular filters and in the final colume well add a bootstrap button*@
<tr dir-paginate=”item in rows | filter:search | filter : applyCategory() | orderBy:predicate:reverse | itemsPerPage: pageSize” current-page=”currentPage”>
<td> <a href=”/EDoc/Launch?identifier=X&employeeid={{item.id}}&filename={{item.linkfile}} “> {{ item.title }} </a></td>
<td> <a href=”/EDoc/Launch?identifier=X&employeeid={{item.id}}&filename={{item.linkfile}} “> {{ item.cathead}} </a></td>
<td> <a href=”/EDoc/Launch?identifier=X&employeeid={{item.id}}&filename={{item.linkfile}} “>{{ item.createdby}} </a></td>
<td> <a class=”btn btn-mini btn-warning btn-mini” style=”FONT-WEIGHT: bold; COLOR: white” href=”/EDoc/Delete?identifier=X&amp;employeeid={{item.id}}&amp;filename={{item.linkfile}}”>Delete</a>
</a></td>

</tr>
</table>
<br />
</div>
<div class=”span12″>

</div>
</div>
@*pagination controller is dynamically populated with dirPagination.tpl.html*@
<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>

///////////////////////////////////////////  end of index.cshtl

////////////////// start of angulargetdata.js

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.rows = data.rows;
})
.error(function (data, status, header, config) {
});
$scope.applyCategory = function (filter) {
return filter;
}

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

//function to strip non numerics out of JSON field

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

});

function OtherController($scope) {

}
myApp.controller(‘OtherController’, OtherController);

////////////////// end  of angulargetdata.js

//////////////// paginationcontroller dirPagination.tpl.html

<ul class=”btn-group” ng-if=”1 < pages.length” >
<li ng-if=”boundaryLinks” ng-class=”{ disabled : pagination.current == 1 }” class=”btn btn-small” >
<a href=”” ng-click=”setCurrent(1)”>&laquo; </a>
</li>
<li ng-if=”directionLinks” ng-class=”{ disabled : pagination.current == 1 }” class=”btn btn-small”>
<a 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 == ‘…’ } “class=”btn btn-small”>
<a href=”” ng-click=”setCurrent(pageNumber)”> {{pageNumber}} </a>
</li>

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

//////////////// end of dirPagination.tpl.html

CSS to build a black yellow stripey ‘maintenance’ border for Eg App_Offline.htm

unavailable

The “<meta http-equiv=”X-UA-Compatible” content=”IE=Edge” />” was added when I moved this from local host to an IIS server.  It was not recognising the css3 elements & created a white box on a flat gray back ground only.  Additionally to render the black / yellow pattern to grey / white.  Change the background in #Wrapper to

 background: repeating-linear-gradient(45deg, white, white 35px, grey 35px, grey 71px);

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

<html xmlns=”http://www.w3.org/1999/xhtml&#8221; >
<head>
<title>Down for Maintenance</title>

<meta http-equiv=”X-UA-Compatible” content=”IE=Edge” />
<style type=”text/css”>
* {
margin: 0;
padding: 0;
}

body {
line-height: 1.75em;
font-family: “Open Sans”, sans-serif;
background-color: #999;
background: linear-gradient(-45deg, grey , black );
background-size: 100% 40%;
background-repeat: repeat-y;
}

br.clearfix {
clear: both;
}

h2 {
border: 15px;
font-size: 2.5em;
font-family: Arial, sans-serif;
margin: 1em;
}

p {
margin: 1.5em;
}

img.alignleft {
float: left;
margin: 5px 30px 30px 0;
border-radius: 6px;
}

#content {

background: white;
padding: 0.1px;
width: 900px;
margin: 50px;
border-style:solid;
border-width:thin;
border-color:black;
}

#page {
border: 15px ;
position: relative;
width: 900px;
}

/*this is the frame */

#wrapper {
background: repeating-linear-gradient(45deg, black, black 35px, yellow 35px, yellow 70px);
min-height: 100%;
width: 1000px;
position: relative;
top: 150px;
padding:1px;
margin: 0 auto 0 auto;
}

</style>

</head>
<body>
<div><img class=”alignleft” src=”url to a logo.png” alt=”Down for maintenance” title=”Down for maintenance” /> </div>

<div id=”wrapper”>
<div id=”content”>
<h2>System unavailable </h2>
<p>
The system is currently undergoing scheduled maintenance. The system will be back shortly. Please check back in 15 minutes. Thank you for your patience.
</p>
</div>
</div>
</body>
</html>

Use Angular to dynamically filter an external json list and format it with an Angular filter or css

The code below combines two ‘try it now’ exercises  from the W3Schools.com course on Angular js to do the following :

1) Read a json list of customers & their countries from an external url:

http://www.w3schools.com//website/Customers_JSON.php  ( The data returned from this is shown at  the end of this text )

2) Filter the list in real time to show only rows containing the text entered in the Input_Text_Filter field.

3) Format the list in two contradictory ways ( for demonstration purposes), to either :

 3.1) Make the first letter of the first word uppercase, & then everything else is lower case – using the Angular app, custom filter called ‘ucf‘.

or

3.2) Make the first letter of EVERY word upper case : using a css style definition, with a class called ‘capitalize’ assigned to it.  The style definition for li.capitalize  formats the text so that every word in that element starts with a capital.

Both functions are run, to allow you to delete the unnecessary, The two approaches conflict, but capitalize wins out in this case.  In the real world use ONE approach,  not both:


<!DOCTYPE html>

<html>
<head>

<style>
li.capitalize {
text-transform: capitalize;
}
</style>

</head>

<body>

<div ng-app=”App” ng-controller=”customersController“>
<p>Filtering input:</p>
<p><input type=”text” ng-model=”Input_Text_Filter “></p>
<ul>
  <li ng-repeat=”x in names | filter:Input_Text_Filter | orderBy:’country‘” class=”capitalize”>
    {{ (x.Name |ucf) + ‘, ‘ + x.Country }}
  </li>
</ul>
</div>
<script>
var app = angular.module(‘App’,[]);
function customersController($scope,$http) {
  .success(function(response) {$scope.names = response;});
}
app.filter(‘ucf’, function()
{
    return function(word)
    {
        return word.substring(0,1).toUpperCase() + word.slice(1).toLowerCase();
    }
});
</script>
</body>

</html>


Json Data returned at time of writing from http://www.w3schools.com//website/Customers_JSON.php

[ { “Name” : “Alfreds Futterkiste”, “City” : “Berlin”, “Country” : “Germany” }, { “Name” : “Berglunds snabbköp”, “City” : “Luleå”, “Country” : “Sweden” }, { “Name” : “Centro comercial Moctezuma”, “City” : “México D.F.”, “Country” : “Mexico” }, { “Name” : “Ernst Handel”, “City” : “Graz”, “Country” : “Austria” }, { “Name” : “FISSA Fabrica Inter. Salchichas S.A.”, “City” : “Madrid”, “Country” : “Spain” }, { “Name” : “Galería del gastrónomo”, “City” : “Barcelona”, “Country” : “Spain” }, { “Name” : “Island Trading”, “City” : “Cowes”, “Country” : “UK” }, { “Name” : “Königlich Essen”, “City” : “Brandenburg”, “Country” : “Germany” }, { “Name” : “Laughing Bacchus Wine Cellars”, “City” : “Vancouver”, “Country” : “Canada” }, { “Name” : “Magazzini Alimentari Riuniti”, “City” : “Bergamo”, “Country” : “Italy” }, { “Name” : “North/South”, “City” : “London”, “Country” : “UK” }, { “Name” : “Paris spécialités”, “City” : “Paris”, “Country” : “France” }, { “Name” : “Rattlesnake Canyon Grocery”, “City” : “Albuquerque”, “Country” : “USA” }, { “Name” : “Simons bistro”, “City” : “København”, “Country” : “Denmark” }, { “Name” : “The Big Cheese”, “City” : “Portland”, “Country” : “USA” }, { “Name” : “Vaffeljernet”, “City” : “Århus”, “Country” : “Denmark” }, { “Name” : “Wolski Zajazd”, “City” : “Warszawa”, “Country” : “Poland” } ]

NUnit validation of a web service at “http://localhost:1234/api/values”;

NUnit validation of a web service at “http://localhost:1234/api/values&#8221;;

UnitTest.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVCWebApi.Controllers;
using NUnit.Framework;
using System.Web.Http;
using System.Net.Http;
using MVCWebApi.Models;
using System.Configuration;
using System.Net;
using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace MVCWebApi.Controllers.Tests
{
[TestFixture()]
public class UnitTest1
{

private string UrlBase = “http://localhost:49493/&#8221;;
private string servicePath = “api/values”;
private HttpClient client;
private HttpResponseMessage response;
private string jsonData;
private List<Person> list;

[SetUp]
public async void SetUP()
{

client = new HttpClient();
client.BaseAddress = new Uri(UrlBase);

// get service header data into a queryable form

response = client.GetAsync(servicePath).Result;
// assuming service works load json into a string
jsonData = client.GetStringAsync(servicePath).Result;

// Parse json into a custom c# class – in this case, Person
list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(jsonData);
}
// Test for OK status code

[Test]
public void GetResponseIsSuccess()
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
// Test for returned data is JSON

[Test]
public void GetResponseIsJson()
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));

Assert.AreEqual(“application/json”, response.Content.Headers.ContentType.MediaType);
}

// Test for authentication

[Test]
public void GetAuthenticationStatus()
{
Assert.AreNotEqual(HttpStatusCode.Unauthorized, response.StatusCode);

}
// Test parsed ( people) object contains data from the json

[Test]
public void jsonIsvalid()
{
Assert.GreaterOrEqual(list.Count, 0);

}

// Test parsed ( people) object contains a value for specific name

[Test]
public void jsonIsvalid2()
{
bool found = false;
foreach (Person people in list)
{
if (people.Name == “sankar parida”)
{ found = true; }
}

Assert.IsTrue(found);
}

// Test parsed ( people) object contains another specific name

[Test]
public void jsonIsvalid3()
{
bool found = false;
foreach (Person people in list)
{
if (people.Address == “cuttack”)
{ found = true; }
}

Assert.IsTrue(found);
}
}
}

Where MVCWebApi looks like

API / ValuesController

using MVCWebApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MVCWebApi.Controllers
{
public class ValuesController : ApiController
{
PersonEntities db = new PersonEntities();
// GET api/values
public IEnumerable<Person> Get()
{
return db.Persons.ToList();
//return new string[] { “value1″, “value2″ };
}
// GET api/values/5
public Person Get(int id)
{
return db.Persons.Find(id);
//return “value”;
}
// POST api/values
public void Post(List<string> val)//(Person obj )
{
try
{
Person obj = new Person();
obj.Name = val[0];
obj.Address = val[1];
obj.DOB = Convert.ToDateTime(val[2]);
db.Persons.Add(obj);
db.SaveChanges();
}
catch (Exception) { }
}
// PUT api/values/5
public void Put(int id, Person obj)//(int id, [FromBody]string value)
{
try
{
db.Entry(obj).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
catch (Exception) { }
}
//This method for update through Json
public void Put(List<string> val)
{
try
{
int id = Convert.ToInt32(val[3]);
Person obj = db.Persons.Find(id);
obj.Name = val[0];
obj.Address = val[1];
obj.DOB = Convert.ToDateTime(val[2]);
db.Persons.Add(obj);
db.Entry(obj).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
catch (Exception) { }
}
// DELETE api/values/5
public void Delete(int id)
{
Person obj = db.Persons.Find(id);
db.Persons.Remove(obj);
db.SaveChanges();
}
}
}

Controllers / PersonController is

using MVCWebApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCWebApi.Controllers
{
public class PersonController : Controller
{
ValuesController db = new ValuesController();
public ActionResult Index()
{
var list = db.Get(); //Getting List of records from web API
return View(list);
}
public ActionResult Index2()
{
return View();
}
public ActionResult Details(int id)
{
Person person = db.Get(id);
return View(person);
}

public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Person obj)
{
try
{
List<string> person = new List<string>();
person.Add(obj.Name);
person.Add(obj.Address);
person.Add(obj.DOB.ToString());
db.Post(person);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}

public ActionResult Edit(int id)
{
Person person = db.Get(id);
return View(person);
}
[HttpPost]// POST: /Person/Edit/5
public ActionResult Edit(int id, Person obj)
{
try
{
db.Put(id, obj);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}

// GET: /Person/Delete/5
public ActionResult Delete(int id)
{
Person person = db.Get(id);
return View(person);
}
[HttpPost]// POST: /Person/Delete/5
public ActionResult Delete(int id, FormCollection collection)
{
try
{
db.Delete(id);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}
}
}

Person.cs

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace MVCWebApi.Models
{
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime DOB { get; set; }
}
public class PersonEntities : DbContext
{
public DbSet<Person> Persons { get; set; }
}
}

Follow

Get every new post delivered to your Inbox.