Decrease Font Size
Increase Font Size
   BLOG

Asp.net MVC Warning Banner using Bootstrap and AngularUI Bootstrap

MVC Warning Banner using Bootstrap and AngularUI Bootstrap

Table of Contents

Introduction

In this article, the author will share on how to implement the warning banner and reusable Modal using ASP.NET MVC, jQuery, Web API, Bootstrap, AngularJS and AngularUI Bootstrap. Some people refer warning banner as disclaimer banner, consent screen, splash screen, system acceptable use policy, log-on warning banner, login policy, Logon Banner, Logon Notice, etc... Basically, this is the page/notice that appear/display when the user first visit or logon into the application. Here is the summary on what the reader can extract from this article.

  • How to create warning banner using jQuery + Bootstrap
  • How to create warning banner using AngularJS + AngularUI Bootstrap
  • How to create a simple Web API
  • How to call Web API from MVC Controller
  • How to include anti-forgery token into the AJAX POST Request
  • How to create reusable Modal using MVC partial view and jQuery + Bootstrap
  • How to create reusable Modal using AngularJS Template + AngularUI Bootstrap
  • How to create an Authorization filter

Figure 1 shows the Solution contains two project namely CommonApp and WarningBanner respectively. The CommonApp is a simple WebAPI project responsible to return the warning banner content.

Figure 1

project structure

Warning Banner View

Listing 1 shows the Bootstrap modal markup in the WarningPage view. In this example, the content for the modal-body will be read from the ViewBag. You can change it to bind from the Model or hard-code the banner content onto the page depending on your requirement. The data-keyboard is set to "false" to prevent the user from closing the warning banner by hitting the ESC key on the keyboard.

Listing 1

    @{ Html.RenderPartial("~/Views/Shared/_AntiforgeryToken.cshtml"); }
<div class="modal fade" id="myModal" role="dialog" data-keyboard="false" data-backdrop="static">
        <div class="modal-dialog">
        <div class="modal-content">
        <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
                </div>
            @if (ViewBag.SystemWarningMessage != null)
            {
        <div class="modal-body"> @Html.Raw(ViewBag.SystemWarningMessage)</div>
            }
        <div class="modal-footer">
        <button type="button" class="btn btn-default" id="btnAcceptPolicy" data-dismiss="modal">OK</button>
                </div>
            </div>
        </div>
    </div>

Warning Banner View Action

On page load, the WarningPage action method will check if the user clicked on the OK button on the warning banner page. If yes, redirect the request to Home page else get the warning banner message and display it on the page.

Listing 2

    public ActionResult WarningPage()
        {
            if (Session["SessionOKClick"] != null)
            {
                return RedirectToAction("Index");
            }
            SetWarningBanner();
            return View();
        }

Shown in listing 3 is the code to set the warning banner content. In this example, the method is getting the content from the Web API through the HttpClient class. The code is very straight forward, however, we can make a few improvements here. The code can be modified to read the Web API URI from a configuration file and sanitize the JSON result before return it to the client. If Web API is not the preferred choice, then we can modify the code to read the content from the database/entity, configuration file or even hard-coded it. The purpose of the cache-control headers in this method is to prevent the browser back button from serving the content from the cache. For instance, if the user click on the browser back button after accepted the policy, the Warning Banner Modal will re-appear and prompt the user to accept the policy again.

Listing 3

internal void SetWarningBanner()
{
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
    Response.Cache.SetNoStore();
    //Content can be from the database/Entity, Web Services, hardcode here
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost:47503");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var response = client.GetAsync("api/warningbanner/1").Result;
        if (response.IsSuccessStatusCode)
        {
            string responseString = response.Content.ReadAsStringAsync().Result;
            JObject json = JObject.Parse(responseString);
		//you can update the code to sanitize the json output here
            ViewBag.SystemWarningMessage = json["Content"].ToString();
        }
    }
}

Warning Banner Web API

Listing 4 shows the contents of the Warning Banner web API controller. This class contains a WarningBanner object with couple of sample data in it and a Get method to retrieve the WarningBanner by id. Again, feel free to modify this code to read the data from the database/entity, configuration file and etc.

Listing 4

