Creating reusable HTML view components using Razor in ASP.NET MVC - asp.net

I have a Razor helper function that creates a re-usable HTML panel that saves me writing the same HTML over and over again.
#helper DefaultPanel(string panelTitle) {
<div class="panel">
<div class="panel-logo"><img src="/logo.png"></div>
<div class=panel-inner">
<p class="panel-title">#panelTitle</p>
<div class="panel-content">
/* Can I pass content to be rendered in here here? */
</div>
</div>
</div>
</div>
}
I'm wondering, is it possible to re-use this helper to fill .panel-content with more HTML to allow further flexibility and code reuse - similar to something like below:
#LayoutHelpers.DefaultPanel("Welcome back") {
<div class="panel-content-inner">
<p>Welcome back, please select from the following options</p>
Profile
My Defails
</div>
}
Whilst using .NET MVC I've noticed the Html.BeginForm() does a similar thing when wrapping the code within the #using statement within the Html.BeginForm declaration, like so:
#using (Html.BeginForm("Index", "Login", FormMethod.Post))
{
<div>This content gets rendered within the <form></form> markup.</div>
}
But can this done using #helper methods? If not, is it possible to create a HtmlHelper extension to do a similar thing the way the Html.BeginForm() method does?
You can do a very similar thing using the #section syntax as seen here
This seems like something that would be really useful to be able to do, and odd that there's no easy way to do it on a component level.

There are two ways to achieve the required functionality.
1. #helper
Create #helper which accepts whatever parameters you need plus a function (single object parameter, returns object):
#helper DefaultPanel(string panelTitle, Func<object, object> content)
{
<div class="panel">
<div class="panel-logo">
<img src="/logo.png" />
</div>
<div class="panel-inner">
<p class="panel-title">#panelTitle</p>
<div class="panel-content">
#content(null)
</div>
</div>
</div>
}
Usage:
#DefaultPanel("title",
#<div class="panel-content-inner">
<p>Welcome back, please select from the following options</p>
Profile
My Defails
</div>
)
Your function may also accepts parameters, example here.
2. HtmlHelper extension method
Add the following code anywhere in your project:
namespace System.Web.Mvc
{
public static class HtmlHelperExtensions
{
public static HtmlDefaultPanel DefaultPanel(this HtmlHelper html, string title)
{
html.ViewContext.Writer.Write(
"<div class=\"panel\">" +
"<div class=\"panel-inner\">" +
"<p class=\"panel-title\">" + title + "</p>" +
"<div class=\"panel-content\">"
);
return new HtmlDefaultPanel(html.ViewContext);
}
}
public class HtmlDefaultPanel : IDisposable
{
private readonly ViewContext _viewContext;
public HtmlDefaultPanel(ViewContext viewContext)
{
_viewContext = viewContext;
}
public void Dispose()
{
_viewContext.Writer.Write(
"</div>" +
"</div>" +
"</div>"
);
}
}
}
Usage:
#using (Html.DefaultPanel("title2"))
{
<div class="panel-content-inner">
<p>Welcome back, please select from the following options</p>
Profile
My Defails
</div>
}
The extension method writes directly to the context. The trick is to return a disposable object, which Dispose method will be executed at the end of using block.

I don't know if #helper methods can do this but HtmlHelper extensions certainly can. You've mentioned the Html.BeginForm() example which is probably the most well known - all that does is return an object which implements IDisposable which means that when the Dispose() method is called it just calls the complimentary Html.EndForm() method to add the appropriate closing tags.
It would be very simple to do something similar for your HTML code. You can view the source code to the ASP.NET MVC HtmlHelpers at http://aspnetwebstack.codeplex.com/ - the BeginForm() code can be specifically be viewed here.

Related

How to pass csHtml to ViewComponent in .Net Core MVC?

I would like to pass csHtml to a ViewComponent like this:
<vc:some-component title="Title"
body="#Html.Raw("<div>this is a nice div, my name is #Model.Name</div>")">
</vc:some-component>
As you can see, I want to pass HTML that contains references to variables or the model (that's why I'm calling it csHtml and not just Html).
And here is the ViewComponent code:
<div>
<p>#Model.Title</p>
#Model.Content
</div>
Is this even possible? I've been searching and haven't found any examples of how to do this.
Edit: Got it working! Thanks everyone for your help.
Here is some sample code for how to do this:
Calling ViewComponent:
#{
Func<dynamic, object> children =
#<div>
<p style="color: white">aaaa</p>
<p style="color: white">bbbb</p>
<p style="color: white">bbbb</p>
</div>;
}
<vc:some-component body=#children>
</vc:some-component>
View Component content:
#model SomeComponentModel
<div>
#Model.Body("")
</div>
You should be able to add the parameters to the ViewComponent and pass them along towards the ViewComponent view itself as you go.
<vc:some-component title="Title"
body="#Html.Raw("<div>this is a nice div, my name is #Model.Name</div>")">
</vc:some-component>
or something similar to this
#await Component.InvokeAsync("SomeComponent", new { title="Title", body= Html.Raw($"<div>this is a nice div, my name is {Model.Name}</div>") })
Your viewcomponent IViewComponentResult would look something like this, using ViewBag just as example
public async Task<IViewComponentResult> InvokeAsync(string title, string body)
{
//...
ViewBag.Content = body;
ViewBag.Title = title;
//...
return View();
}
And then in the related ViewComponent View
<div>
<p>#ViewBag.Title</p>
#ViewBag.Content
</div>
Creating a more specific ViewModel to pass along would also be an option ofcourse
Yes it is possible, you have to make a workaround but it is achievable,this answer has an example of what you are looking for.

