Create Dynamic View with static HTML in MVC - html

I have been researching dynamic content for MVC views and partial views but have not successfully found an architecture to fit my needs.
Basically I am required to create a landing page based on parameters pass by the URL.
For basics
http://mydns.com/myconroller/myview/?landingpage=Param1
The controller will need to find the HTML that will be used to create the view.
The view is going to be different based on the landing page.
(for the sake of the question, I am using landingpage as an example)
My goal is to be able to deploy a Landing page and based on the URL use that HTML Landing page in the view based on the landingpage parameter that is passed.
There are other views that are working currently in the controller. I am trying to add functionality to be able to add a new one time page without having to recompile.
I have searched through various ideas on how to load dynamic views but cannot seem to find a solution that fits this need based on what I have read.
I can possibly RedirectToAction but I am still in the dark on where to deploy and I am getting several problems with Razor as it is not in the shared directory and then I am stuck with deployment issues as I want to organize the landing pages differently than I am organizing the views.
Solution:
I decided to take a different approach and use the ContentResult Action in the controller. I still have the Main View and I use the HTML extensions to render the HTML pages that I have deployed in my customer's directory.
#{
Html.RenderAction("LandingPageContent", "Controller", Model);
}
Then in the controller I load the HTML directly and return the ContentResult
public ContentResult LandingPageContent(object model, FormCollection collection)
{
MySRCHelper helper = new MySRCHelper();
ContentVariables variables = helper.getContentSRC(model.EntryCode);
model.ContentSRC = variables.LandingPageSRC;
return Content(System.IO.File.ReadAllText(Server.MapPath(model.ContentSRC)));
}
I can then configure the path to the raw HTML file to be used and it will be loaded into the View. The View can then house all of the paths to load jQuery, CSS and other necessary javascript to integrate with the raw HTML and allow me to deploy the HTML files into any directory structure that I want. The configuration XML file allows me to find XML elements and use those values for any HTML that I am looking for, like a welcome and thank you page. The helper object will open the XML and find the configuration based on the parameters passed to the View.
<ContentLandingItem entrycode="1" customerID="Cutomer1">
<ContentLandingPageSRC>~/Customers/Customer1/Customer1Landing.htm</ContentLandingPageSRC>
<ContentThankyouSRC>~/Content/Default/GenericThankyou.htm</ContentThankyouSRC>
</ContentLandingItem>
<ContentLandingItem entrycode="2" customerID="Cutomer2">
<ContentLandingPageSRC>~/Customers/Customer2/Customer2Landing.htm</ContentLandingPageSRC>
<ContentThankyouSRC>~/Customers/Customer2/Customer2Thankyou.htm</ContentThankyouSRC>
</ContentLandingItem>
The view still performs its duties and works independently on it own letting the raw HTML decorate the View. The model is still intact and can be used as I wish. The FormCollection is there in case a form submit posts the values to the view and provides some things that I omitted from this question as it did not pertain to this subject.
I don't want to answer my own question and I found the pieces that helped me on another site, so I am putting what I did here in case anyone needs this functionality.

This sounds like using the you can inherit from the virtual path provider view engine and decide based on the URL parameters (or other) which view to return. Some example that you can adjust to your needs:
public class CustomViewEngine : VirtualPathProviderViewEngine
{
public MyViewEngine()
{
this.ViewLocationFormats = new string[] { "~/Views/{1}/{2}.mytheme ", "~/Views/Shared/{2}.mytheme" };
this.PartialViewLocationFormats = new string[] { "~/Views/{1}/{2}.mytheme ", "~/Views/Shared/{2}. mytheme " };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(partialPath);
return new RazorView(controllerContext, physicalpath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(viewPath);
return new RazorView(controllerContext, physicalpath);
}
}
In there you can return a RazorView or WebFormView and set your desired path for the view to use.

Related

ASP.NET Core - Set Layout Page Outside of the View

I have an ASP.NET Core 2.0 application and I'm trying to set the layout page a view should use outside of the view. That way I won't have to keep repeating the same code at the top for all of my views.
I can achieve this by inheriting all of my views from the following base class which sets it within the constructor:
public class RazorPage<TModel> : Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel> {
public RazorPage() {
var theme = "Theme1;"
Layout = $"~/Areas/{theme}/Views/Shared/_Layout" + RazorViewEngine.ViewExtension;
}
}
This works fine however the name of theme changes based on the current URL. I figured I could do this by accessing the current context, however if I call the Context property within the constructor it returns null.
There doesn't appear an appropriate method to override where I could set the Layout property and have access to the current request context.
Does anyone know an alternative way of doing?
Please note that I am aware I could achieve this with _ViewImports/_ViewStart files but due to the structure of my application it would require me to have duplicate files and also I don't like having business logic within my views.
I used an IViewLocationExpander (as suggested by #valery.sntx) to specify where to look for my theme's shared views which changes based on the current URL.
I then auto generated a _ViewStart file using an IFileProvider and simply set it's content to:
#{
Layout = "_Layout";
}
The second part is optional but it saved me from having to create multiple _ViewStart files due to the way my application is designed.

How to Cache JSON Response to generate HTML for Navigation and Branding of a website?

What I am trying to do is that I have a set of three websites hosted on three different clients(A,B,C). One of the website(That is B) carries information about the headers and footers of the site A. I want to get information from the SITE B in a JSON response of HTML to display the Navigation on the SITE A and SITE C.
My understating is that to get the response is JSON and save that information in the cache file to generate HTML.
Is my understanding correct? How can I achieve this? Is there a better way to do this?
Basically, if you want to cache JSON result from your controller's action, you can use OutputCache attribute :
[OutputCache(Duration = 10)] //Duration is in seconds
public ActionResult GetMyJsonData() {
var myData = new Data();
return Json(data);
}
You can apply the same attribute on your action that returns the HTML.

How to have multiple HTML static pages on a ASP.NET MVC project using the Layout page?

On my MVC project I have to incorporate 40 static pages.
I want these pages to use the Layout page.
What is the best way to do that?
I know this question was asked before but I didn't find any good answer.
Any advise?
I don't relly know ASP, but I try to give a generic answer.
So I think if you have a lot of similar static pages, somehow you could make a controller action that handles all these pages. For example the action gets the name of the page as a path variable in the URL, and return the view according to that.
But if that is not possible in the language you are using, you can just make simple separate actions for these pages. Maybe you could group the related ones into the same controller, so you would have a few controllers that handle these pages, and they are not stuffed in one controller.
Basically the solution is very simple, you have to create views for you static HTML (cshtml), then you should add a Route to your Route.Config like this:
routes.MapRoute(
"OrdeForm",
"OrderForm/{file}",
new { controller = "MyController", action = "Page", file} = "" }
);
Where "File" is a dynamic parameter that gets the View name from the URL and renders the right View.
The global controller should be something like this:
public class OrderFormController : Controller
{
public ActionResult Index(string file)
{
return View(file);
}
}
That works perfectly!
Thank you #Erik Philips for the excellant answer!

