Friday, November 8, 2013

File uploads with Angular part 2

We left off last time with a file uploader that was functional, but not without its shortcomings. Let's continue with addressing some of the immediate issues that bothered me.

Emptying the file input

The first thing that was bothering me about the file uploader as we left it last was that while the files were uploaded as soon as they were picked in the file input element, they were left as being selected in the element. Unfortunately, the file list is readonly, so I can't just clear that. However I can replace the used file input with a new one to simulate the list being cleared out. To do this I will need to handle the use of the file-uploader attribute a little differently. If Angular attaches events to a file input element, and then I replace it with a new one, all of Angular's bindings disappear. So I'm going to use an outer element instead.
<-- Partial -->
<div file-uploader="onFileSelected($files)"></div>
I no longer have a file input element so I'll have to handle the creation of that in the directive handler:
// Directive
app.directive("fileUploader", ["$parse", function ($parse) {
    var fileInputTemplate = "<input type='file' multiple />"; // jquery template for new file input
    
    return function (scope, $elem, attrs) {
        var fn = $parse(attrs["fileUploader"]);

        var changeFunc = function(e) {
            fn(scope, { $files: e.target.files });

            // Empty the container div and append a new element based
            // on fileInputTemplate and attach the change event handler
            $elem.empty().append( 
                $(fileInputTemplate).on("change", changeFunc)
            );
        };
        
        // Append a new element based on fileInputTemplate and
        //attach the change event handler
        $elem.append(
            $(fileInputTemplate).on("change", changeFunc)
        );
    };
}]);
Since I'm attaching the file-uploader directive on a div instead of an input element, I need to create the input element manually and append it to the div. I'm also declaring a function "changeFunc" which I'm using as the change handler. This is the same as the old event handler, except that in addition to calling fn I'm emptying the container div and appending a new input element with a new change event handler on it.

Adding a file queue

It's rare that someone would want a file to upload immediately upon selection in a file input. It's more likely that a user would want to see the selected files and wait to upload until a form submission. I'm going to modify the controller to populate an array on the scope instead of immediately sending files to be uploaded:
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.files = [];
        
        $scope.onFileSelected = function ($files) {
            for (var i = 0; i < $files.length; ++i)
                $scope.files.push($files[i]);
            $scope.$apply();
        };
    }
]);
Instead of submitting the files like before, I'm adding them to a scope variable called files. It's now trivial to add a list of filenames to the partial.
<!-- Partial -->
<div file-uploader="onFileSelected($files)"></div>
<ul>
    <li ng-repeat="file in files">
        {{file.name}}
    </li>
</ul>
<input type="button" ng-click="upload()" value="Upload"/>
I went ahead and added a submit button. Since all of the files are stored on the scope, I can use the submit button to loop through those files and post them to the server:
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.files = [];
        
        $scope.onFileSelected = function ($files) {
            for (var i = 0; i < $files.length; ++i)
                $scope.files.push($files[i]);
            $scope.$apply();
        };

        $scope.upload = function() {
            var formData = new FormData();

            for (var i = 0; i < $scope.files.length; ++i) {
                (function () {
                    var $file = $scope.files[i];
                    formData.append("file", $file, $file.name);
                })();
            }

            var options = {
                method: "POST",
                url: "/api/Import",
                data: formData,
                headers: { "Content-Type": undefined },
                transformRequest: angular.identity
            };

            $http(options).success(function (data, status) {
                if (status != 200)
                    console.log("Error uploading files");
                else {
                    for (var i = 0; i < data.length; ++i)
                        console.log(data[i]);
                    $scope.files.length = 0; // clear the array
                }

            });
        };
    }
]);
The new $scope.upload function should look familiar, it's essentially the old $scope.onFileSelected function.

Styling the file input

Right now the file input still looks like a plain old file input. This means, in Chrome, we will perpetually see the label "No file chosen" even if we have chosen files to upload. I'm going to take a page out of the jQuery File Upload book and style my button like theirs. First I'll need to modify the directive to handle a button caption:
// Directive
app.directive("fileUploader", ["$parse", function ($parse) {
    var fileInputTemplate = "<input type='file' multiple />"; // jquery template for new file input

    return function (scope, $elem, attrs) {
        var fn = $parse(attrs["fileUploader"]);

        var changeFunc = function (e) {
            fn(scope, { $files: e.target.files });

            // Empty the container div and append a new element based
            // on fileInputTemplate and attach the change event handler
            $elem.children("input").replaceWith(
                $(fileInputTemplate).on("change", changeFunc)
            );
        };

        $elem.addClass("file-uploader");
        var $button = $(document.createElement("button"));
        var text = $elem.text();
        var $span = $(document.createElement("span")).text(text);

        // Append a new element based on fileInputTemplate and
        //attach the change event handler
        $button.append($span).append(
            $(fileInputTemplate).on("change", changeFunc)
        );
        $elem.empty().append($button);
    };
}]);
There are a few significant differences between this version of the directive and the last. Note line 13. Instead of taking a shotgun approach and emptying the entire container div before appending the new input element, I'm specifically targeting the input element and replacing it. Below that, I am taking the text that was present in the container div and putting it into a span element to target it more easily with CSS. I'm then wrapping the whole thing into a button element that I can also style with CSS.

Speaking of CSS, here are the style rules I'm going to use:
// CSS
.file-uploader button {
    position: relative;
    overflow: hidden;
}

.file-uploader input {
    position: absolute;
    top: 0;
    right: 0;
    margin: 0;
    opacity: 0;
    -ms-filter: 'alpha(opacity=0)';
    font-size: 200px;
    direction: ltr;
    cursor: pointer;
}
This is a stripped down form of how jQuery File Upload styles their input button. It very cleverly mixes the opacity property of the input element (to hide it) with the overflow property of the button to mask the input element from the cursor.

Additional attributes

I'd like to add a couple more attributes for functionality. I'll start with an attribute to specify the maximum number of files and an attribute to signify whether the user would like to append to the current list of files or replace the current list of files every time they click browse.
<!-- Partial -->
<div file-uploader="files" 
     file-uploader-max-files="5" 
     file-uploader-browse-action="replace">Select files</div>
<ul>
    <li ng-repeat="file in files">
        {{file.name}}
    </li>
