Angular2

I have being playing around with Angular2 for last couple of months.

Here is my list of links on Angular2 that helped me in my journey:

Functional Reactive Programming(FRP) in Angualr2

 

AngularJs scripts and minification

AngularJS provides Dependency Injection where a controller, service or module can expect Angular to pass the dependencies.

For e.g. if we have two services and a controller then controller can get the service dependency by writing the formal parameter in it’s constructor with the same name as service name.

var myModule = angular.module('simpleApp',[]);
myModule.service('dataService',function($http){
	this.getData = function() {
		return $http.get('/some/url');
	}

});
myModule.controller('simpleController',function($scope, dataService){
	dataService.getData().then(function(data){
		$scope.data = data;
	});
});

In the code above, simpleController is getting the dataService dependency because the parameter is also name as dataService. If the name is something else Angular will not be able to fulfill the dependency.

JavaScript Minification

With JavaScript minification the same JavaScript code will look like this

var myModule=angular.module("simpleApp",[]);

myModule.service("dataService",function(e){this.getData=function(){return e.get("/some/url")}});

myModule.controller("simpleController",function(e,t){t.getData().then(function(t){e.data=t})})

Due to minification the parameters have been renamed to a smaller length name like ‘e’, ‘t’. As now the service parameter name in simpleController is ‘t’ AngularJS will not be able to fulfill the dependency.

Dependency Injection with JavaScript minification

To enable DI we have to write controller and service methods with an array whose elements are list of all parameter names in string followed by the function.

var myModule = angular.module('simpleApp',[]);
myModule.service('dataService',[ '$http', function($http){
	this.getData = function() {
		return $http.get('/some/url');
	}

}]);
myModule.controller('simpleController',['$scope', 'dataService', function($scope, dataService){
	dataService.getData().then(function(data){
		$scope.data = data;
	});
}]);

And the code post minificaiton

var myModule=angular.module('simpleApp',[]);

myModule.service('dataService',['$http',function(e){this.getData=function(){return e.get('/some/url')}}]);

myModule.controller('simpleController',['$scope','dataService',function(e,t){t.getData().then(function(t){e.data=t})}])

Note the strings are not getting changed so now AngularJS will pass ‘dataService’ to parameter

‘t’ in the above minified code.

Alternatively, if you are not using module constructor and service calls then you can set $inject as array of parameter names.

var myModule = angular.module('simpleApp',[]);
function dataService($http){
this.getData = function() {
  console.log('called getData');
		return  'done';
	};
};
dataService.$inject = ['$http'];
myModule.service('dataService', dataService);
	
 function simpleController($scope, dataService){
	$scope.msg = 'loading'; 
   
     $scope.msg = dataService.getData();
	  
 }
 simpleController.$inject = ['$scope','dataService'];
 
 myModule.controller('simpleController',simpleController);

RemoteDataSource in Upshot.js

RemoteDataSource in Upshot.js can be used to fetch and save data using DataProvider.

Let’s see it’s constructor.

RemoteDataSource takes an options object as single parameter.

The options can have following parameters:

1. bufferChanges: (Optional) If ‘true’, edits to model data are buffered until RemoteDataSource.commitChanges.  Otherwise, edits are committed to the server immediately.
            
2. result:
(Optional) The observable array into which the RemoteDataSource will load model data.
       
3. dataContext:
(Optional) A DataContext instance that acts as a shared cache for multiple DataSource instances.  When not supplied, a DataContext instance is instantiated for this RemoteDataSource.
       
4. entityType:
The type of model data that will be loaded by this RemoteDataSource instance.
       
5. provider:
(Optional) Specifies the DataProvider that will be used to get model data and commit edits to the model data.  Defaults to upshot.DataProvider which works with System.Web.Http.Data.DataController.
       
6. providerParameters:
(Optional) Parameters that are supplied to the DataProvider for this RemoteDataSource and used by the DataProvider when it gets model data from the server.
       
7. mapping:
(Optional) A function (typically a constructor) used to translate raw model data loaded via the DataProvider into model data that will be surfaced by this RemoteDataSource.
       

RemoteDataSource have four public methods:

1. setSort : Establishes the sort specification that is to be applied as part of a server query when loading model data.

Parameter: sort (required)