Create three columns layout, all dynamic

I want to create a dynamic website in asp.net using MVC model from Visual Studio 2013.
All the columns will have dynamic content, each one changing it's content on different selections based on user actions or by the controllers.
How can I create those three dynamic columns?
I already have my layout in HTML and CSS but I need to separate those three columns and have the possibility to change their content from my controllers.
Later edit:
In my case I have two menus in the website, one to the left and the other one to the right. Actually they are three with the other one from the top. When I select something from the top, the menu from the left must be showed depending on the top selection.
You can populate each column by using ViewBag or ViewData. So in the Layout, use this data to pupulate this columns, for example:
1) Calling this action will change the content for something that you specify:
public ActionResult MyView()
{
// you can put anything in this ViewBag,
// a string, a list of something and then use it in the view
// Its dinamyc
ViewBag.Column1 = "My content column 1";
ViewBag.Column2 = "My content column 2";
ViewBag.Column3 = new string[] {"Product1", "Product1", "Product1"};
}
2) Consume this data in your layout to generate the dynamic content:
<div id='column1'>
#ViewBag.Column1
</div>
<div id='column2'>
#ViewBag.Column2
</div>
<div id='column3'>
<ul>
#foreach(var s in ViewBag.Column3)
{
<li>
#s
</li>
}
<ul>
You should take a look here for more information:
What is ViewData, ViewBag and TempData?
You should use Sections as well, take a look here:
ASP.NET MVC 3: Layouts and Sections with Razor
I would advise you to make three different Controller Actions. For Example:
public ActionResult Part1()
{
return View();
}
public ActionResult Part2()
{
return View();
}
public ActionResult Part3()
{
return View();
}
And then call them in your Common page.
Example:
<div class="row">
<div class="col-md-4">
#Html.Action("Part1")
</div>
<div class="col-md-4">
#Html.Action("Part2")
</div>
<div class="col-md-4">
#Html.Action("Part3")
</div>
</div>
You can have the selection criteria controls inside the view of the controllers. i.e. if you need a drop down list for the First Block. Place it in the Part1 View. Also to make it async you can have Ajax forms in all three views.

MVC3 #Model becomes null in layout

So I have something odd happening in a MVC3 layout I created. It's strongly typed to BaseVM. Every View model inherits from BaseVM. When I debug and step through the code and it it steps through my layout, the BaseVM model is not null and it calls a method to get the current year for the copyright in the footer. But as soon as I step to the bottom of the layout, it throws an exception back up where I was calling the property to get the year and suddenly the Model is null. Now this doesn't cause the application to show an error screen or message, but this exception gets logged every time the page is loaded. Here's a code sample:
public class BaseVM
{
public string CurrentYear
{
get
{
return DateTime.Today.Year.ToString();
}
}
public BaseVM()
{
}
}
public class PrelaunchReserveVM : BaseVM
{
public PrelaunchDTO Prelaunch { get; set; }
public PrelaunchReserveVM()
: this(new PrelaunchDTO()) { }
public PrelaunchReserveVM(PrelaunchDTO prelaunch)
: base()
{
this.Prelaunch = prelaunch;
}
}
The action in the controller:
public ActionResult Reserve()
{
return View("Reserve", new PrelaunchReserveVM(new PrelaunchDTO()));
}
The layout:
#model StickyThink.Models.VM.BaseVM
...
<div id="container">
<div id="main" role="main" style="padding: 0px; margin: 0px;">
#RenderBody()
</div>
</div>
<footer>
<div id="FooterPageContainer" class="center">
Copyright &copy #Model.CurrentYear ... All rights reserved.
</div>
</footer>
<!-- END: Add your site or application content here -->
#Html.Partial("_Scripts")
#RenderSection("scripts", false);
So it steps into #Model.CurrentYear and model is not null. Then it steps down to #RenderSection (to render some scripts from the view) and then an exception is thrown and #Model is null. Thoughts?
You need to pass Model as the 2nd parameter in #Html.Partial("_Scripts", Model).
If I had to take a wild guess, I think there is a problem with the content of the _Scripts partial. Maybe it needs the model #krillgar, but my guess onn that would be no. I would suggest moving the _Scripts partial to NOT be a partial and reference the scripts in the header like normal. I suspect that this exception will then shows its face earlier and help you find the problem, and the reason the Model becomes null is becuase of the type of exception. So if you move the _Scripts partial and have null earlier, you know this is the place to look.