</ul>
<input type="button" ng-click="upload()" value="Upload"/>
You'll notice that I changed the value of "file-uploader" in the partial to bind directly to the "files" scope property instead of the "onFileSelected" scope property. In order to check for these attributes inside my directive, I'm going to need to refactor my code a bit. Currently the directive serves as nothing more than a traffic cop which redirects the "change" event of the file input to a function that is defined on the controller. I'm going to modify this pattern to bind the attribute directly to the files property on the scope and handle the population directly in the directive (no pun intended).
// Directive
app.directive("fileUploader", ["$parse", function ($parse) {
    var fileInputTemplate = "<input type='file' multiple />"; // jquery template for new file input

    return function (scope, $elem, attrs) {
        var scopePropGetter = $parse(attrs["fileUploader"]);

        var maxFiles = $parse(attrs["fileUploaderMaxFiles"])() || attrs["fileUploaderMaxFiles"];
        maxFiles = Math.max(parseInt(maxFiles || 0), 0);
        if (maxFiles == 1)
            fileInputTemplate = "<input type='file' />"; // remove the multiple attribute
        
        var browseAction = $parse(attrs["fileUploaderBrowseAction"])() || attrs["fileUploaderBrowseAction"];
        browseAction = browseAction || "replace";
        if (browseAction != "append")
            browseAction = "replace";

        var changeFunc = function (e) {
            var files = scopePropGetter(scope);
            if (browseAction == "replace")
                files.length = 0;

            var numFilesToAdd = e.target.files.length;
            if (maxFiles > 0)
                numFilesToAdd = Math.min(maxFiles - files.length, e.target.files.length);
            
            for (var i = 0; i < numFilesToAdd; ++i)
                files.push(e.target.files[i]);
            scope.$apply();

            // Empty the container div and append a new element based
            // on fileInputTemplate and attach the change event handler
            $elem.children("input").replaceWith(
                $(fileInputTemplate).on("change", changeFunc)
            );
        };

        $elem.addClass("file-uploader");
        var $button = $(document.createElement("button"));
        var text = $elem.text();
        var $span = $(document.createElement("span")).text(text);

        // Append a new element based on fileInputTemplate and
        //attach the change event handler
        $button.append($span).append(
            $(fileInputTemplate).on("change", changeFunc)
        );
        $elem.empty().append($button);
    };
}]);
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.files = [];

        $scope.upload = function() {
            var formData = new FormData();

            for (var i = 0; i < $scope.files.length; ++i) {
                (function () {
                    var $file = $scope.files[i];
                    formData.append("file", $file, $file.name);
                })();
            }

            var options = {
                method: "POST",
                url: "/api/Import",
                data: formData,
                headers: { "Content-Type": undefined },
                transformRequest: angular.identity
            };

            $http(options).success(function (data, status) {
                if (status != 200)
                    console.log("Error uploading files");
                else {
                    for (var i = 0; i < data.length; ++i)
                        console.log(data[i]);
                    $scope.files.length = 0; // clear the array
                }

            });
        };
    }
]);
Let's start with the controller. The only thing we changed is we removed the "onFileSelected" function from the scope. As far the directive, there were quite a few changes made. Lines 8-16 deal with reading the values out of the new attributes and making sure they are valid. Lines 19-29 contain the new code that was taken from the old "onFileSelected" function. It handles the population of the whatever scope variable is bound to the directive. Lines 19-25 decide how many files to add based on the maximum file limit as well as clearing out the existing files if "replace" is selected. Lines 27-29 handle appending the new files onto the scope property.

If you've followed along, you should now have a functioning file uploader. It isn't quite feature complete, but I will continue adding to it in future posts.

File uploads with Angular

Uploading a file is a common operation, and in designing an Angular-based site the odds are high that one will eventually need to add support for it. I reached the point in one of my projects where I needed to add support. I've always used the very nice jQuery File Upload plugin, but since I am fairly new to Angular, I want to use this as a chance to explore the behind-the-scenes of creating directives and manually creating HTTP file posts.

Before jumping into any code I'll explain some assumptions. I'll be adding code to three different areas -- the partial, the controller, and the directive. The main javascript file will start off looking something like this:
var app = angular.module("myApp", []); // declare this module, named "myApp". Matches ng-app attribute.
We will assume that my partial file is somehow loaded through some sort of routing.

Now that we have that out of the way, let's jump in.

Creating the directive

First, I'm going to create a directive that I can use for my input tag. According to Angular's documentation, "Directives are markers on a DOM element (such as an attribute, element name, or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children." This will allow me to add functionality to an HTML element just by adding an attribute. I'll add a simple attribute to a file input tag like so:
<!-- Partial-->
<input multiple="" file-uploader="" type="file" />
Now that I've added the "file-uploader" attribute, I need to create a directive that's going to pick up on that and add some functionality:
// Directive
app.directive("fileUploader", [function() {
    return function() {
        console.log("It is working.");
    };
}]);
When I load the page I see the console message. Success! Now on to adding more functionality to the directive.

Expanding the directive

// Directive
app.directive("fileUploader", [function() {
    return function (scope, $elem, attrs) {
        $elem.on("change", function(e) {
            console.log("File changed");
        });
    };
}]);
A little more productive, I now have a jquery event handler for when a new file is picked. Say I want the developer to be able to specify his/her own callback function for when a file is picked. Just like any other binding, it will be assigned as a property on the scope:
// Controller
app.controller("ImportController", ["$scope",
    function ($scope) {
        $scope.onFileSelected = function() {
            console.log("onFileSelected called");
        };
    }
]);
<!-- Partial -->
<input multiple="" file-uploader="onFileSelected()" type="file" />
This still won't work, however. Nothing in our application is actually binding this method to the change event of the file input. If you run this as-is, you'll still get the "File changed" log message, but nothing that says "onFileSelected called". So, I need to modify the directive function:
// Directive
app.directive("fileUploader", ["$parse", function ($parse) {
    return function (scope, $elem, attrs) {
        // fn will be the callback function
        // injected into the directive attribute
        var fn = $parse(attrs["fileUploader"]);

        $elem.on("change", function(e) {
            fn(scope);
        });
    };
}]);
I've modified the directive function to take the string passed into the file-uploader directive and parse out the scope variable assigned to it. This should return a function, as it should be the custom callback function assigned to the scope. Once assigned to fn, it can be called, passing in the scope as the first parameter. Now when the a file is selected, the onFileSelected function I've added to the scope is called. Next, I'd like to actually get some file information back to the custom event handler. This is located inside the event object [todo -- on what browsers]. I'll make modifications to pass back this information.
// Directive
app.directive("fileUploader", ["$parse", function ($parse) {
    return function (scope, $elem, attrs) {
        var fn = $parse(attrs["fileUploader"]);
        $elem.on("change", function (e) {
            fn(scope, { $files: e.target.files });
        });
    };
}]);
// Controller
app.controller("ImportController", ["$scope",
    function ($scope) {
        $scope.onFileSelected = function($files) {
            console.log("onFileSelected called");
            console.log($files);
        };
    }
]);
<!-- Partial -->
<input multiple="" file-uploader="onFileSelected($files)" type="file" />
Inside the directive function, I've added a second parameter to the fn() call. The first parameter sends the scope, and the second parameter will extend the scope by adding/replacing properties on the scope. This is useful if you'd like to pass extra data to the callback function, but you don't want to make any lasting changes to the scope that other parts of the application could see. I'm using the property name "$files", with the dollar sign signifying that this is not a normal scope variable. Inside the controller, I've modified the onFileSelected function to take one parameter which will be assigned in the partial. Inside the partial, I've modified the file-uploader attribute to pass $files to the onFileSelected function. This completes the circle, and ensures that the callback has the data it needs.

