Friday, August 7, 2015

Office Javascript API and Angular JS HTML5 hash tag Url issue

I have faced very new and tricky issue with AngularJS and Office.js,

My app was working fine in HTML5Mode (i.e url without #) and all url navigated properly when only AngularJS JS file included.

But if i include Office App JavaScript API (aka Office AddIns) JS file (office.js), HTML5Mode stops working and Url shows with # tags. Means app worked without HTML5 History API, i found this using $sniffer.history in AngularJS. This returns as false.

Following two line of code made this problem, so to avoid this issue comment following lines in office.debug.js or office.js file

window.history.replaceState = null;
window.history.pushState = null;

Monday, July 20, 2015

JavaScript - Pinch and Zoom using Hammer JS

How to zoom content and not header or footer. Follow as below

Its simple with Hammer JS, download from here http://hammerjs.github.io/  or from Nuget package.

HTML Page:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
...
<script src="~/Scripts/hammer.min.js"></script>
...
<div id="someContainer">
..Some Content goes here
</div>

<script type="text/javascript">
    $(document).ready(function () {
        //Regisister here
        var element = $('#someContainer');
        contentPinchIntializer(element[0]);
    });

</script>


JavaScript to Pinch and Zoom content:
- Here i created max scale as 5, you can customize it
- Here i added Margin left and Margin Right to avoid space issue while on zooming, you can adjust the pixels however you want
- Added touch-action to enable horizontal scrolling

var contentPinchIntializer = function (element) {
    var scale = 1;
    var currentScale = null;
    var currentMarginLeft = null;
    var currentMarginRight = null;

    var pinchInHanlder = function (e) {
        scale = event.gesture.scale == null ? scale : event.gesture.scale;
        currentScale = currentScale == null ? scale : currentScale;
        currentMarginLeft = currentMarginLeft == null ? $(this).css("margin-left") : currentMarginLeft;
        currentMarginRight = currentMarginRight == null ? $(this).css("margin-right") : currentMarginRight;

        if (scale < currentScale) {
            scale = currentScale + 0.1;
            $(this).css({ "margin-left": currentMarginLeft, "margin-right": currentMarginRight });
        }

        scale = ('scale(' + (scale - 0.1).toString() + ')');

        $(this).css({ 'transform': scale, '-ms-transform': scale, '-webkit-transform': scale });
    };

    var pinchOutHandler = function (e) {
        scale = event.gesture.scale == null ? scale : event.gesture.scale;
        currentScale = currentScale == null ? scale : currentScale;
        currentMarginLeft = currentMarginLeft == null ? $(this).css("margin-left") : currentMarginLeft;
        currentMarginRight = currentMarginRight == null ? $(this).css("margin-right") : currentMarginRight;

        if (scale <= 5) {
            $(this).css({ "margin-left": "200px", "margin-right": "100px" });
            scale = ('scale(' + (scale + 0.1).toString() + ')');
            $(this).css({ 'transform': scale, '-ms-transform': scale, '-webkit-transform': scale });
        }
    };

    var hammertime = Hammer(element).on("pinchin", pinchInHanlder).on("pinchout", pinchOutHandler);

    $(element).css("touch-action", "manipulation");


}

Friday, May 15, 2015

MVC - Cache dynamic files in browser based on query string from action results

To cache we can do many things such as StaticCache in webconfig, HTML5 manifest cache and also we can use OutputCache in MVC.

To enable cache for dynamically changing contents. For ex, to load images based on file name and to cache use following attribute in controller or action.


        [HttpGet]
        [OutputCache(Duration = 600, VaryByParam = "imageFileName")]
        public FileResult GetSomeImageFile(string imageFileName)
        {
//your logic to load image file

 }

Here Duration mentioned in seconds and VaryByParam property to specify to cache based on parameter name 'imageFileName'

Saturday, April 25, 2015

MVC & WebAPI - Model Binder

There are some specific cases in order to use model binder to MVC and WebAPI in same project.

Here i will explain step by step to implement MVC and WebAPI Model Binders

What is Model Binder?

In some cases, we should do common operations for all actions inside controller. So in that case we can add model binder and pass as parameter to actions. In below example UserModel class sent as Model Binder

public ActionResult Index([ModelBinder]UserModel model)

{
...
}

Implementing Model Binder for MVC

MVC uses different namespace such as System.Web.Mvc. So Model Binder class also should use the same.

Step 1:

Create UserModel class as required. Here we can add our required properties. In this example, i am going to display UserId and UserName

public class UserModel
    {
        public string UserId { get; set; }
        public string UserName { get; set; }

    }

Step 2:

Create custom ModelBinder class which can be implemented from System.Web.Mvc.IModelBinder as below. Here i created as partial for merging with WebAPI. If you dont need WebAPI then just have it as normal class


public partial class UserModelBinder : System.Web.Mvc.IModelBinder
    {
        public object BindModel(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(UserModel))
            {
                return null;
            }

            UserModel userModel = new UserModel();

            var userId = HttpContext.Current.User.Identity.GetUserId();

            if (!string.IsNullOrEmpty(userId))
            {
/// Create your custom logics to fill the other required details. In this example i can fill username
            }
            return userModel;
        }
    }

