jones.busy

technical musings of a caffeine converter

NAVIGATION - SEARCH

Ordering array of objects in Select Dropdown with Angular

As a newbie to angular and, in some sense, to javascript, when populating a select dropdown from an array of objects returned from my api, I fell into the trap of expecting the “| orderBy” filter to work as described. I’d already come a cropper with dynamically populated selects and trying to get the correct property to be shown so I probably should have been less surprised than I was.

Here’s the unordered select, populated from an array, ‘tenantTypes’ which itself is populated from an api call returning an array of a .net class, TenantType with the properties Id: long and Name: string.

<select ng-model="controller.registration.organisationType" ng-options="key as value.name for (key, value) in controller.tenantTypes" class="form-control"> <option value="">Organisation Type...</option> </select>

The ng-options statement took me some time to work out, but it worked as expected so I hope to be able to simply add the following in:

| orderBy:’name’

Not happening. The ng-options statement needed some jiggery-pokery to work with the object array and so does the sorting. After looking around, I came across this excellent filter created by Justin Klemm:

http://justinklemm.com/angularjs-filter-ordering-objects-ngrepeat/

Looks bang on the money so I grabbed it and added the following in:

| orderObjectBy:’name’

Still not happening: Once I’d ruled out my usual issues (reference the script, injecting it into the app module etc.), I started scratching my head. The only difference I could see was that this filter used the ng-repeat approach in its example. I knew I could have declared my select using <option ng-repeat… instead of the ng-options route but I had expected them to work the same under the bonnet. Well, if they do, then the ng-options does not work well with the custom filter as I finally managed to get my select ordering nicely by re-coding the select design to the following:

<select ng-model="controller.registration.organisationType" class="form-control"> <option value="" ng-selected="true">Organisation Type...</option> <option ng-repeat="type in controller.tenantTypes | orderObjectBy:'name'" value="{{type.id}}">{{type.name}} </option> </select>

Again, seasoned angular devs may be able to point out some glaring mistakes or assumptions I have made and please do, but just in case anyone else has tried or is trying the same approach, this might at least save them a couple of hours of head scratching Smile

Extending Angular $http service

Recently, I’ve been working with AngularJs, developing an Azure targeted application with an Entity Framework (Code First) backend, Web Api v2.0 middle tier and an AngularJs front-end. I like AngularJS for a number of reasons, one of which is that, as a .Net, WPF developer for most of my time, having so many different options and approaches to consider with client-side web development, having a framework that minimises those options into one core approach is beneficial to me. I completely understand that this same reason could be seen as a negative and I am well aware of the impending re-write of Angular due this year but to move from the safety net of a strongly typed .net background to the javascript playground, AngularJS feels a little like I’ve got some stabilisers on my dev bike Smile

Using the $http service over the weekend, I stumbled across a couple of issues:

1. Calling a web api method with a simple string parameter:

public async Task<IHttpActionResult> PostRole([FromBody]string roleName)

The issue is that by sending the string across as a variable causes some misinterpretation of as it as a json literal so instead of the value finding its way to the web api method, you get a null object instead. So, instead of calling the following:

return $http.post(serviceBase + 'api/admin/roles', roleName);

You need to do this:

return $http.post(serviceBase + 'api/admin/roles', "'" + roleName + "'");

2. Calling a ‘get’ method from within IE:

public async Task<IHttpActionResult> Get()

For some unknown reason, no matter how hard I tried to stop this from caching when testing in IE, I had no luck. In the end, the only approach that worked for me was to randomise the request as follows:

return $http.get(serviceBase + 'api/admin/roles?rnd=' + new Date().getTime());

It’s not very pretty is it? Firstly, I must asking anyone reading that has any suggestions or comments about what I’ve done or the issues I have faced, please do get in touch as it is perfectly feasible I have completely crossed my wires with this and there is another approach I should have taken. But for now, these are the issues I hit, Googled, and then put in some workarounds.

Now, having found a workaround, I realised that these sorts of calls could be a regular occurrence and so I don’t want to have to remember to apply these fixes each time. My first thought was to see whether I could extend the $http service to wrap this functionality in a neat bundle for me. Coming from C# extension methods i looked at the following options:

1. prototyping

2. behaviours

3. providers

I’ve heard about most of these approaches but never seen any in practice. I really wanted to stick to passing in $http and just being able to either handle or extend the functionality to handle these requirements but could not get this to work with my limited javascript knowledge. In the end, I create a new service, extendedHttpService, which is actually a factory class that returns an extended version of the $http service:

