AngularJS: Factory to show/hide loader

Standard
   
app.factory("ajaxLoader", [
        function () {
            return {
                show: function() {
                    $('#dvProgress').show();
                },
                hide: function() {
                    $('#dvProgress').hide();
                }
            };
        }
    ]);

Invoking Code inside controller(you have to include above factory into
controller inject list):

ajaxLoader.hide();
ajaxLoader.show()

SFTP File upload using ASP.NET Web API and AngularJS

Standard
Uplaod files to SFTP using ASP.NET Web API and AngularJS upload plugin by nervgh with Bootstrap 3.0

Introduction

Angular-file-upload directive by nervgh is an awesome lightweight AngularJS directive which handles file upload for you and lets you upload files asynchronously to the server. This post will give you basic understanding on how to upload files by using this directive together with .NET WebAPI service on SFTP server. For the purpose of this tutorial, I’ll keep everything as simple as possible since the focus here is on connecting AngularJS and async upload with a .NET WebAPI service and not on additional functionality which can be built afterwards around angular-file-upload.

Background

From the unix man page: “sftp is an interactive file transfer program, similar to ftp, which performs all operations over an encrypted ssh transport”.
Why is SFTP better than FTP?
In FTP all data is passed back and forth between the client and server without the use of encryption. This makes it possible for an evesdropper to listen in and retrieve your confidential information including login details. With SFTP all the data is encrypted before it is sentsent across the network.

Using the code

A brief description of how to use the article or code. The class names, the methods and properties, any tricks or tips.

Html Code:

<html id="ng-app" ng-app="app">
<head>
    <title>Simple example</title>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/
     bootstrap.min.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
    <script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
    <script src="~/Scripts/angular-file-upload.js"></script>
    <script src="~/Scripts/controllers.js"></script>
    <style>
        .my-drop-zone {
            border: dotted 3px lightgray;
        }

        .nv-file-over {
            border: dotted 3px red;
        }
        .another-file-over-class {
            border: dotted 3px green;
        }

        html, body {
            height: 100%;
        }
    </style>
</head>
<body ng-controller="AppController" nv-file-drop="" uploader="uploader"
filters="queueLimit, customFilter">
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <h3>Select files</h3>
                <div ng-show="uploader.isHTML5">
                    <div nv-file-drop="" uploader="uploader">
                        <div nv-file-over="" uploader="uploader"
                            over-class="another-file-over-class" class="well my-drop-zone">
                            Another drop zone with its own settings
                        </div>
                    </div>
                </div>
                Single
                <input type="file" nv-file-select="" uploader="uploader" />
            </div>
            <div class="col-md-9" style="margin-bottom: 40px">
                <h3>Upload queue</h3>
                <p>Queue length: {{ uploader.queue.length }}</p>
                <table class="table">
                    <thead>
                        <tr>
                            <th width="50%">Name</th>
                            <th ng-show="uploader.isHTML5">Size</th>
                            <th ng-show="uploader.isHTML5">Progress</th>
                            <th>Status</th>
                            <th>Actions</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="item in uploader.queue">
                            <td><strong>{{ item.file.name }}</strong></td>
                            <td ng-show="uploader.isHTML5" nowrap>
                            {{ item.file.size/1024/1024|number:2 }} MB</td>
                            <td ng-show="uploader.isHTML5">
                                <div class="progress" style="margin-bottom: 0;">
                                    <div class="progress-bar" role="progressbar"
                                     ng-style="{ 'width': item.progress + '%' }"></div>
                                </div>
                            </td>
                            <td class="text-center">
                                <span ng-show="item.isSuccess"><i class="glyphicon glyphicon-ok">
                                </i></span>
                                <span ng-show="item.isCancel"><i class="glyphicon
                                glyphicon-ban-circle"></i></span>
                              <span ng-show="item.isError"><i class="glyphicon glyphicon-remove">
                                </i></span>
                            </td>
                            <td nowrap>
                                <button type="button" class="btn btn-success btn-xs"
                                 ng-click="item.upload()" ng-disabled="item.isReady ||
                                 item.isUploading || item.isSuccess">
                                    <span class="glyphicon glyphicon-upload"></span> Upload
                                </button>
                                <button type="button" class="btn btn-warning btn-xs"
                                 ng-click="item.cancel()" ng-disabled="!item.isUploading">
                                    <span class="glyphicon glyphicon-ban-circle"></span> Cancel
                                </button>
                                <button type="button" class="btn btn-danger btn-xs"
                                 ng-click="item.remove()">
                                    <span class="glyphicon glyphicon-trash"></span> Remove
                                </button>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <div>
                    <div>
                        Queue progress:
                        <div class="progress" style="">
                            <div class="progress-bar" role="progressbar"
                             ng-style="{ 'width': uploader.progress + '%' }"></div>
                        </div>
                    </div>
                    <button type="button" class="btn btn-success btn-s"
                     ng-click="uploader.uploadAll()"
                     ng-disabled="!uploader.getNotUploadedItems().length">
                        <span class="glyphicon glyphicon-upload"></span> Upload all
                    </button>
                    <button type="button" class="btn btn-warning btn-s"
                     ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
                        <span class="glyphicon glyphicon-ban-circle"></span> Cancel all
                    </button>
                    <button type="button" class="btn btn-danger btn-s"
                    ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
                        <span class="glyphicon glyphicon-trash"></span> Remove all
                    </button>
                </div>
            </div>
        </div>
    </div>