Is there an example on using Razor to generate a static HTML page?

I want to generate a static HTML page by RAZOR, basically by using includes of partial sub pages.
I have tried T4 as well and do look for an alternative: see here and here
This answer says it is possible - but no concrete example
I have installed Razor generator because I thought this is the way to go, but I do not get how to generate static HTML with this.
Best would be a complete extension which behaves like the T4 concept, but allows me to use the RAZOR syntax and HTML formatting (the formatting issue is basically the reasons why I am not using T4).
If you are trying to take a Razor view and compile it and generate the HTML then you can use something like this.
public static string RenderViewToString(string viewPath, object model, ControllerContext context)
{
var viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = String.Empty;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view,
context.Controller.ViewData,
context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
Or outside of ControllerContext http://razorengine.codeplex.com/
The current version of Razor Generator has the "Generator" option which when used with the "MvcHelper" generator produces a static method for the helpers too.
For example, add this line at the top of your CSHTML file (with the Custom Tool Visual Studio property set to RazorGenerator of course):
#* Generator: MvcHelper, GeneratePrettyNames : true *#
The pretty names option is not strictly necessary but is something I feel should be default, to avoid those crazy long class names with underscores :-)
As you may know already, the main benefit of this method is you can share your helpers in separate assemblies. That is why I use Razor Generator in the first place.
Even within the same assembly, you could now leave your code outside App_Code folder. However that is not the best practice (at least for security) and the Visual Studio designer gets confused. It thinks the method is still not static, but it isn't and works fine.
I'm prototyping my helpers in the App_Code folder of the same site/assembly for speed then copying them to shared components when they're tested. The reason I needed this solution was to create generic Bootstrap helpers without hand-coding every piece of HTML in a HtmlHelper, i.e. used together with this solution from #chrismilleruk.
I guess later I may have to convert the CSHTML helpers to a hand-coded HtmlHelper for speed. But to start with see a great development speed increase at the beginning, from the ability to copy and paste blocks of HTML code I want to automate, then perfect and debug them quickly in the same format/editor.

Model Validation without the ValidationSummary?

I have a LoginModel for my Login Action, but I'm wanting to use just HTML.
Example...
public class LoginModel
{
[Required]
public string Email { get;set; }
}
in my HTML, I have
<input type="text" value="" name="Email">
This is because I'm going to be storing my HTML in my database, problem I'm having is, how do I get model validation without using Html.ValidationSummary()?
I was hoping I could just do <div class="validation-summary-errors"></div>
As this is what is in the HTML, but does not work..
Ideas?
Regardless of where you store your HTML the validation is done on the client side. There are various posts on how to use the virtual path provider to store your views somewhere else (DB) and then validation should still work fine. I think I'm missing why it's not working for you though so I have to imagine you aren't using the path provider to find your views.
Edit
Seems you want to inject messages into a Div. This wont happen automaticaly unless you work some magic in the path provider. Use your own helper method in the view to avoid hacks or just use what's provided by default. If you really want to do it render your view in your controlllet and search for your Div pattern to replace.
custom ValidationForMessage helper removing css element
Note Darin's method
var expression = ExpressionHelper.GetExpressionText(ex);
var modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
var modelState = htmlHelper.ViewData.ModelState[modelName];
without access to ViewContext in your controller you can only render your html for your View. However, somewhere in your view you need (as far as I can tell) a helper method to stick your error collection into ViewData.
Your Virtual Path Provider may have to inject this helper method into your view text so it is there for Razor to parse. Actually - duh. This may be much easier. Your provider may be able to just simply read your html from the database, find the div, and inject the #Html.ValidationSummary into that div. I believe this would work. Why not just put the validation summary in there though if its going to end up there in the end anyways (essentially)