(function (ng, app) { "use strict"; app.factory('extendedHttpService', ['$http', function($http) { var forceGet = function(url) { return $http.get(url + "?rnd=" + new Date().getTime()); }; var postString = function(url, str) { return $http.post(url, "\"" + str + "\""); }; $http['forceGet'] = forceGet; $http['postString'] = postString; return $http; } ]); })(angular, app);

Allowing me to then call the extended functions as follows:

(function (ng, app) { "use strict"; app.service('rolesService', ['extendedHttpService', 'appSettings', function (extendedHttpService, appSettings) { var serviceBase = appSettings.apiServiceBaseUri; this.getAllRoles = function () { return extendedHttpService.forceGet(serviceBase + "api/admin/roles/"); }; this.createRole = function (roleName) { return extendedHttpService.postString(serviceBase + 'api/admin/roles', roleName); }; this.updateRole = function (role) { return extendedHttpService.put(serviceBase + 'api/admin/roles', role); }; }]); })(angular, app);

This is much neater, but I’d love to hear on other approaches that I could have used in this scenario.

Separating responsibility client-side in MVC with RequireJs and RMP

I my previous post, I talked about splitting my Knockout view model from the rest of my JavaScript by using employing the  Revealing Module Pattern to my code. I mentioned at the end of the post that I could take it one step further and split the code into separate script files then use something like RequireJs to organise the dependencies. It turned out to be a bit more fiddly than I expected so here’s my experience in the hope that it may point other newbies in this area in the right direction (or indeed, prompt any JS veterans to point out my mistakes)

Firstly, I want to tackle the important issue of “why do it if it’s complicated?”. Granted, what I had before worked but, as with most of my development, my code is an evolutionary product and whilst I always aim for it to be complete, I always work under the expectation that either I am going to have to come back here to make further improvements or, more importantly, someone else might. With that in mind, whilst there may seem to be an unjustified overhead initially in spending time introducing another library and re-organising existing, working code, I do believe that it’s one worth paying. Secondly, it’s mostly only complicated because of the learning curve and that’s really a one-off that can benefit in the long term elsewhere.

I had already tidied up my ‘viewmodel’ script by separating out the various areas of concern into three different modules: viewmodel, view and what I referred as rendering which was responsible for manipulating the view based on user interactions and start up defaults. These modules were contained in a separate file leaving only the following script inside the actual view:

   1: @section Scripts



   2: {



   3:     <script src="~/scripts/app/page.details.js"></script>



   4:     <script type="text/javascript">



   5:         (function () {



   6:             var viewModel = details.initialiseViewModel(ko.mapping.fromJS(@Html.Raw(Json.Encode(Model))));



   7:             detailsView.initialiseView(viewModel);



   8:         })();



   9:     </script>



  10: }

The next step was about separating out the modules into individual script files and managing the dependencies between them. Before I did this, I revisited the responsibility question of each module and decided that I wanted to make some changes.

I wanted the viewmodel module solely responsible for view data, computed data and commands, but clean of actual view components (ids, classes etc.) etc. – those should be handled in the view module.

The rendering module was bugging me as it shared some of the view module’s responsibility. What I actually was missing was a module that acts as a sort of controller so I decide to clean up the rendering module and allow the view module to handle click events and manipulation of view model data and introduce a dataService module, solely responsible for conversing with a remote service. This did not need to be defined per page though so I created this under the RMP pattern within my scripts/apps folder as I wanted it initialised on startup.

What I ended up with is shown below – basically MVVM without the Model as we already have a domain model on the back end which is then converted into DTOs for the front end so I did not need another model for this. A Model can also act as the DAL but I don’t like this coupled approach and all of my DA goes through multiple layers leaving my web app completely clean of the DA technology.

image

The view model module declares the properties and functions I want my view to be able to bind to. I see the functions acting like the Command pattern does in WPF and Silverlight so where I need to talk to the data service, I use the following knockout notation to go through the view model:

   1: data-bind="click: deleteCompanyCommand"

Where I am only manipulating the view, I am using plain old event handling in my view module to control this:

   1: $("#createCompanyBtn").on("click", initialiseCreateCompanyDialog);

Both the View and ViewModel modules are hooked up via the Require library with the following notation. Note I opted to make the View dependent on the ViewModel module:

