Showing posts with label Razor. Show all posts
Showing posts with label Razor. Show all posts

Thursday, April 25, 2024

Html JS - Handle Browser Back Button on SPA

In Single page applications, we will not have dedicated URLs. So on click of Browser Back button it will navigate us to previous page and we lost our current page changes. We can handle this with native JavaScript functions,

Create Html Page as below to mimic SPA, 


On click of each Page button, it can switch the content,




On Each Page navigation we can set the hash URL something like below using history.pushstate or location.hash



Now on click of back button use the hashchange event to detect and open the page accordingly


Here is full code:

<html>

<head>

    <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>

</head>

<body>

    <input type="button" value="Page1" onclick="openPage('1');" />

    <input type="button" value="Page2" onclick="openPage('2');" />

    <input type="button" value="Page3" onclick="openPage('3');" />

 

    <div id="page1" class="page">Page 1 Content</div>

    <div id="page2" class="page">Page 2 Content</div>

    <div id="page3" class="page">Page 3 Content</div>

 

 

    <div id="history"></div>

</body>

 

<script>

    $(document).ready(function () {

        hideAllPages();

     });

 

    $(window).on('popstate', (e) => {

        e.preventDefault();

        var currentLocation = window.location.pathname;

        history.push(currentLocation)

    });

     window.onbeforeunload = function () {

        return "Are you sure you want to leave this page?";

     };

    addEventListener("hashchange", (event) => {

        alert("New" + event.newURL);

        alert("Old" + event.oldURL);

        var currentHash = event.currentTarget.location.hash;

        openPage(currentHash.replace("#", ""), true);

     });

    function hideAllPages() {

        $(".page").hide();

    }

    function openPage(pageId, ignoreHistoryPush = false) {

        hideAllPages();

        $("#page" + pageId).show();

        if (!ignoreHistoryPush)  //This is to not add history on everytime

            history.pushState({ page: pageId }, pageId, '#' + pageId);
            //
location.hash = pageId  //This way also can set it

    }

 

 

</script>

</html>


Thursday, March 31, 2016

MVC - Domain Route for Multi tenancy application to handle different URL

In some cases, we should handle Multi-tenant application with single code base and different domain URLs.

So how do we handle that in MVC Routing?

We have got "Constraints" concept in MVC routing, this is very helpful to filter URL based on domain also.

Okay, let us consider we have different URLs for same code base  for ex:  CompanyAName.com, CompanyBName.com. So here we have to handle two different domain URLs considering URLs already configured for same application.

routes.MapRoute(
    name: "CompanyARoute",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Index", action = "Home", id = UrlParameter.Optional },
    constraints: new { host = "CompanyAName.com" });


routes.MapRoute(
    name: "CompanyBRoute",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Index", action = "Home", id = UrlParameter.Optional },

    constraints: new { host = "CompanyBName.com" });



This code will handle two different domains, "host" in constraints is default syntax and will map to domain automatically. here we can change default Controller, Action to anything or even we can pass some parameters. 


Suppose, if we want to add some subdomain as companyname. For ex;   DefaultURL.com/CompanyName

We can use same "Constraints" to handle this

  routes.MapRoute(
    name: "SubRoute",
    url: "{companyName}/{controller}/{action}/{id}",
    defaults: new { controller = "Index", action = "Home", id = UrlParameter.Optional },
    constraints: new { companyName = "CompanyA" });


To handle multiple company names we can create more routing same like this or even in companyName we can add more like "(CompanyA|CompanyB)". "companyName" in constraints mapped to {companyName} in url.


We can utilize IRouteConstraint interface to extend each constraints attributes.

routes.MapRoute(
  name: "ConstraintRoute,
  url: "{controller}/{action}/{id}",
  defaults: new { controller = "Index", action = "Home", id = UrlParameter.Optional },
  constraints: new { host = new HostRouteConstraint("CompanyAName.com") });

"HostRouteConstraint" is a class extended from IRouteConstraint

