Conditional Placeholders in Angular UI Masking

Standard

When we want to take a credit card or a phone number (or any other input that requires a specific format), we can present a clean UI that tells our users that they are giving us clean input.

We’ll need to ensure we include the mask.js library in our HTML

<script type=“text/javascript” src=“app/bower_components/angular-ui-utils/modules/mask/mask.js”></script>

And set the ui-mask as a dependency for our app:

angular.module(‘myApp’, [‘ui.mask’])

 

<input type="text" name="txtAccountNumber" numbers-only 
ui-mask="{{
bankName == 'Bank' ? '999999' :
(bankName == 'Bank2' ? '99-9999999-99' :
(bankName == 'Bank3' ? '9999999999' :
(bankName == 'Bank4' ? '9999-99999-999-999999' : '' ) ) )}}">

AngularJS directive to allow number only in textbox

Standard

Here I show how to use AngularJS directive to allow only numbers to be entered in a textbox. This is useful for phone numbers, IDs, ZipCodes, and, well, that’s about it.

Demo on Github

app.directive('numbersOnly', function () {
        return {
            require: 'ngModel',
            link: function (scope, element, attr, ngModelCtrl) {
                function fromUser(text) {
                    if (text) {
                        var transformedInput = text.replace(/[^0-9-]/g, '');
                        if (transformedInput !== text) {
                            ngModelCtrl.$setViewValue(transformedInput);
                            ngModelCtrl.$render();
                        }
                        return transformedInput;
                    }
                    return undefined;
                }
                ngModelCtrl.$parsers.push(fromUser);
            }
        };
    });

<input type="text" name="txtAccountNumber" numbers-only ng-model="account">

Update:

I have tested on chrome/firefox/IE 9+, It is working fine,  It doesn’t allow space even.
For all those have issues like not working in specific browser or space(‘ ‘) character is allowed etc, please make sure there are no integration issues by inspecting browser console window.
P.S. ng-model attribute is required to use that directive
<input type="text" name="txtAccountNumber" numbers-only ng-model="account">

AngularJS directive to set visibility of html element

Standard

The visibility property specifies whether an element’s content is visible or not. Its values can be visible, hidden, collapse, and inherit. The default value is inherit. If visibility is set to visible, the element displays normally. If visibility is set to hidden, the element’s content is hidden (but transparent), but the element still takes up the same location of its generated box. If visibility is set to collapse and the element is not a row or column, according to the CSS2 specification, collapse should have the same meaning as hidden. Firefox does this correctly, but IE6 and IE7 do not treat collapse as hidden.

app.directive('visible', function() {
        return {
            restrict: 'A',
            link: function(scope, element, attributes) {
                scope.$watch(attributes.visible, function(value) {
                    element.css('visibility', value ? 'visible' : 'hidden');
                });
            }
        };
    });

AngularJS state provider integration in project

Standard

URL routing is a popular approach to matching the contents of a URL to specific functionality within a web application. URL routes programatically present specific content to users based on the URL that they are visiting. It is a popular approach that has proven to be very effective.

Something that might not be obvious is that URL routing is also a finite state machine. When you configure the routing for an app, you are laying out the various states the application can be in, and informing the application what to display and do when a specific route is encountered.

AngularJS supplies URL routing by default. It is adequate, but also has some limitations.

app.config(['$stateProvider', function ($stateProvider) {
        $stateProvider.state('page1', {
            url: '/page1',
            templateUrl: 'Scripts/app/modules/views/page1.html',
            controller: 'page1Controller',
            controllerAs: 'vm'
        }).state('profile', {
            url: '/profile',
            templateUrl: 'Scripts/app/layout/views/profile-shell.html',
            abstract: true,
        }).state('page2', {
            url: '/page2',
            templateUrl: 'Scripts/app/modules/views/page2.html',
            controller: 'page2Controller',
            controllerAs: 'vm'
        }).state('page3', {
            url: '/page3',
            templateUrl: 'Scripts/app/modules/views/page3.html',
            controller: 'page3Controller',
            controllerAs: 'vm'
        }).state('page4', {
            url: '/page4',
            templateUrl: 'Scripts/app/modules/views/page4.html',
            controller: 'page4Controller',
            controllerAs: 'vm'
        }).state('page5', {
            url: '/page5',
            templateUrl: 'Scripts/app/modules/views/page5.html',
            controller: 'page5Controller',
            controllerAs: 'vm'
        });
    }]);
 
    app.run(['$state', function ($state, $rootScope, $templateCache) {
        $state.go('page1');
    }]);

 