ViewModel:

   1: define(function () {



   2:



   3:     var initialiseViewModel = function (data) {



   4:     ...



   5:     return {



   6:         initialiseViewModel: initialiseViewModel



   7:     };



   8: });

View:

   1: define(['pageScripts/viewModel'], function (viewModel) {



   2:     var viewModel,



   3:     ...



   4:     initialiseView = function (data) {



   5:         viewModel = viewModel.initialiseViewModel(data);



   6:         viewSubscriptions();



   7:         wireEvents();



   8:     }



   9:



  10:     return {



  11:         initialiseView: initialiseView



  12:     };



  13: });

Then, inside the html, I have the following script:

   1: @section Scripts



   2: {



   3:     <script type="text/javascript">



   4:



   5:         require.config({



   6:             baseUrl: "/scripts",



   7:             paths: {



   8:                 pageScripts: "views/index"



   9:             }



  10:         });



  11:



  12:         require(['pageScripts/view'], function(view) {



  13:             view.initialiseView(ko.mapping.fromJS(@Html.Raw(Json.Encode(Model))));



  14:         });



  15:     </script>



  16: }

The config section sets up my base url and then provides me with an alias to easily refer to my scripts for that particular page – remember, i don’t want these scripts being loaded elsewhere, only for this page. The require loads in the dependency for my View which, in turn, has already declared its dependency on the view model.

I did find that trying to use explicit or relative paths inside the require seemed to result in undefined dependencies further down the chain but I would recommend setting up those paths for cleanliness in any case.

I also have the option of using the Require Minimiser to negate the impact of needing to load multiple resources as this will combine and minify all my script into one resource for download. Very nice indeed :)

I’ve found this whole app an interesting challenge in determining which web technologies, in particular, 3rd party javascript/ css packages work well together. I certainly found the using Twitter Bootstrap, which its attribute binding approach fits in nicely with Knockouts MVVM approach and both have enabled me to take a clean, separated line of attack when managing my javascript. Coming from a C# and Xaml background, that’s a really pleasant and familiar feel to how I like to code.

Separating Knockout Viewmodel from View

As mentioned in a previous post, I’m becoming a big fan of Knockout. I don’t favour MVVM over MVC per se, simply like the idea of being able to manipulate, and react to the model changing on the client side without the need for a return server trip every time.

Whilst it is perfectly normal for a Knockout view model to be declared inside the cshtml View to which it is bound, I have found that very quickly, the script can become quite bulky and difficult to maintain.

Take for instance the following script inside a View of mine:

   1: @section Scripts

   2: {

   3:     <script type="text/javascript">

   4:         /// <reference path="../jquery-1.9.1.js" />

   5:         /// <reference path="../knockout-2.2.1.js" />

   6:         /// <reference path="knockout.extensions.js" />

   7:         /// <reference path="../knockout.mapping-latest.js" />

   8:         /// <reference path="../jquery-ui-1.10.2.js" />

   9:         (function () {

  10:  

  11:             var viewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));

  12:             viewModel.currentView = ko.observable('company');

  13:             viewModel.editMode = ko.observable(false);

  14:             viewModel.editMode.subscribe(detailsRendering.toggleModelEdit);

  15:  

  16:             var btnActiveClass = 'btn-success';

  17:  

  18:             var manageSelection = function () {

  19:                 removeSelection();

  20:                 viewModel.currentView($(this).data("view-id"));

  21:                 addSelection($(this));

  22:             };

  23:  

  24:             var addSelection = function (element) {

  25:                 element.addClass("btn-primary");

  26:             };

  27:  

  28:             var removeSelection = function () {

  29:                 $(".detailsSelector").removeClass("btn-primary");                

  30:             };

  31:  

  32:             var toggleModelEdit = function (edit) {

  33:                 if (edit) {

  34:                     $("#lockModelBtn").removeClass(btnActiveClass);

  35:                     $("#unlockModelBtn").addClass(btnActiveClass);

  36:                 } else {

  37:                     $("#unlockModelBtn").removeClass(btnActiveClass);

  38:                     $("#lockModelBtn").addClass(btnActiveClass); 

  39:                 }

  40:             };

  41:  

  42:             $(".detailsSelector").on('click', manageSelection);

  43:             $("#lockModelBtn").on('click', function () { viewModel.editMode(false); });

  44:             $("#unlockModelBtn").on('click', function () { viewModel.editMode(true); });

  45:  

  46:             addSelection($("#companyDetailsSelector"));            

  47:  

  48:             toggleModelEdit(false);

  49:  

  50:             ko.applyBindings(viewModel);

  51:         })();

  52:     </script>   

  53: } 

It’s not very complicated but already I am finding it a little fiddly to follow. And, I’m not yet finished with the code, so it’ll only get bigger. I could separate out the areas responsible for initialising the view model from those responsible for reacting to user interaction with multiple script tags but a) that would add extra js code and b) I have a lot of overlap between the two areas of responsibility so scope is an important issue.

What I opted to go with is the Revealing Module Pattern (RMP) that provides a nice separation of concern in simplistic fashion. I created three RMP functions:

1. Initialise View Model

2. Initialise View

3. Handle User Interaction with the View