using System.Web.Routing;
public class HostRouteConstraint : IRouteConstraint
    {
        private readonly string _host;

        public DomainConstraint(string host)
        {
            _host = host.ToLower();
        }

        public bool Match(HttpContextBase httpContext,
            System.Web.Routing.Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection)
        {
//Here you can do your custom logic to match host
            return httpContext.Request.Url.AbsoluteUri.ToLower().Contains(_host);
        }
    }


Tuesday, March 24, 2015

MVC - HTML5 Appcache for offline MVC Razor application

I would like to create application which support offline mode using HTML5 appcache. Before get into MVC offline mode, just read about HTML5 application cache 

Steps to create MVC HTML5 Offline application

1) Create new MVC Web project


2) For example purpose, i am using views created by default with MVC project. I am adding new action for Manifest file



2) On created Manifest.cshtml add code as required to do cache in browser



 - Here i removed layout 
 - Also set the content type as 'text/cache-manifest', so that browser will understand this file
 - CACHE MANIFEST is required syntax for manifest file
 - #version added with assembly file name and date. This will help to update cache automatically whenever new build happens
 - NETWORK is *
 - Under CACHE we can do whatever required. Here JS files, CSS files, Images, CSHTML pages, Other site resources added
 - FALLBACK added to logoff, if there is no connection for logoff then it will return error page instead
- Code
@{
    Layout = null;
}

@{
    HttpContext.Current.Response.ContentType = "text/cache-manifest";
}CACHE MANIFEST

    #version @System.Reflection.Assembly.GetExecutingAssembly().GetName().Version - @File.GetCreationTime(System.Reflection.Assembly.GetExecutingAssembly().Location).ToString("MM/DD/YYYY")

    NETWORK:
    *

    CACHE:
    # JS files
    @Url.Content("~/Scripts/jquery-1.10.2.min.js")

    # CSS files
    @Url.Content("~/Content/bootstrap.min.css")
    @Url.Content("~/Content/site.css")
   

    #Images
    @*@Url.Content("~/Content/images/logo.png")*@

    #CSHTMLPages
    @Url.Content("~/Home/Index")
    @Url.Content("~/Home/About")
    @Url.Content("~/Home/Contact")

    #Other Site Resources
    @Url.Content("http://fonts.googleapis.com/css?family=Roboto:00,400,700,300")

    FALLBACK:
    @Url.Content("~/Account/LogOff") @Url.Content("~/Errors/Default.html")




3) Include this manifest file path in _Layout.cshtml page html tag section


4) If you run application we will get error then we should know what is that



5) So we can add appcache.js file under script folder add following code to verify error 




- Code

var appCache = window.applicationCache;

appCache.addEventListener('cached', function (event) {
    console.log('All files downloaded!');
}, false);

// Checking for an update. Always the first event fired in the sequence.
appCache.addEventListener('checking', function (event) {
    console.log('Checking for manifest!');
}, false);

// An update was found. The browser is fetching resources.
appCache.addEventListener('downloading', function (event) {
    console.log('Downloading cache!');
}, false);

// The manifest returns 404 or 410, the download failed,
// or the manifest changed while the download was in progress.
appCache.addEventListener('error', function (event) {
    console.log('An error occurred!');
}, false);

// Fired after the first download of the manifest.
appCache.addEventListener('noupdate', function (event) {
    console.log('No cache updates!');
}, false);

// Fired if the manifest file returns a 404 or 410.
// This results in the application cache being deleted.
appCache.addEventListener('obsolete', function (event) {
    console.log('Manifest cannot be found!');
}, false);

// Fired for each resource listed in the manifest as it is being fetched.
appCache.addEventListener('progress', function (e) {
    console.log('File downloaded!');
}, false);


// Fired when the manifest resources have been newly redownloaded.
appCache.addEventListener('updateready', function (e) {
    console.log('New cache available!');
    //appCache.swapCache();
}, false);



6) Include appcache.js in _layout.cshtml


7) If we run application and check in console of browser, we can see logs of this appcache and thats it.