Sending the data to the server

Now I'm going to loop through all of the files that were passed through the $files parameter and put together an HTTP request:
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.onFileSelected = function ($files) {
            console.log("onFileSelected called");
            console.log($files);
            for (var i = 0; i < $files.length; ++i) {
                (function() {
                    var $file = $files.item(i);

                    var formData = new FormData();
                    formData.append("file", $file, $file.name); // test

                    var options = {
                        method: "POST",
                        url: "/api/Import",
                        data: formData,
                        headers: { "Content-Type": undefined },
                        transformRequest: angular.identity
                    };

                    $http(options);
                })();

            }
        };
    }
]);
Here I've added a loop to go through all of the attached files and issue an HTTP post for each one. I'm taking advantage of the FormData function, as it makes sending complex HTTP request incredibly easy. Unfortunately, FormData is not supported in versions of Internet Explorer prior to 10. I will add support for some older versions of IE in a future blog post. After building the FormData object, I'm adding it to the HTTP options. I'm also setting the Content-Type header to undefined and the transformRequest property to angular.identity, a bit of Angular magic to parse our FormData object. The controller as it stands will issue one request for every file, which is a bit overkill. It can be modified to lump all files together:
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.onFileSelected = function ($files) {
            var formData = new FormData();
            
            for (var i = 0; i < $files.length; ++i) {
                (function() {
                    var $file = $files.item(i);
                    formData.append("file", $file, $file.name);
                })();
            }

            var options = {
                method: "POST",
                url: "/api/Import",
                data: formData,
                headers: { "Content-Type": undefined },
                transformRequest: angular.identity
            };

            $http(options);
        };
    }
]);
The backend I have this running against will spit out a list of relative paths that the uploaded files can be reached at, so I'm going to add some logging for the response.
// Controller
app.controller("ImportController", ["$scope", "$http",
    function ($scope, $http) {
        $scope.onFileSelected = function ($files) {
            var formData = new FormData();
            
            for (var i = 0; i < $files.length; ++i) {
                (function() {
                    var $file = $files.item(i);
                    formData.append("file", $file, $file.name);
                })();
            }

            var options = {
                method: "POST",
                url: "/api/Import",
                data: formData,
                headers: { "Content-Type": undefined },
                transformRequest: angular.identity
            };

            $http(options).success(function(data, status) {
                if (status != 200)
                    console.log("Error uploading files");
                else {
                    for (var i = 0; i < data.length; ++i)
                        console.log(data[i]);
                }
                    
            });
        };
    }
]);
It's not very pretty, but it functions. In a future post I'll talk about how to add more functionality and dress things up a bit. I eventually want to integrate jquery file uploader, but this is a nice exercise in learning all of the nuts and bolts behind Angular file uploads.

For more information, check out our website.

Wednesday, January 9, 2013

DropDownListFor with Enums

When creating a dropdown list using the DropDownListFor method off of the html helper, you usually have to manually include all of the list items:

@{
    List<selectlistitem> items = new List<selectlistitem>
    {
        new SelectListItem {Text = "Red", Value = "Red", Selected = Model.FavoriteColor == "Red"},
        new SelectListItem {Text = "Green", Value = "Green", Selected = Model.FavoriteColor == "Green"},
        new SelectListItem {Text = "Blue", Value = "Blue", Selected = Model.FavoriteColor == "Blue"}
    };
}

@Html.DropDownListFor(model => model.FavoriteColor, items)

In this case, FavoriteColor is a string property that has some options on a list for convenience. But what if it was an Enum?
public enum Colors
{
    Red,
    Green,
    Blue
}

We can use some fancy maneuvering and LINQ to create a dropdown list in one command:
@Html.DropDownListFor(model => model.FavoriteColor, Enum.GetValues(typeof(Colors)).Cast<colors>().Select(color => new SelectListItem { Text = Colors.ToString(), Value = Colors.ToString(), Selected = model.FavoriteColor == color }))

This LINQ query uses the static method GetValues to get a list of all entries in an enum. They get casted to their appropriate type and then a new SelectListItem is selected out of them. This works great, but it's a bit too much work. Let's try an HtmlHelper extension method:
public static class HtmlHelperExtensions
{
    public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {

    }
}

By creating a new static class and adding it to the same namespace as the view (or adding a "using" statement to the top of the view), we will now see a new overload for the DropDownListFor method which takes only one parameter, the property expression.
@Html.DropDownListFor(model => model.FavoriteColor)

In populating the method, it has to do 5 things:
  1. It must establish the name of the property
  2. It must establish the type of the property and ensure it is an enum
  3. It must get a list of all possible options for the enum
  4. It must create a list of SelectListItems
  5. Finally, it must return a dropdown list
Here is the complete method in action:
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
    var propertyName = ExpressionHelper.GetExpressionText(expression);
    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

    var propertyInfo = metadata.ContainerType.GetProperty(propertyName);

    if (!propertyInfo.PropertyType.IsEnum)
        throw new InvalidParameterException("Must use with a property of an Enum type.");

    var options = Enum.GetValues(propertyInfo.PropertyType).Cast<object>().Select(o => o.ToString()).ToList();
    var descriptions = propertyInfo.PropertyType.GetEnumDescriptions();

    var selectedOption = metadata.Model.ToString();
    var selectList = options.Select(option => new SelectListItem {Text = descriptions[option], Value = option, Selected = option == selectedOption});

    return htmlHelper.DropDownListFor(expression, selectList);
}
For more information, check out our website.

Wednesday, November 14, 2012

Code Refactoring - Improving speed without changing external behavior


As part of our Contract Management solution Contract Guardian, we have developed a reporting tool to enable us to enter complex search terms and fetch contracts satisfying the search terms. Our Rippe & Kingston development team wanted this to be a tool that we could use on any of our custom projects and products.  This tool was developed using ExtJS as the front end and C# ASHX pages to process the query. This sounds really simple except when it was used in real situations that fetch many thousand contracts. When this project was deployed to a client that had more than 5000 contracts, we got the dreaded IE error “A script on this page is causing Internet Explorer to run slowly”.

On further research we determined that a script on our program was taking a long time to finish executing. This is a note from Microsoft1 for Internet Explorer  - Because some scripts may take an excessive amount of time to run, Internet Explorer prompts the user to decide whether they would like to continue running the slow script. Some tests and benchmarks may use scripts that take a long time to run and may want to increase the amount of time before the message box appears. "

Now there were a couple of options to resolve this error. One of the options provided in the Microsoft Knowledge Base includes modification of a registry value on client machines to wait for more time before throwing the notification message. That option would introduce issues during mass deployment across large client installations.  Further, the same script could also be running slower in other browsers as well.

The other option was to go back to the drawing board and refactor2 the script that was causing the issue.  Refactoring is basically recoding the program without affecting the end result of the program.