The resulting script is as follows:

   1: /// <reference path="../jquery-1.9.1.js" />

   2: /// <reference path="../knockout-2.2.1.js" />

   3: /// <reference path="knockout.extensions.js" />

   4: /// <reference path="../knockout.mapping-latest.js" />

   5: /// <reference path="../jquery-ui-1.10.2.js" />

   6: var details = function () {

   7:     

   8:     var initialiseViewModel = function (data) {

   9:         var viewModel = data;

  10:         viewModel.currentView = ko.observable('company');

  11:         viewModel.editMode = ko.observable(false);

  12:         viewModel.editMode.subscribe(detailsRendering.toggleModelEdit);

  13:  

  14:         ko.applyBindings(viewModel);

  15:  

  16:         return viewModel;

  17:     }

  18:  

  19:     return {

  20:         initialiseViewModel: initialiseViewModel

  21:     };

  22: }();

  23:  

  24: var detailsView = function () {

  25:  

  26:     wireEvents = function (vm) {

  27:         $(".detailsSelector").on('click', function () { detailsRendering.manageSelection(vm, $(this)); });

  28:         $("#lockModelBtn").on('click', function () { vm.editMode(false); });

  29:         $("#unlockModelBtn").on('click', function () { vm.editMode(true); });

  30:     },

  31:  

  32:     initialiseView = function (vm) {

  33:  

  34:         wireEvents(vm);

  35:         detailsRendering.manageSelection(vm, $("#companyDetailsSelector"));

  36:         detailsRendering.toggleModelEdit(false);

  37:     }

  38:  

  39:     return {

  40:         initialiseView: initialiseView

  41:     };

  42: }();

  43:  

  44: var detailsRendering = function () {

  45:  

  46:     var btnActiveClass = 'btn-success',

  47:     btnPrimaryClass = 'btn-primary',

  48:  

  49:     addSelection = function (element) {

  50:         element.addClass(btnPrimaryClass);

  51:     },

  52:  

  53:     removeSelection = function () {

  54:         $(".detailsSelector").removeClass(btnPrimaryClass);

  55:     }

  56:  

  57:     toggleModelEdit = function (edit) {

  58:         if (edit) {

  59:             $("#lockModelBtn").removeClass(btnActiveClass);

  60:             $("#unlockModelBtn").addClass(btnActiveClass);

  61:         } else {

  62:             $("#unlockModelBtn").removeClass(btnActiveClass);

  63:             $("#lockModelBtn").addClass(btnActiveClass);

  64:         }

  65:     },

  66:     manageSelection = function (viewModel, element) {

  67:          removeSelection();

  68:          viewModel.currentView(element.data("view-id"));

  69:          addSelection(element);

  70:      }

  71:  

  72:     return {        

  73:         toggleModelEdit: toggleModelEdit,

  74:         manageSelection: manageSelection

  75:     };

  76: }();

The first module, “details” (named after the view), providers a function for setting up the view model and returning. It does nothing else and knows of nothing else. The only dependency it has is on the initial input of the model which has come from the server.

The second module, “detailsView”, expects the view model and sets up the components in view from wiring the click events, to setting up the default view by calling into the third module, “detailsRendering”, which again expects the view model as an input and provides the functionality for manipulating the view based on either the user’s input or manually in the case of the detailsView initial setup. These three modules are in the same js file, but could quite easily be separated out, in which case, I’d be wise to use something like require to handle dependencies and also minimise the load.

To get the ball rolling, the html embedded script now looks like this:

   1: @section Scripts

   2: {

   3:     <script src="~/scripts/app/page.details.js"></script>   

   4:     

   5:     <script type="text/javascript">

   6:         (function () {

   7:             var viewModel = details.initialiseViewModel(ko.mapping.fromJS(@Html.Raw(Json.Encode(Model))));

   8:             detailsView.initialiseView(viewModel);

   9:         })();

  10:     </script>    

  11: }

Rather than returning the view model from the initial function, I could have called the initialiseView method from within but this way, I have the option of making further calls if needs be without the need to chain. Having spent most of my time in C# and Xaml, I’d love to hear from html and javascript guys on what their preferred approach to client side MVVM is.

Script List missing in IE10 F12 Debugger Tool

Recently had a “where the flip has that gone moment” with the IE debugger tools when I wanted to set a break point on some JavaScript code inside a separate file. Clicking on the Script tag brought up the html code and all the script embedded within in but when I hit “Start Debugging”, I was expected a Script List drop down to appear beside it:

IE10 F12 debugger window

I knew I had seen it there before as a brief search on msdn revealed (for IE8). After wasting looking ‘everywhere’ for it, I finally found the blighter in plain view, just a little more subtle:

image

Obvious once spotted but thought this might save anyone else a few precious minutes!