public class WarningBannerController : ApiController
{
//sample data 
IList<WarningBanner> warningBanners = new List<WarningBanner>
    {
        new WarningBanner { Id = 1, Content = "<b> *** Warning Banner Content 1 ****</b>",
            LastUpdatedDate = DateTime.Now},
        new WarningBanner { Id = 2, Content = "<b> *** Warning Banner Content 2 ****</b>",               
            LastUpdatedDate= DateTime.Now.AddDays(-30)}
    };
    public IHttpActionResult Get(int id)
    {
        var banner = warningBanners.FirstOrDefault((p) => p.Id == id);
        if (banner == null)
        {
            return NotFound();
        }
        return Ok(banner);
    }
}

Warning Banner View OK button click

Listing 5 shows the Ajax call will be triggered to post to Home/AcceptPolicy method on Ok button click. In this example, the post will include an anti-forgery token and a key value pair data. if the request succeeds, the request will be redirected to the page that it initially being requested.

Listing 5

<script type="text/javascript">
    $(window).load(function () {
        $('#myModal').modal('show');
    });
    jQuery(document).ready(function () {
        jQuery("#btnAcceptPolicy").click(function () {
            jQuery.ajax({
                type: "POST",
                url: '@Url.Content("~/Home/AcceptPolicy")',
                data: { rURL: 'someDummyValue' },
                beforeSend: function (xhr) { xhr.setRequestHeader('RequestVerificationToken', $("#antiForgeryToken").val()); },
                success: function (data) {
                    window.location.href = data;
                }
            });
        });
    });
</script>

Warning Banner OK button Action method

The AcceptPolicy action method is being decorated with HttpPost and AjaxValidateAntiForgeryToken attributes. The main purpose of this method is to tell the client what URL to redirect the request to, after user click on the Ok button. Refer to listing 6. You can be creative here also by adding additional logic to log the user response into the database. The method accepts one parameter, rURL, that variable is not being used by the code. The goal is to demonstrate that the AJAX post can pass a parameter to the action method. The purpose of SessionOKClick session variable is to keep track if user clicked on the Ok button. On the other hand, SessionReturnUrl session variable contain the URL of the Initial page request. The URL is being set at AuthorizeAttribute.OnAuthorization method which will be discussed in next section.

Listing 6

[HttpPost]
[AjaxValidateAntiForgeryToken]
public ActionResult AcceptPolicy(string rURL)
{
    Session["SessionOKClick"] = "1";
    string decodedUrl = string.Empty;
    //return url to redirect after user accepted the policy
    if (Session["SessionReturnUrl"] != null)
    {
        decodedUrl = Server.UrlDecode(Session["SessionReturnUrl"].ToString());
    }
    if (Url.IsLocalUrl(decodedUrl))
    {
        return Json(decodedUrl);
    }
    else
    {
        return Json(Url.Action("Index", "Home"));
    }
}

RequireAcceptPolicy - Custom Authorization filter attribute

Listing 7 shows the custom authorization filter to check if the request need to be redirected to the warning banner page before the action methods gets invoked by the authorized users. In this example, the OnAuthorization method contains only the logic to handle the warning banner policy. In reality, your application might have other logic to validate authorized user. We can append/merge the code in Listing 7 into your application authorization filter class. The PageToSkipPolicyNotification and PageToShowPolicyNotification methods allow to specify certain page to skip or show the warning banner page respectively. The OnAuthorization method will redirect the request to Warning page if

  • The action is not from Ok button click (AcceptPolicy) and
  • The user must accept the policy before viewing the requested page and
  • The user has not click on the Ok button (AcceptPolicy) before

Recall earlier, the SessionOKClick session variable is being set at WarningPage AcceptPolicy Action method. The purpose of the SessionReturnUrl session variable is to hold the URL of the request. We can’t expect all the users to enter the web application through the /home page, some might enter from /home/contact page, /home/about page, and etc. Let said the user initially enter the web application from /home/contact page. After the user click on the Ok button (AcceptPolicy) on the warning page, the request will be redirected to the /home/contact page. If you want to see the warning banner with AngularJS, in the appSettings element of the web.config, set the WaningBannerAngular value to 1.

Listing 7