Bootstrap Sticky Notifications AngularJS Service

Standard
Bootstrap 3.0 Sticky Alerts using Angular JS service

Introduction

Let’s face it, your users need sweet little notifications to keep them all warm and fuzzy inside. Below angualrJs code snippet allows you to send such messages with ease, and class. Quickly notify a user of software updates, process completions, or annoy them with registration reminders. This AngularJS directive which handles notifications that makes it easy to create alert – success – error – warning – information – confirmation messages as an alternative the standard alert dialogue. Each notification is added to a queue. (Optional). The notifications can be positioned anywhere with ease through CSS

Using the code

Html Code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/
     bootstrap.min.css" />
    <script src="Content/jquery-1.8.2.js"></script>
    <script src="Content/angular.js"></script>
    <script src="Content/angular-route.js"></script>
    <script src="Content/App/Controller.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
    <link href="Content/site.css" rel="stylesheet" />
</head>
<body ng-app="notifyApp">
    <div ng-controller="notifyController">
        <input type="button" ng-click="info()" value="info" />
        <input type="button" ng-click="success()" value="success" />
        <input type="button" ng-click="warning()" value="warning" />
        <input type="button" ng-click="error()" value="error" />
   
        <div>
            <div class="alerts" ng-shw="notify.queue.length > 0">
                <div class="alert alert-{{(m.type)||'info'}} alert-dismissable fade in pull-right"
                 ng-repeat="m in notify.queue">
                    <button type="button" class="close" ng-click="closeAlert(m.body)" 
                     data-dismiss="alert">&times;</button>
                    <label>{{m.title}}</label>
                    <div>{{m.body}}</div>
                </div>
            </div>
        </div>
    
    </div>
</body>
</html>

Controller Code:

(function () {
    //Module
    var sample = angular.module('notifyApp', ['ngRoute']);
    
    sample.service('notifications', ['$rootScope', function ($rootScope) {
            var queue = [];
            return {
                queue: queue,
                add: function (item) {
                    var index = -1;
                    //check alert with same body not active in dom
                    for (var i = 0; i < this.queue.length; i++) {
                        if (queue[i].body == item.body) {
                            index = i;
                            break;
                        }
                    }
                    if (index != -1)
                        return;
                    queue.push(item);
                    setTimeout(function () {
                        $('.alerts .alert').eq(0).remove();
                        queue.shift();
                    }, 3000);
                },
                pop: function (item) {
                    var index = -1;
                    //to find alert from queue of active alerts in dom
                    for (var i = 0; i < this.queue.length; i++) {
                        if (queue[i].body == item) {
                            index = i;
                            break;
                        }
                    }
                    if (index != -1)
                        queue.splice(index, 1);
                    return this.queue;
                }
            };
        }
    ]);
    
    //Controller
    sample.controller('notifyController', function ($scope, notifications) {
    
        $scope.notify = notifications;
        $scope.closeAlert = function (item) {
            notifications.pop(item);
        }
        
        $scope.info = function()
        {
            setNotification(notifications, 'info', 'Info Header', 'Info Body');
        }
        
        $scope.success = function()
        {
            setNotification(notifications, 'success', 'Success Header', 'Success Body');
        }
        
        $scope.warning = function()
        {
            setNotification(notifications, 'warning', 'Warning Header', 'Warning Body');
        }
        
        $scope.error = function()
        {
            setNotification(notifications, 'danger', 'Error Header', 'Error Body');
        }
    });
    
    function setNotification(notifications, type, title, body) {
        notifications.add({
            type: type,
            title: title,
            body: body
        });
    }
})();

 

Points of Interest

1. Same type of notifications is only active on screen once in case if multiple clicks.
2. Close button result in removal of notification from queue.
3. Timeout value can be increased or decreased as per need.

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

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