</body>

AngularJS Controller Code

'use strict';

angular
.module('app', ['angularFileUpload'])
.controller('AppController', ['$scope', 'FileUploader', function($scope, FileUploader) {
    // Uploader Plugin Code
 
    var uploader = $scope.uploader = new FileUploader({
        url: window.location.protocol + '//' + window.location.host +
             window.location.pathname + '/api/Upload/UploadFile'
    });

    // FILTERS

    uploader.filters.push({
        name: 'extensionFilter',
        fn: function (item, options) {
            var filename = item.name;
            var extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
            if (extension == "pdf" || extension == "doc" || extension == "docx" ||
                extension == "rtf")
                return true;
            else {
                alert('Invalid file format. Please select a file with pdf/doc/docs or
                rtf format and try again.');
                return false;
            }
        }
    });

    uploader.filters.push({
        name: 'sizeFilter',
        fn: function (item, options) {
            var fileSize = item.size;
            fileSize = parseInt(fileSize) / (1024 * 1024);
            if (fileSize <= 5)
                return true;
            else {
                alert('Selected file exceeds the 5MB file size limit.
                       Please choose a new file and try again.');
                return false;
            }
        }
    });

    uploader.filters.push({
        name: 'itemResetFilter',
        fn: function (item, options) {
            if (this.queue.length < 5)
                return true;
            else {
                alert('You have exceeded the limit of uploading files.');
                return false;
            }
        }
    });
 
    // CALLBACKS
 
    uploader.onWhenAddingFileFailed = function (item, filter, options) {
        console.info('onWhenAddingFileFailed', item, filter, options);
    };
    uploader.onAfterAddingFile = function (fileItem) {
        alert('Files ready for upload.');
    };

    uploader.onSuccessItem = function (fileItem, response, status, headers) {
        $scope.uploader.queue = [];
        $scope.uploader.progress = 0;
        alert('Selected file has been uploaded successfully.');
    };
    uploader.onErrorItem = function (fileItem, response, status, headers) {
        alert('We were unable to upload your file. Please try again.');
    };
    uploader.onCancelItem = function (fileItem, response, status, headers) {
        alert('File uploading has been cancelled.');
    };

    uploader.onAfterAddingAll = function(addedFileItems) {
        console.info('onAfterAddingAll', addedFileItems);
    };
    uploader.onBeforeUploadItem = function(item) {
        console.info('onBeforeUploadItem', item);
    };
    uploader.onProgressItem = function(fileItem, progress) {
        console.info('onProgressItem', fileItem, progress);
    };
    uploader.onProgressAll = function(progress) {
        console.info('onProgressAll', progress);
    };
        
    uploader.onCompleteItem = function(fileItem, response, status, headers) {
        console.info('onCompleteItem', fileItem, response, status, headers);
    };
    uploader.onCompleteAll = function() {
        console.info('onCompleteAll');
    };

    console.info('uploader', uploader);
}]);

Web API Controller

[System.Web.Http.HttpPost]
public void UploadFile()
{
    if (HttpContext.Current.Request.Files.AllKeys.Any())
    {
        var httpPostedFile = HttpContext.Current.Request.Files["file"];
  bool folderExists = Directory.Exists(HttpContext.Current.Server.MapPath("~/UploadedDocuments"));
        if (!folderExists)
            Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/UploadedDocuments"));
        var fileSavePath = Path.Combine(HttpContext.Current.Server.MapPath("~/UploadedDocuments"),
                                        httpPostedFile.FileName);
        httpPostedFile.SaveAs(fileSavePath);

        if (File.Exists(fileSavePath))
        {
            //AppConfig is static class used as accessor for SFTP configurations from web.config
            using (SftpClient sftpClient = new SftpClient(AppConfig.SftpServerIp,
                                                         Convert.ToInt32(AppConfig.SftpServerPort),
                                                         AppConfig.SftpServerUserName,
                                                         AppConfig.SftpServerPassword))
            {
                sftpClient.Connect();
                if (!sftpClient.Exists(AppConfig.SftpPath + "UserID"))
                {
                    sftpClient.CreateDirectory(AppConfig.SftpPath + "UserID");
                }

                Stream fin = File.OpenRead(fileSavePath);
                sftpClient.UploadFile(fin, AppConfig.SftpPath + "/" + httpPostedFile.FileName,
                                      true);
                fin.Close();
                sftpClient.Disconnect();
            }
        }
    }
}

Points of Interest

1. extensionFilter is used to allow only pdf, doc, docx and rtf documents


2. File size limit is also implemented by allowing file upto 5 MB


3. Number of files allowed in upload queue is also implemented by filter.


4. In WebAPI, UploadedDocuments folder is created to get file into webserver from client.
5. Drag and drop is also implemented along with default browse button.

For the complete source code, please see https://github.com/m-hassan-tariq/SFTPFileUploadUsingWebAPIandAngularJS

Performance Enhancement in asp.net [Client Side]

Standard

Performance Enhancement

1. Minify Resources (HTML, CSS, and JavaScript):¨

Minification refers to the process of removing unnecessary or redundant data without affecting how the resource is processed by the browser – e.g. code comments and formatting, removing unused code, using shorter variable and function names, and so on.

2. Enable Compression:

¨All modern browsers support and automatically negotiate gzip compression for all HTTP requests. Enabling gzip compression can reduce the size of the transferred response by up to 90%, which can significantly reduce the amount of time to download the resource, reduce data usage for the client, and improve the time to first render of your pages.

Solution:

¨All excel templates/uploaded files etc in application

3.Optimize Images:

¨Images often account for most of the downloaded bytes on a page. As a result, optimizing images can often yield some of the largest byte savings and performance improvements: the fewer bytes the browser has to download, the less competition there is for the client’s bandwidth and the faster the browser can download and render content on the screen.

Solution

¨Sprites
¨Save Image as font [using @font-face]
¨Using SVG’s [bit controversial]

4.Optimize CSS Delivery:

¨Before the browser can render content it must process all the style and layout information for the current page. As a result, the browser will block rendering until external stylesheets are downloaded and processed, which may require multiple roundtrips and delay the time to first render.

¨If the external CSS resources are small, you can insert those directly into the HTML document, which is called inlining. Inlining small CSS in this way allows the browser to proceed with rendering the page.

¨If the amount of data required exceeds the initial congestion window, it will require additional round trips between your server and the user’s browser. For users on networks with high latencies such as mobile networks this can cause significant delays to page loading.

5. Reduce the size of the above-the-fold content:

Solution:

To make pages load faster, limit the size of the data (HTML markup, images, CSS, JavaScript) that is needed to render the above-the-fold content of your page. There are several ways to do this:

¨Structure your HTML to load the critical, above-the-fold content first

¨Reduce the amount of data used by your resources

6.Remove Render-Blocking JavaScript:

¨Before the browser can render a page it has to build the DOM tree by parsing the HTML markup. During this process, whenever the parser encounters a script it has to stop and execute it before it can continue parsing the HTML. In the case of an external script the parser is also forced to wait for the resource to download, which may incur one or more network roundtrips and delay the time to first render of the page.

Solution:
¨Inline JavaScript [if few lines of code]
¨<script async src=”my.js”>

7. Working with the DOM:

¨Working with the DOM can cause browser reflow, which is the browser’s process of determining how things should be displayed. Directly manipulating the DOM, changing CSS styles of elements, and resizing the browser window can all trigger a reflow. Accessing an element’s layout properties such as offsetHeight and offsetWidth can also trigger a reflow. Because each reflow takes time, the more we can minimise browser reflow, the faster our applications will be.

function addAnchor(parentElement, anchorText, anchorClass)

{

var element = document.createElement(‘a’); parentElement.appendChild(element);

element.innerHTML = anchorText;

element.className = anchorClass;

}

Solution

function addAnchor(parentElement, anchorText, anchorClass)

{

var element = document.createElement(‘a’);

element.innerHTML = anchorText;

element.className = anchorClass;

parentElement.appendChild(element);

}

8. Avoid Plugins:

¨Plugins help the browser process special types of web content, such as Flash, Silverlight, and Java. Most mobile devices do not support plugins, and plugins are a leading cause of hangs, crashes, and security incidents in browsers that provide support.

Solution:

¨Use HTML5
¨Removal of activeX control from application

 

Dev Tool: Client Side Performance Evaluation

Page Speed Insight [Extension by Google Chrome]
¨Minimise payload
¨Minimise delay in page load
¨Other

Notes prepared From Google Blog