public class RequireAcceptPolicy : AuthorizeAttribute, IAuthorizationFilter
{
    internal static bool PageToSkipPolicyNotification(HttpContext ctx)
    {
        string[] pagesToExclude = { "/home/testwarningpage", "/home/warningpage"};
        string pageToCheck = ctx.Request.Path.ToString().ToLower().TrimEnd('/');
        return pagesToExclude.Contains(pageToCheck) ? true : false;
    }
    internal static bool PageToShowPolicyNotification(HttpContext ctx){ ….}
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        //Other Authorization logic ….
	        ….
        //don't prompt if the action is from acceptpolicy, and pages to exclude and SessionOKClick != null (user already accepted policy)
        if (!filterContext.ActionDescriptor.ActionName.ToLower().Contains("acceptpolicy") && !PageToSkipPolicyNotification(HttpContext.Current) &&
                            HttpContext.Current.Session["SessionOKClick"] == null)
        {
            //track the request url, include the query string
            HttpContext.Current.Session["SessionReturnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery;
            //redirect to policy page
            if (System.Configuration.ConfigurationManager.AppSettings["WaningBannerAngular"] == "1")
            {
                filterContext.Result = new RedirectResult("~/home/WarningPageNg");
            }
            else
            {
                filterContext.Result = new RedirectResult("~/home/WarningPage");
            }
        }
    }
}

Filter Config

Listing 8 shows how to add the Custom Authorization filter attribute, RequireAcceptPolicy to the global filter collection.

Listing 8

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new RequireAcceptPolicy());
    }
}

Ajax Validate Anti-Forgery Token

The Anti-Forgery Token code is from Passing Anti-Forgery Token to MVC Actions using Ajax Post. Instead of placing the client related code on every single page, the code is placed into a partial view, _AntiforgeryToken.cshtml. You can place the partial view that require Anti-Forgery Token into a single page or Layout page. Shown in listing 9 is the contents of _AntiforgeryToken.cshtml. The partial view comprises of two hidden fields with the ID antiForgeryToken and antiForgeryTokenNg respectively. The hidden field hold the Anti-Forgery Token generated by the GetAntiForgeryToken method. The antiForgeryTokenNg hidden field is utilized by AngularJS.

Listing 9

@functions{
    public string GetAntiForgeryToken()
    {
        string cookieToken, formToken;
        AntiForgery.GetTokens(null, out cookieToken, out formToken);
        return cookieToken + "," + formToken;
    }
}
<input type="hidden" id="antiForgeryToken" value="@GetAntiForgeryToken()" />
<input id="antiForgeryTokenNg" data-ng-model="antiForgeryToken" type="hidden"
    data-ng-init="antiForgeryToken='@GetAntiForgeryToken()'" />

Listing 10 shows how the jQuery AJAX post passing the Anti-Forgery Token to the Controller Action method. You can see the complete JavaScript from listing 5.

Listing 10

    beforeSend: function (xhr) { xhr.setRequestHeader('RequestVerificationToken', $("#antiForgeryToken").val()); }

Listing 11 briefly shows how the AngularJS passing the Anti-Forgery Token to the Controller Action method. Please download the source code because I might skip something in this section. On page load the Controller will read the token from the hidden field and then store it into an array variables namely items. When the users click on the Accept Policy/OK button, the AngularJS $http service will post to the server including the headers property. In this example the header property contain RequestVerificationToken parameter and the value from $scope.items[0]. The array in this example happen to has only one item that why the position is 0.

Listing 11