As noted, the front end was ExtJS . This consists of a Contract grid (A ExtJS Grid Panel) with default columns. The users have options to choose the display fields that will be displayed as columns on the grid.   These display fields are saved on the Cache engine behind our reporting tool.  The Cache saves all the filters (Departments, Companies, Users, Contract Types) used to filter the contracts and the display fields that show up on the Contract Grid.

 Our script calls a corresponding ASHX page that processes the query and the query in turn uses the Cache to get the fields. The filters selected/entered by the user generates the JSON object to be returned to the script. The JSON object consists of the headers that will recreate the ExtJS Grid.Panel columns and the data that will go to the ExtJS store that populates the grid.  

Since the fields are not known until run time, the script was processing every header field, recreating columns, filters, creating a store, paging tool bar and many other things and finally adding the data to the store in the Grid Panel. This took a lot of time.
// Comment


function populateContractGrid(data) {

   // Code omitted .....
        reconfigure_grid_test(newHeaders, newHeaderTypes, newData, dataStore);
   }

//pass the information to the next method from here
function reconfigure_grid_test(headers, types, data, dataStore) {
    // Code omitted .........
    reconfigure_grid(headerFields, headerTypes, dataFields, dataStore);
}
 
function reconfigure_grid(fields, types, data, dataStore) {
    // Code omitted...
    //loop will create grid columns and grid fields, adding them to the above arrays
            
        // Code omitted 
        columnArray.push(column);

       // Code omitted 
        fieldArray.push(field);
    }

    //build the column model that will be used in the contract grid
   
    // Code omitted
    var reader = new Ext.data.ArrayReader(
    {
        totalProperty: ''
    }, record);

    var memoryProxy = new Ext.ux.data.PagingMemoryProxy(data);

    var store3 = new Ext.data.ArrayStore(
    {
        remoteSort: true,
        reader: reader,
        fields: fieldArray,
        baseParams:
        {
            lightWeight: true,
            ext: 'js'
        },
        data: data,
        cm: columnModel,
        proxy: memoryProxy
    });

   // Create the filters for the grid
   // Code omitted
    pagingTBar.bindStore(store3, true);
    contractGrid.reconfigure(store3, columnModel);

    if (contractGrid.plugins) {
        for (var index = 0; index <= contractGrid.plugins.length; index = index + 1) {
            contractGrid.plugins.pop();
        }
    }

    filterPlugin.init(contractGrid);
    summary.init(contractGrid)
    contractGrid.plugins.push(filterPlugin);
    contractGrid.plugins.push(summary);

   
    // Code omitted
}


Our solution to this was to recreate the Grid Panel before fetching the JSON data from the  Query_Handler ASHX page and then replacing the store in the Ext.Grid.Panel with the store recreated from the JSON Object returned by the ASHX page.

So basically when the user selected the fields for the Grid, the fields were saved in the Cache by the DisplayFields_ASHX page and in return a JSON Object with empty data rows was returned by this ASHX page. Our script used this JSON Object to run the code displayed above with empty data (instead of more than 5000 records). 

Then when the query was run, the store of the ExtJS Grid Panel was just replaced with the new store returned by the JSON object without recreating the grid. That resulted in a dramatic change and the script was much faster in IE. Instead of calling the original code, we called a new function demonstrated below, where the store in the Contract Grid was replaced with the data from the JSON object.
// Comment
function populateContractGridWithData(data) {
  // Code Omitted
    var newData = [];
    for (var i = 0; i < arrayRows.length; i++) {
        newData[i] = arrayRows[i].value;
    }
    var store4 = contractGrid.getStore();
    var memoryProxy = new Ext.ux.data.PagingMemoryProxy(newData);
    store4.proxy = memoryProxy;
    pagingTBar.bindStore(store4, true);

    store4.load(
    {
        params:
        {
            start: 0,
            limit: 100
        }
    });



This design provided better user experience and also minimizes the need on system resources.

In summary, for slow operation of any code, one of the best options to resolve these options is to go back to the drawing board and refactor the code to improve speed without changing the external result or behavior of the code.

For more information check our website

References:

Tuesday, November 13, 2012

Advanced Techniques with Database Migrations

While Entity Framework's database migrations will automatically pick up on structure changes, there are times where we want to do a little bit more. Consider the following model:


public class Attachment
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedOn { get; set; }
    public DateTime ModifiedOn { get; set; }
    public string Path { get; set; }
    public long Size { get; set; }
    public string MimeType { get; set; }
}


We'll assume that this model has already been added to the database via a previous migration. Let's say that we'd like to give users the ability to change the file in a given attachment. More than that, let's say that instead of just keeping track of the current file, we would like to have a history of all files that a user has uploaded for versioning. We'll modify our model:

public class Attachment
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedOn { get; set; }
    public DateTime ModifiedOn { get; set; }

    public virtual List<AttachmentFile> AttachmentFiles { get; set; }
}

public class AttachmentFile
{
    public int Id { get; set; }
    public DateTime CreatedOn { get; set; }
    public string Path { get; set; }
    public long Size { get; set; }
    public string MimeType { get; set; }

    public Attachment { get; set; }
    public int AttachmentId { get; set; }
}


Instead of the Attachment model containing information about the file, it has a one-to-many relationship with AttachmentFiles. We will assume that the AttachmentFile with the latest date will be used as the "primary" attachment.

Now we need to carry this change to our database. Using the package manager console, we run "add-migration AddAttachmentFiles", and we come up with this:

public partial class AddAttachmentFiles : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "AttachmentFiles",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    CreatedOn = c.DateTime(nullable: false),
                    Path = c.String(),
                    Size = c.Single(nullable: false),
                    MimeType = c.String()
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("Attachments", t => t.AttachmentId, cascadeDelete: true)
            .Index(t => t.AttachmentId);
        DropColumn("Attachments", "Path");
        DropColumn("Attachments", "Size");
        DropColumn("Attachments", "MimeType");
    }

    public override void Down()
    {
        AddColumn("Attachments", "MimeType", c => c.String());
        AddColumn("Attachments", "Size", c => c.Single(nullable: false));
        AddColumn("Attachments", "Path", c => c.String());
        DropIndex("AttachmentFiles", new[] { "AttachmentId" });
        DropForeignKey("AttachmentFiles", "AttachmentId", "Attachments");
        DropTable("AttachmentFiles");
    }
}


This looks acceptable. This migration will create the new table and remove the desired columns from the Attachments table. But what about our attachment data in the Attachments table? There is no way for Entity Framework to know that we want to do anything with the data in the columns we're deleting from the Attachments table. So, we do it manually. The DbMigration class has a Sql() method we can use to execute raw SQL against our database. If we modify our migration:

