react-social-login

The easiest way to integrate Social Login in your React Apps ...Checkout NPM

Saturday, June 23, 2012

Managing raw HTML and Razor code in project


Intent of this post
=================
This article is about approach for structuring MVC application so as to manage front end  raw HTML design code along with developer's corresponding dynamic razor code


Background
==================
In general,  following is a flow of an idea to a live page (from design perspective):


Now here comes a question: 
"How should static HTML code and dynamic Razor code be managed in a solution?"


Answer...Depends.......!
Case-1: When developer is the same person who does both front end design and server code, there is no special arrangement required and one physical .cshtml file is enough. 


Case-2: When designers are smart enough to sneak into developer's razor code and implement any UI requirement, again a single file is sufficient.


Case-3: In bigger projects, where exists separate team for Design+HTML code and Razor+Backend code which is bridged by a Manager, there, it is really a good idea to have design HTML exists physically separate from file which a developer works on.


Picking up Case-3, an ideal structuring would  be one which satisfies following:



  1. Shouldn't be complex for designers to add their raw HTML files
  2. Dynamic Page URL and it corresponding raw HTML design URLs should be relative so that they can be easily toggled in browser
  3. Raw HTML code and dynamic code should be physically separate so that both has their own repository version
  4. Raw HTML code shouldn't go to production. Only its dynamic code variant should!



An Approach
=====================
Following is an approach that I've found useful:
1. Create a separate area for design files. 
2. Create a default controller action that returns view passed in as argument to action
3. Create a custom config entry (say "KeepDesignFiles") and remove physical files based on this parameters. With this, different environments can explicitly specify need to have (or not have) design files.


Illustration
====================
As a example, let's say business people think of 2 live pages
1. http://www.mysite.com/theDate, which would displays current date
2. http://www.mysite.com/theDate/Tomorrow, which would display next day date


Then, as per our approach, the static design URLs should be


1. http://www.mysite.com/design/theDate, which would displays current date
2. http:// www.mysite.com /design/theDate/Tomorrow, which would display next date


Let's build it up!



1. Ctrl+Shift+N and create a new ASP.NET MVC 3 Web Application (I chose DesignManagementDemo as application name)
2. Select Empty Template and choose Razor as View Engine
Visual Studio will create folder structure but without any controller/view file
3. Right-Click project and select Add>Area. Give area "Design" as name


So far we've just added a new project and added a area called "Design". Now comes some customization.
4. Right Click Design>Contoller folder and add a new Controller (call HomeController)
5. Modify default Index action method as following:



public ActionResult Index(string viewName)
        {
            return View(viewName);
        }


Notes: We do not want to create separate action for each new view. Rather this scheme makes design framework generic and all what designer needs to do is add views file and need not worry about controllers/actions internals.

4. Right-Click Views folder and add a new folder called "Home"

5. Add a new View to this new folder called "TheDate"
Steps 4,5 create a new file Index.cshtml at Areas>Design>Views>Home>TheDate.cshtml

6. Put some content in this file. Like: This is a static page 20-June-2012

Imagine this is static HTML added by designer.
Compile and run your project. The page you've just added can be browsed using:
http://localhost:58429/design/home?viewname=thedate (Port number will likely differ)
Now, that's generic. But let's improve a bit by removing the need to specify viewname like this. 

7. Open Areas>Design>DesignAreaRegistration.cs and alter code as below:




 public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Design_default",
                "Design/{viewName}",
                new { controller="Home", action = "Index", viewName = "TheDate" }
            );
        }



Notes: We have hidden the need to explicitly set Home/Index in URL and set them as default within route definition. Now the same URL can be accessed as: 
http://localhost:58429/design/TheDate (exactly what we wanted)


At this point take a break and think on what if this solution is sent to a designer with only one instruction "Add all design files inside Areas>Design>Home by adding a view".

Let's see how does that please a designer?


Imagine business want a new URL http://localhost:58429/design/TheYear (which eventually has to display  current year). All designer need to do is:
a. Right Click on Home Folder
b. Add View called "TheYear"
c. Write their static HTML content. That's it!

Continuing with our example, where we need another URL to display next date, designer would follow following steps:
a. Right Click Home folder
b. Add a folder TheDate
c. Add a view called "TomorrowsDate"
d. Write static HTML content. Thats it!


It's now so easy for designer to add their design files with static HTML without worrying about internals and moreover the URLs go so well with actual need by just prefixing "Design".


Developers can now create their dynamic views by copying content from static HTML files and replacing them with dynamic code.


Above arrangement, helps us meet 3/4 objectives:

  • Shouldn't be complex for designers to add their raw HTML files
  • Dynamic Page URL and it corresponding raw HTML design URLs should be relative so that they can be easily toggled in browser
  • Raw HTML code and dynamic code should be physically separate so that both has their own repository version



We've one more objective to meet: Raw HTML code shouldn't go to production. Only its dynamic code variant should!


In a nutshell, I would create a custom config key in Web.Config called "KeepDesignFiles" with value true/false. If false, application will physically delete the Design folder otherwise do nothing. So in production all I need to do is just set this property as true.


For an illustration, In your web.config add:

<appSettings>
    ...
    <add key="KeepDesignFiles" value="false" />
appSettings>



We'll use PreApplicationStartMethod to read this config and delete design folder even before Application_Start is called.
In AssemblyInfo.cs 



[assemblyPreApplicationStartMethod(typeof(DesignManagementDemo.PublishDesignFilesConfiguration), "Start")]

In this code, we tell .NET to call Start method of file called PublishDesignFileCOnfiguration


Code of class and method registered in AssemblyInfo.cs:



using System.Configuration;
using System.Linq;
using System.IO;
using System.Reflection;
namespace DesignManagementDemo
{
    public static  class PublishDesignFilesConfiguration
    {
        public  static void  Start()
        {
            var appsettings = ConfigurationManager.AppSettings["KeepDesignFiles"];
            bool keepDesignFiles = bool.Parse(appsettings);
 
            if (!keepDesignFiles)
            {
                var dir =
                    Directory.GetParent(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///""")).Parent.
                        GetDirectories("Areas/Design");
                if(dir.Any())
                    dir[0].Delete(true);
            }
        }
    }
}



Above code is called automatically once even before application_start event is called. It reads config and sees if KeepDesignFiles is true/false? If false, it deletes the Design folder. I find this approach better as you can always put more customized logic and configuration keys for this purpose and the best thing is application itself tells whether it should or shouldn't have design. Above code is good for local test but definitely you would write better code in production :)


Hope this post is of help. I encourage to post any different approach as there is always scope of improvement to everything.


 Cheers!