Step 3:

Register this Custom ModelBinder class into Global.asax.cs file

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Model binder for MVC controller usermodel
            ModelBinders.Binders.Add(typeof(UserModel), new UserModelBinder());

        }

Step 4:

Use this custom model binder in action methods of MVC controller

public ActionResult Index([System.Web.Http.ModelBinding.ModelBinder]UserModel model)

{
...
 //Write your logic to do with UserModel class object
}


Thats all for MVC Model Binder.


Implementing Model Binder for WebAPI

WebAPI cannot be worked with MVC model binder. So we need to create differently. Step 1 to create UserModel is same. So i will go from Step 2

Step 2:

Create Custom Model Binder for WebAPI as follows. Here IModelBinder uses System.Web.Http.ModelBinding.

using Microsoft.AspNet.Identity;
using System.Web;
using System.Web.Http;
using System.Web.Http.ModelBinding;

public partial class UserModelBinder : IModelBinder
    {
        public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(UserModel))
            {
                return false;
            }

            UserModel userModel = new UserModel();

            var userId = HttpContext.Current.User.Identity.GetUserId();

            if (!string.IsNullOrEmpty(userId))
            {
               //Your logic to fill usermodel class and assign to binding context’s model
                bindingContext.Model = userModel;
                return true;
            }

            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Error in model binding");
            return false;
        }

    }


Step 3:

Create ModelBinderProvider class as follows. It is going to be register with configuration. Here ModelBinderProvider uses namespace System.Web.Http.ModelBinding..

public class UserModelBinderProvider : ModelBinderProvider
    {
        private System.Type type;
        private UserModelBinder userModelBinder;

        public UserModelBinderProvider(System.Type type, UserModelBinder userModelBinder)
        {
            this.type = type;
            this.userModelBinder = userModelBinder;
        }

        public override IModelBinder GetBinder(HttpConfiguration configuration, System.Type modelType)
        {
            if (modelType == type)
            {
                return userModelBinder;
            }

            return null;
        }

    }


Step 4:

Register ModelBinderProvider in Global.asax.cs file under WebApiConfig register method

public static class WebApiConfig

    {
    public static void Register(HttpConfiguration config)
        {
         var provider = new UserModelBinderProvider(typeof(UserModel), new UserModelBinder());

         config.Services.Insert(typeof(ModelBinderProvider), 0, provider);


Step 5:

Use in APIController action methods as follows

[HttpGet]
        public string GetUserName([ModelBinder]UserModel model)
        {
              return model.UserName;
  }

Thats all for WebAPI Model Binder.


Wednesday, April 22, 2015

JavaScript - Validate Multiple Email Address

To validate multiple email address text use below JavaScript Function

Valid :
"balajisrmv@gmail.com"


function isValidEmailAddress(emailAddresses) {
    var splitter = ' ';
    var isValid = true;
    if (emailAddresses.indexOf(',') != -1)
        splitter = ',';
    else if (emailAddresses.indexOf(';') != -1)
        splitter = ';';

    var emailCollection = emailAddresses.split(splitter);

    if (emailCollection != null && emailCollection.length > 0) {
        emailCollection.forEach(function (emailAddress, index) {
            var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
            if (isValid)
                isValid = pattern.test(emailAddress.trim());
        });
    }
    return isValid;

};