public partial class AddAttachmentFiles : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "AttachmentFiles",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    CreatedOn = c.DateTime(nullable: false),
                    Path = c.String(),
                    Size = c.Single(nullable: false),
                    MimeType = c.String()
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("Attachments", t => t.AttachmentId, cascadeDelete: true)
            .Index(t => t.AttachmentId);

        Sql("");

        DropColumn("Attachments", "Path");
        DropColumn("Attachments", "Size");
        DropColumn("Attachments", "MimeType");
    }

    public override void Down()
    {
        AddColumn("Attachments", "MimeType", c => c.String());
        AddColumn("Attachments", "Size", c => c.Single(nullable: false));
        AddColumn("Attachments", "Path", c => c.String());

        Sql("");

        DropIndex("AttachmentFiles", new[] { "AttachmentId" });
        DropForeignKey("AttachmentFiles", "AttachmentId", "Attachments");
        DropTable("AttachmentFiles");
    }
}


we can write SQL queries that will be executed in the flow of commands. The first query will move data from the to-be-deleted columns in the Attachments table to the AttachmentFiles table:

INSERT INTO AttachmentFiles (CreatedOn, Path, Size, MimeType, AttachmentId)
SELECT CreatedOn, Path, Size, MimeType, Id AS AttachmentId FROM Attachments


This query selects only the relevant fields from the Attachments table and inserts them into the AttachmentFiles. Since there will be exactly one set of file data per Attachment record, there will be exactly one AttachmentFile per Attachment. This means that the "primary" AttachmentFile for each Attachment will by default be the previous contents of the Attachments table simply because it will be the only record.

The second query will move data from the AttachmentFiles table back into the re-created columns in the Attachments table:

UPDATE Attachments
SET Attachments.Path = af1.Path, Attachments.Size = af1.Size, 
    Attachments.MimeType = af1.MimeType
FROM Attachments
INNER JOIN AttachmentFiles af1 on af1.Id =
    (SELECT TOP 1 Id FROM AttachmentFiles af2
    WHERE af2.AttachmentId = Attachments.Id 
        ORDER BY af2.CreatedOn DESC)
This query is much more complicated, because we have many AttachmentFiles per Attachment, and we need to select out only one per Attachment. We do this by using a subquery that selects out the most recent AttachmentFile for a given AttachmentId.

Our final migration looks like this:

public partial class AddAttachmentFiles : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "AttachmentFiles",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    CreatedOn = c.DateTime(nullable: false),
                    Path = c.String(),
                    Size = c.Single(nullable: false),
                    MimeType = c.String()
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("Attachments", t => t.AttachmentId, cascadeDelete: true)
            .Index(t => t.AttachmentId);

        Sql("INSERT INTO AttachmentFiles " +
            "(CreatedOn, Path, Size, MimeType, AttachmentId) " +
            "SELECT CreatedOn, Path, Size, MimeType, Id AS AttachmentId " +
            "FROM Attachments");

        DropColumn("Attachments", "Path");
        DropColumn("Attachments", "Size");
        DropColumn("Attachments", "MimeType");
    }

    public override void Down()
    {
        AddColumn("Attachments", "MimeType", c => c.String());
        AddColumn("Attachments", "Size", c => c.Single(nullable: false));
        AddColumn("Attachments", "Path", c => c.String());

        Sql("UPDATE Attachments " +
            "SET Attachments.Path = af1.Path, Attachments.Size = af1.Size, " +
                "Attachments.MimeType = af1.MimeType " +
            "FROM Attachments " +
            "INNER JOIN AttachmentFiles af1 on af1.Id = " +
                "(SELECT TOP 1 Id FROM AttachmentFiles af2 " +
                "WHERE af2.AttachmentId = Attachments.Id " +
                    "ORDER BY af2.CreatedOn DESC)");"

        DropIndex("AttachmentFiles", new[] { "AttachmentId" });
        DropForeignKey("AttachmentFiles", "AttachmentId", "Attachments");
        DropTable("AttachmentFiles");
    }
}


For more information, check out our website.

Tuesday, October 30, 2012

Functions as objects in Javascript

In Javascript, functions are objects.

That statement probably comes off a little underwhelming. Let's look at an example.

function addTheNumbers(num1, num2) {
    return num1 + num2;
}

function subtractTheNumbers(num1, num2) {
    return num1 - num2;
}

var func; 

func = addTheNumbers;
var result1 = func(1, 2); // 3

func = subtractTheNumbers;
var result2 = func(1, 2); // -1

We defined two different functions, one for adding and one for subtracting. Both take the same number of parameters. We then separately assign each of the functions to another variable, and then call that variable. You can point to any function just by using the name of that function as a variable.

For more information, check out our website.

Using a Windows Service as a Timer

BACKGROUND: Recently, I came across an issue with an MVC application I was developing. This application is composed of two sections: the Frontend and Backend. The Frontend contains all the visuals (what the user sees) and requests information from the Backend. The Backend fetches all the data from the database and sent it to the Frontend.

ISSUE: The communication between the Frontend and the Backend was being lost randomly. I was never able to witness it. We only knew it happened because the Frontend would display, but no data would be present.

RESOLUTION: I am going to create a windows service that is going to run on the machine hosting the Frontend project. This service will start a timer that will tick every x minutes (5 minutes for this application). On every tick, the Frontend will send a request to the Backend. If the request does NOT return "true" from the Backend, an error will be written to the log.

STEPS: *Using Visual Studio 2010 in .NET v.4

Step 1: Create a basic Windows Service Project and Setup Project
  • http://msdn.microsoft.com/en-us/library/aa984464(v=vs.71).aspx
  • You can ignore the OnContinue() action
  • Tip - For step 7 under the section 'To create the installers for your service', I chose to use Local System instead of Local Service. Local Service was NOT working for me. I would get a permissions error when I attempted to start the service and it would not start.
Step 2: Install the service, Start the service and Verify that it is running by checking the log in the Event Viewer

Step 3: Uninstall the service

Step 4: Add the Timer to the Windows Service
  • Add private System.Timers.Timer _timer = new System.Timers.Timer() to the top of the class
  • Call SetupTimer() from the OnStart function
private void SetupTimer(){
   int interval = 300000; //default to 5 minutes
   this._timer.Interval = interval;
   this._timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerElapsed);
   this._timer.Start();
}
  • Create another function called TimerElapsed() in the same file
 
void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e){
   eventLog1.WriteEntry("Communication check...");
   //Connect to a Backend function that returns true
   if (response)
     //Do nothing or write to log "success"
   else
     eventLog1.WriteEntry("Communication to Backend Failed",EventLogEntryType.Error);
     //EventLogEntryType.Error makes the entry get marked as an error in the Event Log
}

Step 5: Build the Windows Service Project

Step 6: Build the Setup Project
  • Be sure the build these projects in the proper order!
Step 7: Install the service, Start the service and Verify that it is running by checking the log in the Event Viewer


For more information, visit our site: www.rippe.com/index.htm

Friday, October 26, 2012

Editor Templates 101

