Tuesday, June 30, 2015

AngularJS UI Grid Vertical Resize

I was working on a scenario where I want the UI grid to take up all the available space in the browser, both vertically and horizontally. Doing horizontally is easy, I just need to set the width to 100% and specify “ui-grid-auto-resize”.

The trick is to make the grid resize vertically. For this I wrote a small script which calculates the grid height.

    function getGridHeight(gridClass, contentClass, bottomOffset) {
        var contentOffset = angular.element(document.getElementsByClassName(contentClass)).offset();
        var contentHeight = angular.element(document.getElementsByClassName(contentClass)[0]).height();
        var gridOffset = angular.element(document.getElementsByClassName(gridClass)).offset();
        if (gridOffset !== undefined) {
            var gridHeight = contentHeight - (gridOffset.top) - bottomOffset;
            return gridHeight + 'px';
        }
    }

The above function takes the following three parameters:
  • gridClass – This class is used to identify the current location of the grid in the browser
  • contentClass – This class is the container class of the grid (could be body tag) and this is needed to find the relative position of the grid with in the browser
  • bottomOffset – This specifies how much space we need to leave at the bottom of the window


Now we need to associate this function with the grid height in our angular view and controller. First let’s do the binding in the view as shown below (I am binding the gridHeight to the height of the UI-Grid)

<div ui-grid="vm.gridOptions" external-scopes="vm" ui-grid-resize-columns ui-grid-auto-resize class="patient-grid" ng-style="{'height' : vm.gridHeight }"></div>

In the controller, I will calculate the grid height whenever window resizes as shown below
function resizeGrid() {
        vm.gridHeight = utility.getGridHeight('patient-grid', ‘main-content’, 40);
}

angular.element($window).bind('resize', function () {
       resizeGrid();
});


With the above code, the UI grid would be responsive to the browser height.

Here is the Plunker for this code snippet
http://plnkr.co/edit/LcGe64WkNOAJZfqc7Ics?p=preview

Monday, June 15, 2015

MVC 6 Routing for Angular Application

If you are working on a single page application, then you will have only one view in your application. From this view you load up the angular application (or other frameworks) and then fetch the data through ajax. Once loading this single MVC view, we utilize angular routing to load different angular views while user is navigating from one page to another.

In some cases we need to provide an ability for the user to book mark a particular angular view and return directly to that without going to start. This would get tricky as when the application loads, MVC routing thinks that user is trying to reach to an MVC controller/view and returns 404 as it could not find it.

For example, user bookmarked, http://domain.com/vehicle/details. This url should load the default MVC view and then using Angular routing loads the vehicle details. But when we pass this url to MVC, MVC with its default routing tries to fetch the VehicleController and throws 404 error as it could not find it. To avoid this we need to define a catch all route in MVC which returns the default view. Here is the catch all routing:

    routes.MapRoute(
        name: "default",
        template: "{*url}",
        defaults: new { controller = "Home", action = "Index" });

With the above routing we route all urls to our one MVC view. But we should be able to pass our WebAPI controllers thought this routing. Unlike MVC 5 and earlier versions, WebAPI is integrated with MVC and uses the same routing. Hence to differentiate these two I am using a prefix of “api” in the url. I defined an additional route called webapi which defines routing for all my webapi requests.

    routes.MapRoute(
        name: "webapi",
        template: "api/{controller}/{action}/{id?}",
        defaults: new { controller = "Home", action = "details" });

With above two route definitions I can provide an ability for the user to directly go to any particular angular view and also get the data from the server using Web API.

Here is the full routing definition for an MVC 6 Angular single page application:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "webapi",
        template: "api/{controller}/{action}/{id?}",
        defaults: new { controller = "Home", action = "Index" });

    routes.MapRoute(
        name: "default",
        template: "{*url}",
        defaults: new { controller = "Home", action = "Index" });

});