// modal controller
$scope.items = [$scope.antiForgeryToken];
// OK click
$http({
    method: 'POST',
    url: '/home/AcceptPolicy',
    params: Indata,
    headers: {
        'RequestVerificationToken': $scope.items[0]
    }
…

Warning Banner View using AngularJS

Listing 12 shows the AngulatJS and AngularUI Bootstrap modal markup in the WarningPageNg view. Like the WarningPage view, the content for the modal-body is from the ViewBag. You can change it to bind from the Model or hard-code the banner content onto the page depending on your requirement. The contents of the modal are encapsulated in the template under the script directive. The OK button click is routed to the ok() function and openModal() function will be call on page load.

Listing 12

<div ng-controller="modalcontroller">
    <script type="text/ng-template" id="myModal.html">
    <div class="modal-header"></div>
    <div class="modal-body">
        @if (ViewBag.SystemWarningMessage != null)
        {
            <div class="modal-body"> @Html.Raw(ViewBag.SystemWarningMessage)</div>
        }
    </div>
    <div class="modal-footer">
        <button class="btn btn-default" type="button" ng-click="ok()">OK</button>
    </div>
    </script>
    @{ Html.RenderPartial("~/Views/Shared/_AntiforgeryToken.cshtml"); }
    <span ng-init="openModal()"></span>
</div>
@section scripts{
    <script src="~/ControllersNg/ModalController.js"></script>
}

Shows in listing 13 is the contents of ModalController.js file. Initially, the code will create a new module namely TestAngularApp and declare a dependency on the ui.bootstrap module. Then the code will utilize the Config function to inject $httpProvider provider into the module configuration block in the application. The provider will allow the module to add headers of "X-Requested-With" type to the performed calls. This header is necessary to work well with the AjaxValidateAntiForgeryToken attribute and solve the error "required anti-forgery cookie \"__RequestVerificationToken\" is not present." You can see a list of AngularUI Modal open properties and definition here. The OK button click logics are very similar to listing 5. On button click, the page will post to home/AcceptPolicy method with Anti-Forgery token.

Listing 13

    var app = angular.module('TestAngularApp', ['ui.bootstrap']);
app.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
}]);
app.controller('modalcontroller', function ($scope, $uibModal) {
    $scope.animationsEnabled = true;
    $scope.openModal = function (size) {
        $scope.items = [$scope.antiForgeryToken];
        var ModalInstance = $uibModal.open({
            animation: $scope.animationsEnabled,
            templateUrl: 'myModal.html',
            controller: 'InstanceController',
            backdrop: false,
            resolve: {
                items: function () { return $scope.items; }
            }
        });
    };
});
app.controller('InstanceController', function ($scope, $uibModalInstance, $http, $window, items) {
    $scope.items = items;
    $scope.ok = function () {     
        var Indata = { rURL: 'dummyTestParam' };
        $http({
            method: 'POST',
            url: '/home/AcceptPolicy',
            params: Indata,
            headers: {'RequestVerificationToken': $scope.items[0] }
        }).then(function successCallback(response) {
            $scope.status = response.status;
            $scope.data = response.data;
            $window.location.href = response.data;
        }, function errorCallback(response) {
        });
        $uibModalInstance.close();
    };
    $scope.cancel = function () {
        //it dismiss the modal 
        $uibModalInstance.dismiss('cancel');
    };
});

Reusable Modal using jQuery and Bootstrap

Shown in listing 14 is the HTML markup of the _ModalDialog partial view. In this view, there are placeholders for the Modal dialog title and body and several buttons.

Listing 14

<div class="modal fade" id="SharedModalDialog" role="dialog" data-keyboard="false" data-backdrop="static">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal">×</button>
                <h4 class="modal-title">[Title]</h4>
            </div>
            <div class="modal-body">[Body] </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" 
                        id="SharedModalDialogBtnOk" data-dismiss="modal">OK</button>
                <button type="button" class="btn btn-default" 
                        id="SharedModalDialogBtnCancel" data-dismiss="modal">Cancel</button>
            <div>
        </div>
    </div>
</div>

Listing 15 shows the jQuery plugin namely ModalDialog to display the dialog in the _ModalDialog partial view. The plugin accepts five parameters, you can customize it to fit your requirement. The title and body parameters corresponding to the dialog title and body content. The OkButtonText and CancelButtonText parameters will determine the label and the visibility of the OK and Cancel buttons respectively. If OkClickRedirect value is specified, the request will be redirected to the specified URL on OK button click.

Listing 15