Editor templates are a great way to reduce duplicated code in your project. When you're writing an HTML form, a lot of your fields can be simple text boxes, but sometimes we want a bit more functionality. Instead of writing the same code over and over to customize our editors, we put all of that code in one location and refer to it from inside our view.

I'd like to note that there are many different ways to use editor templates -- I'm just going to focus on a couple basic ways in this post.

Let's consider a view that contains a form:

@model MvcApplication.Models.Document

<h2>Create</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Document</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Author)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Author)
            @Html.ValidationMessageFor(model => model.Author)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.FileType)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FileType)
            @Html.ValidationMessageFor(model => model.FileType)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.DateUploaded)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.DateUploaded)
            @Html.ValidationMessageFor(model => model.DateUploaded)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

This is a standard view made using the "Create" template. It has taken all of the fields of our Document model and has stubbed out editors for them. Here is the controller action that backs it up:

public ViewResult Create()
{
    return View();
}

Very simple. When the view is rendered to the browser, we see simple text boxes for each field.



But what if we want to do more than that? Let's say we want to be able to pick up our DateUploaded field with javascript so we can throw on a datepicker. Ok, how about this:

<div class="editor-label">
    @Html.LabelFor(model => model.DateUploaded)
</div>
<div class="editor-field">
    <input id="DateUploaded" name="DateUploaded" data-datepicker="true" value="@Model.DateUploaded" />
    @Html.ValidationMessageFor(model => model.DateUploaded)
</div>

Would this work? Yes, we can pick up on the data-datepicker tag with javascript. But this is beyond dirty. We have just lost a lot of the functional advantages we get by using the EditorFor method on the HTML helper. Sure, this would work... but what if you change the name of the DateUploaded field? If we used the EditorFor, we would see a big nasty error message when we tried to load the view. But using the raw HTML, it's still valid. The http POST won't even fail. We just won't have our DateUploaded field populated. So, how do we accomplish this?

Let's take a look at the overloads for EditorFor.

EditorFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
EditorFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object additionalViewData)
EditorFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string templateName)
...

(There are more but I cut them out for brevity). Look at the third one in the list. What is templateName? Specifying a value for templateName is the most straightforward way to use editor templates. When you provide a template name, MVC will look in a few different locations for a view with a name matching the value you provide as templateName. It's very similar to how calling View() in a controller action looks for a view with a name matching the action name. The view engine checks these locations in order:
  1. ~/Areas/[AreaName]/Views/[ControllerName]/EditorTemplates/[TemplateName].cshtml
  2. ~/Areas/[AreaName]/Views/Shared/EditorTemplates/[TemplateName].cshtml
  3. ~/Views/[ControllerName]/EditorTemplates/[TemplateName].cshtml
  4. ~/Views/Shared/EditorTemplates/[TemplateName].cshtml
So, let's modify our view code to look for an editor template named "DatePicker":

<div class="editor-label">
    @Html.LabelFor(model => model.DateUploaded)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.DateUploaded, "DatePicker")
    @Html.ValidationMessageFor(model => model.DateUploaded)
</div>

Perfect! Now we need to add a "DatePicker" partial view. But what do we put there? It's important to note that using this EditorFor overload is very similar to calling PartialView. Whatever editor template it finds, it will render that in place like a partial view. So, everything we put inside the DatePicker editor template is exactly what we get rendered. Let's try something simple to test.

@model DateTime?
@Html.TextBox("txtDatePicker")

We will save that as ~/Views/Shared/EditorTemplates/DatePicker.cshtml. Notice what we have declared as our model type. DateTime makes sense, but why nullable? Well, if we load our "Create" view with a null model (as is the case with our Create action), then it doesn't have a value for DateUploaded. We have to account for a null value in this case, even if the type on the model is not nullable. 

Let's take a look at the HTML this generates:

<div class="editor-field">
    <input id="DateUploaded_txtDatePicker" name="DateUploaded.txtDatePicker" type="text" value="">
    <span class="field-validation-valid" data-valmsg-for="DateUploaded" data-valmsg-replace="true"></span>
</div>

Notice how it changed the name and id attributes? When you provide a name for an element inside an editor template, what you're really doing is supplying the name of the property you're using inside the editor template. Thus, you get DateUploaded.txtDatePicker. If DateUploaded was an object that had its own properties, this would be perfect. But in our case we just want a textbox that refers to itself. We can accomplish that by passing in an empty string for the name.

@model DateTime?
@Html.TextBox("")

This may seem strange at first, but the truth is we don't actually need to supply a name for our textbox. That name is picked up from the name of the property specified on our Create view.

<div class="editor-field">
    <input data-val="true" data-val-required="The DateUploaded field is required." id="DateUploaded" name="DateUploaded" type="text" value="">
    <span class="field-validation-valid" data-valmsg-for="DateUploaded" data-valmsg-replace="true"></span>
</div>

This looks much better, and we've even picked up on the validation attributes. One thing we're not picking up on is the value of the field. You can't tell in the above HTML because we're using a null model, but even if we had a value, it would not get rendered because we're just spitting out a blank textbox right now. Let's fill in the value parameter by using the ViewData object:

@model DateTime?
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue)

By this point, we now have a correctly functioning textbox that will be populated with a value from the model, and will submit the correctly named field. So it functions exactly as it did before we started with this editor template nonsense, minus a few CSS classes. Given this, is there even a point to making a custom editor template? If you stop now, then no. But let's tweak our editor template code just a bit further:

@model DateTime?
@Html.TextBox("", string.Format("{0:MM/dd/yyyy}", ViewData.TemplateInfo.FormattedModelValue), new { data_datepicker = true })

Now we've got something. Instead of just using the FormattedModelValue, we're calling string.Format to show the date portion of the DateTime. Since we don't care about the time for DateUploaded, we don't need to see it. We also have included an anonymous object, setting data_datepicker to true. For this overload of the TextBox method, the third parameter is an object that will be used to set attributes on the HTML element that gets generated. Here is the HTML output:

<div class="editor-field">
    <input data-datepicker="True" data-val="true" data-val-required="The DateUploaded field is required." id="DateUploaded" name="DateUploaded" type="text" value="">
    <span class="field-validation-valid" data-valmsg-for="DateUploaded" data-valmsg-replace="true"></span>
</div>