Should be supplied as an object of the form { property: <propertyName> [, descending: <bool> ] } or an array of ordered objects of this form.
 
When supplied as null or undefined, the sort specification for this RemoteDataSource is cleared.

2. setFilter : Establishes the filter specification that is to be applied as part of a server query when loading model data.

Parameter: filter (required)

Should be supplied as an object of the form { property: <propertyName>, value: <propertyValue> [, operator: <operator> ] } or an array of ordered objects of this form.
 
When supplied as null or undefined, the filter specification for this RemoteDataSource is cleared.

3. refresh: Initiates an asynchronous get to load model data matching the query established with setSort, setFilter and setPaging.

Parameters:

  1. options (optional) : There are no valid options recognized by RemoteDataSource.
  2. success (optional) : A success callback with signature function(entities, totalCount).
  3. error(optional): An error callback with signature function(httpStatus, errorText, context).

OData access using jsonp

The upshot.js ver. 1.0 does not support jsonp calls. To enable jsonp I have modified the DataProvider.OData get method enable jsonp callback like this.

var jsonpCall ;
            if (parameters) {
                operation = parameters.operationName;
                operationParameters = parameters.operationParameters;
                jsonpCall = parameters.jsonpCall ? (parameters.jsonpCall === true): false;
            }
            if(jsonpCall){
              OData.defaultHttpClient.enableJsonpCallback = true;
             }

Using odata provider access Netflix OData :

(function () {
    var Netflix = window.Netflix = window.Netflix || {};

    Netflix.Movie = function (properties) {
        var self = this;

        self.Name = properties.Name;
        self.ShortName = properties.ShortName;
        self.ShortSynopsis = properties.ShortSynopsis;
        self.AverageRating = properties.AverageRating;
        self.ReleaseYear = properties.ReleaseYear;
        self.Runtime = properties.Runtime;

    }
    Netflix.Movie.Type = "Title:#Netflix.Catalog.v2"

    Netflix.CourseViewModel = function (options) {
        var self = this;

        var dataprovider = new upshot.ODataDataProvider();
        var courseDataSourceParameters = {
            provider: dataprovider,
            providerParameters: { url: options.serviceUrl, dataType: 'jsonp', operationName: "Titles", operationParameters: { "$top": "10"}, jsonpCall : true },            
            entityType: Netflix.Movie.Type,
            bufferChanges: true
        };
        var courseDataSource = new upshot.RemoteDataSource(courseDataSourceParameters);
        self.Titles = [];
        courseDataSource.refresh({}, function (entities, totalCount) {
            self.Titles = [];
            $.each(entities, function (index, item) {
                var entity = new Netflix.Movie(item);
                self.Titles.push(entity);
            });

            alert("Total movies loaded " + self.Titles.length);
        });

    }

})();

Workings of ASP.NET MVC SPA app