$(document).ready(function () {
    (function ($) {
        // jQuery plugin definition
        $.fn.ModalDialog = function (params) {
            // merge default and user parameters
            params = $.extend({ title: '', body: '', OkButtonText: '', OkClickRedirect: '', CancelButtonText: '' }, params);
            if (params.title != '') {
                $(".modal-title").text(params.title);
            }
            if (params.title != '') {
                $(".modal-body").html(params.body);
            }
            if (params.OkButtonText != '') {
                $("#SharedModalDialogBtnOk").text(params.OkButtonText);
                $("#SharedModalDialogBtnOk").show();
            }
            else {
                $("#SharedModalDialogBtnOk").hide();
            }
            if (params.CancelButtonText != '') {
                $("#SharedModalDialogBtnCancel").text(params.CancelButtonText);
                $("#SharedModalDialogBtnCancel").show();
            }
            else {
                $("#SharedModalDialogBtnCancel").hide();
            }
            if (params.OkClickRedirect != '') {
                $("#SharedModalDialogBtnOk").click(function () {
                    window.location.replace(params.OkClickRedirect);
                });
            }
            $('#SharedModalDialog').modal('show');
            // allow jQuery chaining
            return false;
        };
    })(jQuery);
});

Listing 16 shows how to utilize the newly created partial view and jQuery plugin to display a modal dialog. The RenderPartial method can be place into the Layout page. This will be very useful if the application needs to display a simple alert message / notification to the client throughout the application. There are three buttons and example on how to use the plugins in listing 16.

Listing 16

<input type="button" id="btn1" value="Test Bootstrap Modal - OK button/Redirect" />
<input type="button" id="btn2" value="Test Bootstrap Modal - multiple button" />
<input type="button" id="btn3" value="Test Bootstrap Modal - no button" />
@section scripts {
    @{
        Html.RenderPartial("~/Views/Shared/_ModalDialog.cshtml");
    }
    <script type="text/javascript">
        $("#btn1").click(function () {
            $('#SharedModalDialog').ModalDialog({
                title: 'Test - modal',
                body: '<b>Do you agree with the term?</b>',
                OkButtonText: 'Accept',
                OkClickRedirect: '/Home/Index/?test=1'
            });
        });
        $("#btn3").click(function () {
            $('#SharedModalDialog').ModalDialog({
                title: 'Test - modal 2 ',
                body: 'This is ANOTHER test <b>content 2222 </b> more <br/> contents .....'
            });
        });
        $("#btn2").click(function () {
            $('#SharedModalDialog').ModalDialog({
                title: 'Test Bootstrap Dialog - multiple button',
                body: 'Are you sure you want to delete this record?',
                CancelButtonText: 'Cancel',
                OkButtonText: 'Yes',
                OkClickRedirect: '/Home/Index/?test=2'
            });
        });
    </script>
}

Listing 17 shows how to display the dialog on page load or without a button click event.

Listing 17

$(document).ready(function () {
    $('#SharedModalDialog').ModalDialog({
        title: 'Test - Page Load',
        body: 'Display on page load !!!!'
    });
});

Reusable Modal using AngularJS and AngularUI Bootstrap

Shown in listing 18 is the modal dialog template in the _ModalDialogNg.html file. The buttons visibility is based on the expression in the ngShow attribute, namely showX, showOK and showCancel respectively. The title and buttons label are accessible through the properties in the scope class. AngularJS ng-bind-html directive is being utilized to display the HTML contents securely in the div element. But still, make sure to sanitize the user-input before displaying it.

Listing 18

<div class="modal-header">
    <button type="button" class="close" ng-show="showX" ng-click="cancel()">×</button>
    <h3 class="modal-title">{{title}}</h3>
</div>
<div class="modal-body">
    <div data-ng-bind-html="htmlBind"></div>
</div>
<div class="modal-footer">
    <button class="btn btn-default" ng-show="showOK" type="button" ng-click="ok()">{{btnOkText}}</button>
    <button class="btn btn-default" ng-show="showCancel" type="button" ng-click="cancel()">{{btnCancelText}}</button>
</div>

Listing 19 shows the contents of the ModalPartialController.js file. AngularJS $sce (Strict Contextual Escaping) service is being use in the controller to build the trusted version of the HTML values. $uibModal is a service to create modal windows. The purpose of the $window service is to redirect the request to a particular URL on OK button click. The logic to hide or show a button and setting element labels and values are embedded in listing 19.

Listing 19

    var app = angular.module('TestAngularApp', ['ui.bootstrap']);