Notice the addition of the "data-datepicker" field. (aside: the view engine has converted our underscore to a hypen -- this is because hypens aren't allowed in identifiers in C#.) We can pick up on this in javascript and assign datepickers to our field:


$(function() {
    $("[data-datepicker]").datepicker();
});


As simple as that. If we add that javascript to our _Layout.cshtml file, it will show up on every single page. With our DatePicker editor template, we can now instantly attach a jQuery UI datepicker to an editor, simply by setting the template name parameter to "DatePicker".

For more information, check out our website.

Friday, October 19, 2012

Enhancing Your Entity Repositories

Using a repository pattern for data access gives you clear separation and abstraction in your data layer.  The basic idea of adding repositories for data access is illustrated nicely on MSDN:



In the case of an MVC application, your controllers fit into the "Client Business Logic" area, and your Database resides in the "Data Source" area.  What's left is the repository itself, here is a typical example generated using the MVC Scaffolding project.


namespace DataModel.Models
{
    public class PartyRepository : IPartyRepository
    {
        public IQueryable<Party> All
        {
            get { return _context.Parties; }
        }
        public IQueryable<Party> AllIncluding(params Expression<Func<Party, object>>[] includeProperties)
        {
            IQueryable<Party> query = _context.Parties;
            foreach (var includeProperty in includeProperties) {
                query = query.Include(includeProperty);
            }
            return query;
        }
        public Party Find(int id){ return _context.Parties.Find(id);}
       
  //Methods removed for brevity
    }
    public interface IPartyRepository : IDisposable
    {
        IQueryable<Party> All { get; }
        IQueryable<Party> AllIncluding(params Expression<Func<Party, object>>[] includeProperties);
        Party Find(int id);
        //Methods removed for brevity
    }
}



The repository itself abstracts the dirty work of dealing with the context, and provides a great element of re-usability in your application (you can inject a repository anywhere you like and it will use the same context code).  Here are some sample use cases for some of the above methods:


_repository.Find(id); //simple lookup
_repository.All; //get everything
_repository.AllIncluding(model => model.Property1, model => model.Property2); //Get all and include some navigation properties
_repository.AllIncluding(<insert ALL navigation properties>); //Full eager fetch



Simple enough, but this structure raises some concerns:

  1. Specifying the navigation properties in your controller actions when using the repositories feels like a violation of your abstractions.  Dealing with relationships between data driven objects should stick to the data layer as much as possible.
  2. There really isn't a good method to do a full eager fetch in this scenario.  You could provide all of the include properties but that creates a maintainability issue when you add a new navigation property and have to change every location you used the including options.

Our solution to this issue was a small and simple re-working of these methods, here is an example from another object:

namespace DataModel.Models
{
    public class DocumentRepository : IDocumentRepository
    {
        private readonly Expression<Func<Document, object>>[] _allIncludes =
            {
                d => d.Department,
                d => d.Organization,
                d => d.DocumentStatus,
                d => d.DocumentType,
                d => d.FavoriteUsers
            };
        public IQueryable<Document> All(params Expression<Func<Document, object>>[] includeProperties)
        {
            IQueryable<Document> query = _context.Documents;
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query.Where(doc => doc.Organization.AccountId == accountId);
        }
        public IQueryable<Document> All(bool eager, params Expression<Func<Document, object>>[] includeProperties)
        {
            var includes = eager ? _allIncludes : includeProperties;
            return All(accountId, includes);
        }
        public Document Find(int id, params Expression<Func<Document, object>>[] includeProperties)
        {
            IQueryable<Document> query = _context.Documents;
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query.SingleOrDefault(doc => doc.Id == id);
        }
        public Document Find(int id, bool eager, params Expression<Func<Document, object>>[] includeProperties)
        {
            var includes = eager ? _allIncludes : includeProperties;
            return Find(id, includes);
        }
  //Methods removed for brevity
    }
    public interface IDocumentRepository
    {
        IQueryable<Document> All(params Expression<Func<Document, object>>[] includeProperties);
        IQueryable<Document> All(bool eager = false, params Expression<Func<Document, object>>[] includeProperties);
        Document Find(int id, params Expression<Func<Document, object>>[] includeProperties);
        Document Find(int id, bool eager = false, params Expression<Func<Document, object>>[] includeProperties);
        //Methods removed for brevity
    }
}

We simply added an eager option and an overload for the Find and the All(converting it from property to method).  Here are the new use cases:

_repository.Find(id); //simple lookup
_repository.All(); //get everything - lazy loaded
_repository.All(model => model.Property1, model => model.Property2); //Get all and include some navigation properties
_repository.All(true); or the more readable: _repository.All(eager:true); //Full eager fetch

With this modification, controlling the type of fetch you want to do is much more clear, and if we add navigation properties to the model, we only need to update the allIncludes property in the repository, not everywhere the repository is used for eager fetching.  We also still preserved the ability to lazy load, as well as specify exactly the properties you want during a fetch.

A side effect though, is we have some strange edge cases that result, for example:

_repository.All(eager:true, model => model.Property1);

In this case, the provided property is ignored and all properties are fetched.  We chose to lay the blame for this sort of issue on the developer as there are easier ways to use the methods to achieve the desired result, whatever that may be.


All code examples taken from our next version of Contract Guardian.

For more information, check out our Web Site.


Tuesday, September 18, 2012

Common 2012 Fall Conference & Expo



This three-day Power Systems educational and networking event will be packed with over 100 educational sessions on a large variety of topics, including vendor-led sessions, and pre-conference workshops. As expected from all COMMON conferences, attendees will have the opportunity to further enhance their education through numerous networking events and meeting with leading solution providers in the tabletop-style expo.
Visit the COMMON Web site for more information.

Best of all: Two of my favorite vendors will be exhibiting at the Conference.  LANSA & VAULT400

Thursday, March 8, 2012

Microsoft Visual Studio 2010

Scott Guthrie's Blog on Microsoft ASP.NET technologies including the new MVC4 update is excellent.  You may want to take a look at this blog because it offers so much information on Microsoft Development technologies.  Scott lives in Seattle and builds products for Microsoft.  See Scott take you through the ASP.NET MVC4 Beta.  Just click here to view the video. It contains a lot of excellent information.

In summary the presentation covers the new features in MVC4 that provide a rich set of enhancements to this tool set that is accessible from within Microsoft Visual Studio 2010.  MVC4 comes built-into VS2011 as well.  It does install side by side with MVC2 and MVC3 in the same Visual Studio installation on your development workstation.
  • Bundling/Minification Support - Improves performance on your website. 100% Automatic
  • Database Migrations - Allows production database schema to be automatically updated.
  • Web APIs - Great support in VS for creating Web APIs. Easily create HTTP Services.
  • Mobile Web -  Improved developer support for developing Mobile applications for the phone and tablet.
  • Real Time Communications - Allows for Client to Server Persistent connections over HTTP using SignalR.
  • Asynchronous Support - Reduces the number of threads and server resources; Increases scaleability.
If you need a web application developed for your business, just contact us by calling 513-977-4544 or click here to learn more.

Tuesday, February 21, 2012

LANSA 2012 User Conference



As many look forward to the release of LANSA 13.0 it is sometimes rewarding to reflect on the history of LANSA.  The early years were filled with concepts like:


  • Data Repository
  • 4th Generation Language
  • Multilingual
  • Templates
  • Database Triggers
You could recap all of the commands/parameters on a simple quick reference card.
Marketing was generally limited to print media.  (Remember printed trade rags?)
Presentations were given with slide projectors.
You actually had to fly somewhere to present.
Yes LANSA and the industry have come a long way.

Fast forward and we look forward to the conference that will feature:
  • Cloud Based Labs (Bring your laptop)
  • Web & Mobile App development
  • Business Objects
  • Some of the famous and familiar speakers in the LANSA community:
    • Diane Joester
    • Mark Duignan
    • Don Nelson
    • Madan Divaker
    • David Brault
    • and many others
  • Opportunity to network
If you have not registered or are just interested.  Click Here.

Tuesday, November 15, 2011

LANSA Powers up Windows Interoperability

I read in System i NETWORK that LANSA has signed up 50 British i users to a free one-day conference on Windows interoperability in December.  Those of us that have used LANSA for many years readily appreciate the ability to work with and among multiple platforms via a single tool set.  Read Article.

Wednesday, July 13, 2011

Mobile Applications....Go Android!

In the past six months, as Android has gained momentum and market share. Last week, comScore reportedthat in the three months ending in May, Android was the top smartphone platform in the U.S. with 38 percent of existing devices. It grew faster than iOS, which reached 26.6 percent, while Research In Motion's BlackBerry platform fell to 24.7 percent, comScore said.   Good News: aXes and LANSA Wams can deliver to both!


Footnote: Is it a just a coincidence that the Android is colored Green like LANSA?

Thursday, May 19, 2011

Cross Browser Compatibility and LANSA aXes


With the new release of aXes version 2.1 (Beta version) all the major browser types are supported.  In prior versions, the aXes product suite only supported the Microsoft (MS) IE browser.  Now aXes supports the following browsers: MS-IE, Firefox, Google Chrome, and Safari.  All of these browsers combined represent a huge user base.  Opening up aXes to all of these types of browsers vastly broadens the user community for who can use aXes and thus, iSeries applications via the web.  The only browser not sanctioned currently is Opera.  It’s officially unsupported but it executes with this new version.  Opera is a Java based browser so it’s very popular on mobile devices.

For more information or to download a free 30 day evaluation copy of aXes, please visit our website.  For further questions on aXes or other LANSA products, contact us at 1-800-887-1834 or request information by clicking here.

Wednesday, February 2, 2011

Most Popular Browsers

I can remember when it was assumed that you were using Internet Explorer.  That is certainly not the case anymore.  Look at the latest trends and you will notice that it's use has dropped significantly.  Also note how fast Google Chrome is being adopted as the default browser.



Can't wait to see what the landscape looks like next year!

Tuesday, November 16, 2010

Mixed message in the 2010 iPulse Survery conducted by LANSA


The 2010 iPulse Survery conducted by LANSA reminds me of the current economy.  The steep decline has stopped, but has no steam in the engine to rebound.  I am curious what other vendors are doing to address this issue.   Your opinion to the survey is appreciated.  I will publish the results in this blog.  Thanks!


Wednesday, November 10, 2010

No LANSA is not a Cabinet Pull (Handle)

Allow me to vent.  Over the years I have been mildly irritated by my search results when they directed me to sites like IKEA, REMODELISTA and even LANSA Flight 502 on Wikipedia.

The other evening I decided to turn my irritation around and see what the products have in common.  My Aha moment resulted in Modernization!  Continue to indulge my ramblings:

Background:  Old, yet functional kitchen.  Here is my version of some marketing copy for a site like IKEA:
With the new LANSA pulls you can transform dingy cupboards into something more contemporary and infinitely more livable.  

Background:  Old, yet functional 5250 legacy application.  Potential marketing copy from LANSA: With LANSA you can transform dingy hard to use legacy applications into something more contemporary and infinitely more usable.

I just had to vent!


NO...LANSA is not a Cabinet Pull

Allow me to vent:  Over the years I have been mildly irritated by my search efforts when they directed me to sites like IKEA ,  REMODELISTA and even LANSA Flight 502 on Wikipedia.
The other evening I decided to spend a little time and look at it from a different perspective:  What do they have in common?  The modernization theme jumped out at me!                                        
Background:  Old, yet functional kitchen.  Here is some potential marketing copy from a site like IKEA: With new LANSA pulls you can transform dingy cupboards into something more contemporary and infinitely more livable.   These easy to use products are also reasonably priced.  
Background:  Old, yet functional 5250 legacy application.
Now the same message from LANSA:  With LANSA you can transform dingy hard to use legacy systems into something more contemporary and infinitely more useable.   These easy to use tools from LANSA are also reasonably priced.

Sunday, October 17, 2010

New Version of aXes released

Probably the most exciting part of this release is the support for Mobile applications.  Below is a quick recap:


From LANSA, RPG, COBOL or CL applications running on your IBM i (AS/400, iSeries, System i), you can now deliver your business information and data direct to mobile devices — such as iPhones, iPads, Android devices and the latest technology from Blackberry and with Windows Mobile 7 — with Zero Deployment on the mobile devices.

aXes Mobile lets you leverage the growing power of HTML 5 and the emerging industry standard WebKit browser family to provide ubiquitous access to IBM i server based applications.


Design the new RPG, COBOL or CL application with the following in mind:

  • Mobile devices have limited screen size available
  • Use of GUI controls should replace more traditional 5250 screen interactions
  • AJAX Web 2.0 capabilities can be added to any screen to maximize appearance and functionality
  • Send and receive only the data required to minimize the traffic
  • Create RPG, COBOL or CL program(s) to send and receive the information to each mobile screen
  • Layout each mobile screen using aXes eXtensions to:
  • Add controls like drop downs, group boxes, hyperlinks, images, text labels, checkboxes, and calendars
  • Add buttons with scripts that control what happens when they are clicked
  • Alter the position of fields on the screen
  • Insert bars, stripes, colors and color gradients to enhance the appearance
  • aXes handles the rest
Reuse Existing Applications — Let aXes Mobile do the work

You can use your existing RPG, COBOL and CL 5250 applications and access these applications via mobile devices such as smart phones and tablet PCs without changing one line of the underlying programs.


Reuse Existing Applications - Let aXes Mobile do the work
aXes converts existing 5250 screens to Web pages on-the-fly without change to the underlying applications, and modernizes the screen to create rich Internet applications - all accessible from mobile devices.

Graphical user interface out-of-the-box
  • No source code to change. Nothing to recompile. Works instantly, even on 5250 screens for which you do not have the source code (screens from OS/400, purchased packages, etc)
  • Web enabled terminal emulation straight to the mobile device
  • Automatically handled by aXes
Use the aXes eXtensions to enhance the GUI

  • No need to change the underlying RPG, COBOL or CL code
  • Layout each screen using aXes eXtensions:
    • Add controls like drop downs, group boxes, hyperlinks, images, text labels, checkboxes, and calendars
    • Add buttons with scripts that control what happens when they are clicked
    • Alter the position of fields on the screen
    • Insert bars, stripes, colors and color gradients to enhance the appearance
  • aXes handles the rest


This is truly exciting and very timely!  Learn more about aXes and other LANSA products.