ASP.NET MVC 4 Single Page Application template depends on ASP.NET Web API Data WebHost Nuget package (https://nuget.org/packages/AspNetWebApi.Data) which provides two main things:  Html Metadata helper  and DataController.

Html Metadata helper defined in System.Web.Http.Data.Helpers assembly have signature as given below:

 1: public static IHtmlString Metadata<TDataController>(this HtmlHelper htmlHelper) where TDataController : DataController

The Metadata helper generates metadata that will be used by Upshot.js. It takes a type which should derive from DataController. The DataController is defined in System.Web.Http.Data assembly and derives from ApiController.

The call to Metadata helper is made in the javascript file as :

 1: upshot.metadata(@(Html.Metadata<MvcApplication7.Controllers.MvcApplication7Controller>()));

This generates the following content for the controller.

 1: upshot.metadata({

 2:     "TodoItem:#MvcApplication7.Models": {

 3:         "key": ["TodoItemId"],

 4:         "fields": {

 5:             "IsDone": {

 6:                 "type": "Boolean:#System"

 7:             },

 8:             "Title": {

 9:                 "type": "String:#System"

 10:             },

 11:             "TodoItemId": {

 12:                 "type": "Int32:#System"

 13:             }

 14:         },

 15:         "rules": {

 16:             "Title": {

 17:                 "required": true

 18:             }

 19:         },

 20:         "messages": {}

 21:     }

 22: });

TodoItem.cs

 1: namespace MvcApplication7.Models

 2: {

 3:

 4:     public class TodoItem

 5:     {

 6:         public int TodoItemId { get; set; }

 7:         [Required]

 8:         public string Title { get; set; }

 9:         public bool IsDone { get; set; }

 10:     }

 11: }

MvcApplication7Context

 1: namespace MvcApplication7.Models

 2: {

 3:     public class MvcApplication7Context : DbContext

 4:     {

 5:         public DbSet<TodoItem> TodoItems { get; set; }

 6:     }

 7: }

Using the code generated by SPA template, on creating a new TodoItem a POST request is made. Looking into the POST request we can see the format in which data is submitted.

 1: [{

 2:     "Id": "0",

 3:     "Operation": 1,

 4:     "Entity": {

 5:         "__type": "TodoItem:#MvcApplication7.Models",

 6:         "Title": "To blog on Uphot.js internals"

 7:     }

 8: }]

Update TodoItem request looks like this:

 1: [{

 2:     "Id": "0",

 3:     "Operation": 2,

 4:     "Entity": {

 5:         "__type": "TodoItem:#MvcApplication7.Models",

 6:         "IsDone": false,

 7:         "Title": "Try Knockout.js",

 8:         "TodoItemId": 1

 9:     },

 10:     "OriginalEntity": {

 11:         "__type": "TodoItem:#MvcApplication7.Models",

 12:         "IsDone": false,

 13:         "Title": "Test",

 14:         "TodoItemId": 1

 15:     }

 16: }]

Adding a new ViewModel

Disclaimer: The following code is a learning exercise and not the best or recommended way of developing single page application.

To add a new ViewModel I did following steps:

  1. Added server side data model class “Airport.cs”
  2. Updated context with it’s value
  3. Added Controller methods
  4. Added partial view
  5. Added JavaScript

Model Airport and Context class

 1: public class Airport

 2:     {

 3:         public int AirportId { get; set; }

 4:         [Required]

 5:         public String Code {get;set;}

 6:         public String Name { get; set; }

 7:         public String City { get; set; }

 8:     }

 1: public class MvcApplication7Context : DbContext

 2:    {

 3:         public DbSet<TodoItem> TodoItems { get; set; }

 4:

 5:        public DbSet<Airport> Airports { get; set; }

 6:    }

Controller Action Methods for new model

 1: public partial class MvcApplication7Controller : DbDataController<MvcApplication7.Models.MvcApplication7Context>

 2:     {

 3:         public IQueryable<MvcApplication7.Models.Airport> GetAirports()

 4:         {

 5:             return DbContext.Airports.OrderBy(t => t.AirportId);

 6:         }

 7:

 8:         public void InsertAirport(MvcApplication7.Models.Airport entity)

 9:         {

 10:             InsertEntity(entity);

 11:         }

 12:

 13:         public void UpdateAirport(MvcApplication7.Models.Airport entity)

 14:         {

 15:             UpdateEntity(entity);

 16:         }

 17:

 18:         public void DeleteAirport(MvcApplication7.Models.Airport entity)

 19:         {

 20:             DeleteEntity(entity);

 21:         }

 22:     }

Partial View for creating airport entry

 1: <h2> Add Airport</h2>

 2:

 3: <form data-bind="with: editingAirportEntry, submit: addNewAirport">

 4:     <div class="row" >

 5:             <div class="control-group">

 6:                 <label class="control-label">

 7:                     Name :</label>

 8:                 <div class="controls">

 9:                     <input name="name" type="text"  data-bind="value: Name"  />

 10:                 </div>

 11:             </div>

 12:         </div>

 13:         <div class="row">

 14:             <div class="control-group">

 15:                 <label class="control-label">

 16:                     Code :</label>

 17:                 <div class="controls">

 18:                     <input name="code" type="text"   data-bind="value: Code" />

 19:                 </div>

 20:             </div>

 21:         </div>

 22:         <div class="row">

 23:             <div class="control-group">

 24:                 <label class="control-label">

 25:                     City :</label>

 26:                 <div class="controls">

 27:                     <input name="name" type="text"   data-bind="value: City" />

 28:                 </div>

 29:             </div>

 30:         </div>

 31:

 32:         <div class="row">

 33:         <button type="submit" class="btn">Add</button>

 34:         </div>

 35: </form>

Note here  I have used with and submit knockout bindings.

JavaScript code for ViewModel and Navigation

Add code for mapping raw data to entity
 1: var airportEntityType = "Airport:#MvcApplication7.Models";

 2:

 3:     MyApp.AirportEntry = function (data) {

 4:         var self = this;

 5:         self.AirportId = ko.observable(data.AirportId);

 6:         self.Code = ko.observable(data.Code);

 7:         self.City = ko.observable(data.City);

 8:         self.Name = ko.observable(data.Name);

 9:         upshot.addEntityProperties(self, airportEntityType);

 10:     }

DataSource for Airport Entries
 1: var airportDataSourceOptions = {

 2:     providerParameters: { url: options.serviceUrl, operationName: "GetAirports" },

 3:     entityType: airportEntityType,

 4:     bufferChanges: true,

 5:     mapping: MyApp.AirportEntry

 6: };

 7:

 8: var airportEditorDataSource = new upshot.RemoteDataSource(airportDataSourceOptions);

Data property for airport entry being created or edited
 1: self.editingAirportEntry = airportEditorDataSource.getFirstEntity();

Submit event handler
 1: self.addNewAirport = function (formElement) {

 2:     airportEditorDataSource.commitChanges(function () {

 3:         self.showGrid();

 4:     });

 5: };

Navigation code to show Airport editor form
 1: self.createAirportEntry = function () {

 2:    self.nav.navigate({ edit: "new", entity: 'airport' })

 3: };

 4:

 5: self.nav = new NavHistory({

 6:    params: { edit: null, page: 1, pageSize: 10, entity: 'todo' },

 7:    onNavigate: function (navEntry, navInfo) {

 8:

 9:        if (navEntry.params.entity === 'airport') {

 10:            //reset airport editor data source

 11:            airportEditorDataSource.revertChanges();

 12:            airportEditorDataSource.reset();

 13:            if (navEntry.params.edit) {

 14:                if (navEntry.params.edit === 'new') {

 15:                     //for creating new airport entry

 16:                    airportEditorDataSource.getEntities().push(new MyApp.AirportEntry({}));

 17:                }else{

 18:                     //code for editing exiting entry

 19:                 }

 20:            }

 21:        } else {

 22:             //Todo Item navigation code

 23:        }

 24:    }

 25: }).initialize({ linkToUrl: true });

Binding to show Airport Form and button conditionally
 1: <div data-bind="visible: editingAirportEntry">

 2:     @Html.Partial("_AirportEditor")

 3: </div>

 4: <p><button data-bind="click: createAirportEntry, visible: !editingAirportEntry()">Create Airport Entry</button></p>

Conclusion: We have seen today basic steps to add new views to the Single Page Application.

Simple Search Form using knockout.js

In this post I continue to explore the knockout.js with ASP.NET MVC 4.

I wish to create a flight search form which looks like this:screenN1

My ViewModel looks like this:

var airport = function(city,code){

       this.city = city;

       this.code = code;

   };

   var airports =  ko.observableArray([

               new airport("New Delhi", "DEL"),

               new airport("Mumbai", "BOM"),

               new airport("Chennai", "MAA"),

               new airport("Kolkata", "CCU"),

               new airport("Bangalore", "BLR")]);


   var flightSearch = {


       isOneWay: ko.observable("oneWay"),


       airports: airports,


       originatingAirport: ko.observable(),

       destinationAirport: ko.observable(),

       departureDate: ko.observable(),

       returnDate: ko.observable()

   };

I have an airport object defined to take two values: city and code. Then there is an array of airports, which is build using ko.observableArray. Then there is flightSearch view model class with properties for selecting between return flights or one way flight booking, list of airports,  origin and destination airports, departure and optional return date.

I want my view logic to hide return date ui when one way flight is selected.

Here is my markup for the same, along with a summary of values selected.

 1:

 2: <div class="row">

 3:     <div class="span8 ">

 4:         <div class="row">

 5:             <div class="span2 control-group">

 6:                 <input type="radio" name="isOneWay" value="return" data-bind="checked: isOneWay" />

 7:                 Return

 8:             </div>

 9:             <div class="span2 control-group">

 10:                 <input type="radio" name="isOneWay" value="oneWay" data-bind="checked: isOneWay"    />

 11:                 Oneway

 12:             </div>

 13:         </div>

 14:         <div class="row">

 15:             <div class="control-group">

 16:                 <label class="control-label">

 17:                     Leaving From :</label>

 18:                 <div class="controls">

 19:                     <select data-bind="options: airports,optionsText: 'city', optionsValue: 'code', value: originatingAirport, optionsCaption: 'Choose...'">

 20:                     </select>

 21:                 </div>

 22:             </div>

 23:         </div>

 24:         <div class="row">

 25:             <div class="control-group">

 26:                 <label class="control-label">

 27:                     Going To :</label>

 28:                 <div class="controls">

 29:                     <select data-bind="options: airports,optionsText: 'city', optionsValue: 'code',value: destinationAirport, optionsCaption: 'Choose...'">

 30:                     </select>

 31:                 </div>

 32:             </div>

 33:         </div>

 34:         <div class="row">

 35:             <div class="control-group">

 36:                 <label class="control-label">

 37:                     Departing :</label>

 38:                 <div class="controls">

 39:                     <input name="departureDate" type="text" data-bind="value : departureDate" />

 40:                 </div>

 41:             </div>

 42:         </div>

 43:         <div class="row">

 44:             <div class="control-group" data-bind="visible: isOneWay() === 'return'" style="display:none">

 45:                 <label class="control-label">

 46:                     Returning :</label>

 47:                 <div class="controls">

 48:                     <input name="returnDate" type="text" data-bind=" value: returnDate" />

 49:                 </div>

 50:             </div>

 51:         </div>

 52:     </div>

 53:     <div class="span3">

 54:         <h3>

 55:             Search Summary:</h3>

 56:         <div data-bind="if: isOneWay() === 'return'">

 57:             Return flights

 58:         </div>

 59:         <div data-bind="ifnot: isOneWay() === 'return'">

 60:             One way flight

 61:         </div>

 62:         <div>

 63:             Starting at: <span data-bind="text: originatingAirport"></span>

 64:         </div>

 65:         <div>

 66:             Going to: <span data-bind="text: destinationAirport"></span>

 67:         </div>

 68:     </div>

 69: </div>

knockout.js binding usages

Value binding

All input and select controls are bound using value binding.

Options, OptionsText, OptionsValue and OptionsCaption bindings

For Origin and Destination Airports, I have used these bindings. OptionsText and OptionsValue is used to bind the options to JavaScript objects.

The code at line no. 19 specifies that ‘city’ property of the object should be used as text and ‘code’ property of the object should be used a value for the options.

if and ifnot binding

In line number 44 if binding was used to control the visibility of div containing the return date input controls. Similarly in line number 59, ifnot  binding is used to control div’s visibility.

text binding

In line number 63 and 66, text binding is used to show the values of selected airports.

Upshot.js

This post is part of series of post on Single Page Application using ASP.NET MVC 4. We will explore the upshot, nav and history JavaScript libraries in this post.

What is Upshot.js?

Upshot is a new JavaScript library coming out of Microsoft. It’s based on previously previewed JavaScript library RIA/JS.  It is available through NuGet http://nuget.org/packages/upshot. As it is relatively undocumented, expect some of what is written below to change.

Upshot is for data access and related functionalities like paging, sorting, change tracking. Upshot.js is designed to work with DataController and oData (only data reading).

Objects provided by Upshot

  • EntitySource :  a wrapper of array of Entities which are observable. provides method to bind/unbind events
  • EntityView : inherits from EntitySource and provides change tracking on it
  • DataSource : inherits from EntityView and add paging, sorting and filterting support on it.
  • RemoteDataSource 
  • LocalDataSource
  • DataProvider : a wrapper over jQuery ajax to get and submit entity values
  • ODataDataProvider
  • riaDataProvider
  • EntitySet : inherits from EntitySource and provides change tracking on EntityType. This object does the heavy lifting of managing entities
  • DataContext
  • AssociatedEntitiesView

TODO: Update more about these objects

Usage of Upshot in ASP.NET MVC4 SPA

On creating a Single Page Application controller, we got code generated for the View and ViewModel. In the ViewModel code we can some usage of Upshot.

Creating RemoteDataSource

var gridDataSource = new upshot.RemoteDataSource(dataSourceOptions);

var editorDataSource = new upshot.RemoteDataSource(dataSourceOptions);

The RemoteDataSource are created with dataSourceOptions. DataSource options contains property values for dataProvider parameters ( url and oprationName), entityType , bufferChanges, mapping

Fetching data using DataSource

self.todoItems = gridDataSource.getEntities();

self.editingTodoItem = editorDataSource.getFirstEntity();

In the above code getEntities() method is used to get all TodoItem entities, and getFirstEntity() to get first TodoItem entity.

Another way to load data is to call refresh.

// Load and begin editing an existing todoItem instance

editorDataSource.setFilter({ property: "TodoItemId", value: Number(navEntry.params.edit) }).refresh();

// Not editing, so load the requested page of data to display in the grid

gridDataSource.refresh();

Saving data using DataSource

editorDataSource.commitChanges(function () {

                self.successMessage("Saved TodoItem").errorMessage("");

                self.showGrid();

            });

Deleting data using DataSource

editorDataSource.deleteEntity(todoItem);

Knockout.js

This post is part of series of post on Single Page Application using ASP.NET MVC 4. We will explore the knockout JavaScript library in this post.

What is Knockout.js?

Knockout.js is a JavaScript library for building HTML UI using Model-View-ViewModel (MVVM) pattern. The library is MIT licensed and source code is hosted on GitHub at https://github.com/SteveSanderson/knockout and project web hosted at http://knockoutjs.com/

Salient features of this library are:

  • Declarative bindings. A way to bind UIs to underlying data model using declarative bindings like data-bind=”text: firstName”
  • Dependency tracking. The build in dependency tracking updates the right UI when the underlying data changes by doing dependency check.
  • Templating

What is MVVM?

Model-View-ViewModel is a design pattern for building user interfaces.

  • Model: an application’s persisted data.
  • View: an application’s visible, interactive UI component.
  • ViewModel: In MVVM ViewModel is the an abstract layer to separate View and Model. ViewModel is a code based representation of data and operations on a UI.

Using Knockout.js

ViewModel in Knockout.js is a JavaScript object like:

var contact = {

    firstName : 'John',

    lastName  : 'Clark',

    email     : 'john.clark@company.com'

}

To bind this to UI, data-bind attribute are used like:

Contact's first name is  <span data-bind="text: firstName"> </span> 

 

For binding view to viewmodel, call Knockout.js activateBindings method as given here:

ko.activateBindings(contact);

To automatically update UI when the data model changes, we need to use Knockout’s observable as given below:

var contact = {

    firstName   : ko.observable('John'),

    lastName    : ko.observable('Clark'),

    email       : ko.observable('john.clark@company.com')

};

Built-in bindings:

  1. text : use text binding to display text value
  2. html : use html binding to display HTML
  3. css : use css binding to add or remove CSS classes
  4. style : use style binding to add or remove style values to the DOM node
  5. attr : use attr binding to
  6. visible : use visible binding to control the DOM element’s visibility
  7. foreach : use  foreach to loop around array and create markup on them
  8. if : use if to control a section of markup’s visibility
  9. ifnot : similar to if binding but works if the conditional is false
  10. with : use with in templates for creating new binding context
  11. click : use click binding to add an event handler to the click event to elements like a, input and buttons
  12. event : use event  binding to add an event handler to specified events like mouseover, mouseout, keypress, keydown etc. on the DOM element
  13. submit : use submit binding to add an event handler to the DOM node which would be called when the element value is being submitted like in case of form submit
  14. enable : use enable  binding to enable the DOM element based on the specified boolean expression. This is typically used for DOM elements like input, select, textarea.
  15. disable : use disable binding to disable the DOM element based on the specified boolean expression
  16. value : use value binding to  link the DOM element’s value with a property in view model. Typically used with input, select, textarea
  17. hasfocus : use hasfocus  binding to link a DOM element’s focus state with a property in view model
  18. checked : use checekd  binding with checkbox or radio button to link it with property on view model
  19. options  : use options binding to control the options  of a select control (<select>) 
  20. selectedOptions : use selectedOptions binding to control the elements selection in a multi-select list. Use this with <select> element and options binding
  21. uniqueName :
  22. template : use template  binding to render a templates output to the associated DOM element

In addition to the built-in bindings, knockout.js supports creating custom bindings.