app.controller('modalcontroller', function ($scope, $uibModal, $window) {
    $scope.openModal = function (title, body, okBtnText, okClickRedirect, cancelBtnText) {
        $scope.items = [title, body, okBtnText, okClickRedirect, cancelBtnText];
        var ModalInstance = $uibModal.open({
            animation: true,
            templateUrl: '/TemplatesNg/_ModalDialogNg.html',
            controller: 'InstanceController',
            backdrop: false,  //disables modal closing by click on the background
            keyboard: true,     //disables modal closing by click on the ESC key
            resolve: {
                items: function () {
                    return $scope.items;
                }
            }
        });
        ModalInstance.result.then(function (btn) {
            if (btn == "OK") {
                $window.location.href = okClickRedirect;
            }
        }, function () { });
    };
});
app.controller('InstanceController', function ($scope, $uibModalInstance, $sce, items) {
    $scope.items = items;
    $scope.title = $scope.items[0];
    $scope.body = $scope.items[1];
    $scope.btnOkText = $scope.items[2];
    $scope.btnCancelText = $scope.items[4];
    var returnUrl = $scope.items[3];
    //allow html
    $scope.htmlBind = $sce.trustAsHtml($scope.items[1]);
    //hide or close the X close button on the top, you can write extra logic here to hide or show it
    $scope.showX = false;
    $scope.showOK = true;
    if ($scope.btnOkText == '') {
        //hide OK button
        $scope.showOK = false;
    }
    //cancel button
    $scope.showCancel = true;
    if ($scope.btnCancelText == '') {
        $scope.showCancel = false;
    }
    //OK clicked
    $scope.ok = function () {
        if (returnUrl == '') {
            $uibModalInstance.close('');
        } else {
            $uibModalInstance.close('OK');
        }
    };
    $scope.cancel = function () {
        //it dismiss the modal 
        $uibModalInstance.dismiss();
    };
});

Listing 20 shows how the application/view call the AngularUI Bootstrap Modal by passing in different parameters. First, include ModalPartialController.js, the AngularJS controllers from listing 19 on to the page. Then create a div element and include the ng-controller directive with controller class, modalcontroller, as its value. Add a button into the div element and on the ng-click event, specify the openModal method defined in the controller (ModalPartialController.js).

Listing 20

<div ng-controller="modalcontroller">
    <button class="btn btn-default" type="button"
            ng-click="openModal('AngularUI Model','Click on <b>Ok</b> to Redirect to Contact page.', 'OK', '/home/Contact', 'Close')">
                Modal with redirect</button>
</div>
<div ng-controller="modalcontroller">
    <button class="btn btn-default" type="button"
            ng-click="openModal('@ViewBag.Something','This is the content<b>body 2</b>', '','', 'Close It')"> 
                Modal with close button only</button>
</div>
<div ng-controller="modalcontroller">
    <button class="btn btn-default" type="button"
            ng-click="openModal('AngularUI Model 2','This is a notification!', '','', 'Ok')">
        Modal with OK button only
    </button>
</div>
@section scripts{
    <script src="~/ControllersNg/ModalPartialController.js"></script>
}

Listing 21 shows how to display the dialog on page load or without a button click event.

Listing 21

<div ng-controller="modalcontroller"
         ng-init="openModal('AngularUI Model Onload','Display on page load!', '','', 'Ok')">
</div>

Future Plan

Upgrade the code to user Angular 2 and above and MVC 6 core.

Conclusion

I hope someone will find this information useful and make your programming job easier. If you find any bugs or disagree with the contents or want to help improve this article, please drop me a line and I'll work with you to correct it. I would suggest downloading the demo and explore it in order to grasp the full concept because I might miss some important information in this article. Please send me an email if you want to help improve this article. Please use the following link to report any issues https://github.com/bryiantan/WarningBanner/issues.

Tested on: Internet Explorer 11,10,9.0, Microsoft Edge 38, Firefox 52, Google Chrome 56.0
IE, Firefox, Google Chrome, Safari

History

4/5/2017 – Initial version

Watch this script in action

http://mvc.ysatech.com/

Download

Download Source Code (https://github.com/bryiantan/WarningBanner )

Resources

A Beginner's Tutorial for Understanding Filters and Attributes in ASP.NET MVC
Calling Webapi 2 service from MVC 5 controller
How to implement Modal popup using Angular UI in ASP.NET MVC
ng-bind-html
Passing Anti-Forgery Token to MVC Actions using Ajax Post
Why ASP.NET MVC Request.IsAjaxRequest method returns false for $http calls from angular