Acknowledge and Reload PartialView with Razor

I am new to MVC3 and Razor.
I have an "attention banner" on the master page as a Partial View that I want to "acknowledge" with a click on a link to close the banner (without reloading the page). I believe I need to use jQuery and an Ajax call, but I just can't seem to find the right combination.
Here is part of my _Layout.cshtml:
<section id="main">
<span id="attentionBar">#{ Html.RenderPartial("_AttentionBarPartial"); }</span>
#RenderBody()
</section>
This is my Partial View (just using Session as a shortcut for now to get it to work). I'm not sure what to use as the "link" to reload the view:
#{ this.Layout = null;}
#if(! String.IsNullOrWhiteSpace(#Session["Attention"].ToString()))
{
<div class="attentionPanel">
<span class="attentionLabel">Attention</span>
#Session["Attention"].ToString()
<span class="attentionLabel">
#* WHAT DO I PUT HERE *#
#Ajax.ActionLink("X", "AcknowledgeAttentionBar", "Home", new AjaxOptions{ UpdateTargetId="attentionPanel", InsertionMode=InsertionMode.Replace })
</span>
</div>
}
Here is my Home controller. Again, I am not sure that the code is quite correct, but essentially I will clear out the condition that shows the attention banner.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Home Controller Updated At " + DateTime.Now.ToLongDateString()
+ " " + DateTime.Now.ToLongTimeString();
return View();
}
public PartialViewResult AcknowledgeAttentionBar()
{
Session["Attention"] = String.Empty;
return PartialView("_AttentionBarPartial");
}
}
2 things:
Make sure you have included the jquery.unobtrusive-ajax.js script to your page in order for Ajax.ActionLink helper to work and send an AJAX request when the link is clicked instead of a normal redirect:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
In your AjaxOptions you have specified UpdateTargetId="attentionPanel" but there's no element in your markup with id="attentionPanel". You have a div with class="attentionPanel" but that's not the same thing. On the other hand you have wrapped your banner in a <span id="attentionBar">, so you probably meant UpdateTargetId="attentionBar".

how to create reusable "widget" markup across modules in an ASP.NET MVC 2.0 site?

Scenario:
platform: ASP.NET 4.0, MVC 2.0 (doesn't have to be MVC if it's not the right solution)
Create various widgets that inherit the same core markup:
<div class="widget">
<div class="hd-widget">
<!-- dynamically inject code here for each actual widget -->
</div>
<div class="bd-widget">
<!-- dynamically inject code here for each actual widget -->
</div>
<div class="ft-widget">
<!-- dynamically inject code here for each actual widget -->
</div>
</div>
The above "simplified" example should be in some reusable control or masterpage.
The flow is something like this:
Controller -> View -> Partial View (1..n) -> widget markup
User instantiates an action which calls a controller. The controller tries to render a view that contains content + multiple widgets (left rail, right rail, etc). The Widgets all have their own individual partial views but each partial view should inherit or consume a base set of widget markup and have controller areas where to embed functionality.
Functionality in the head, body, and footer can either be plain text, HTML, or additional custom controls.
What is the best recommended approach?
I would be thinking you would have a WidgetModel which contains a set of properties regarding the widget.
Eg.
public class WidgetModel
{
public string ControllerName { get; set; }
public string HeaderAction { get; set; }
public string BodyAction { get; set; }
public string FooterAction { get; set; }
}
And you have a Widget View like
<div class="widget">
<div class="hd-widget">
<%: Html.RenderAction(Model.HeaderAction, Model.ControllerName) %>
</div>
<div class="bd-widget">
<%: Html.RenderAction(Model.BodyAction, Model.ControllerName) %>
</div>
<div class="ft-widget">
<%: Html.RenderAction(Model.FooterAction, Model.ControllerName) %>
</div>
</div>
Of course you could add more to it if you wanted.
I assume that your widgets will be stored in a DB.
I guess you'll need tables for Paths, PageSettings, Widgets, WidgetInstances, MasterPages, MasterPageZones and Zones.
I think that each widget will need to make a call in to RenderAction and this action will be stored in the DB against the widget. The widget header and footer can probably just be retrieved from the DB.
When I did this I spent quite a bit of time creating the dynamic editor for each of the widgets, the state of each widget is serialized in json, the only issue I'm still working on is dynamic validation of the widget editor.
Page Layouts are handled by a custom personalization class, storing the masterpage, path, userId etc.
It's quite an involved solution using JQuery sortable but really quick and SEO friendly.

Resources