This article provides a brief idea of how to add dynamic content inside a view. Well there are various ways through which we can include dynamic content inside the view. The word dynamic means the content of the view is generated at runtime and it will be different for every request. Let's say on a shopping website, the customer is looking for the number of items he/she has added to the cart, in that case every customer will have a different view shown to him/her depending on the number of items he/she added to the cart. This is what we can say is dynamically showing of data. For displaying dynamic content on the view there are several approaches that can be used, some of them are:
- @if else or @switch case or @for or @foreach inside the view itself for doing a conditional construct, we can also call it inline code since it is written inside the view. This is for performing a simple conditional construct. We can call it as inline code.
- Partial Views: an analogy similar to including a user control inside a page. It offers code reusability by using the same piece of code wherever you require it. It has the same extension “.cshtml” and it is stored inside the ~/Views/shared folder so that the view can use it inside itself. Partial views can be either normal or strongly typed.
- Sections: a way to group/create a section that will be inserted in the layout at a specified region. It helps you to define a page like a template with a specific piece of code at a specific location. Sections can be divided into two types, in other words fixed section or optional section.
- HTML Helper Method: you can define your own HTML helper method. There are system-defined helper methods also that we use for creation of a control like an anchor.
- Child Actions: used for executing the action inside the main action.
Ok now let's start with the example; we'll try to create a simple demo for each of them. For this we will create a new project whose project template will be “basic” and no unit testing will be done so uncheck the checkbox for unit testing. I'm naming my project as “DynamicView”.
1. For Inline Code (@if else or @switch case or @foreach or @for)
Let's add a new controller to the application with the name “Home” and also add a new model with the name “Customer.cs”.
Here is the code snippet for the Model “Customer”:
- public enum CustomerType
- {
- Regular = 0,
- FirstTime = 1,
- WindowShopper=2
- }
-
- public class Customer
- {
- public int CustID { get; set; }
-
- public string Name { get; set; }
-
- public string Address { get; set; }
-
- public CustomerType CustomerType { get; set; }
- }
Also here we've added a new folder to the application with the name “Infrastructure” that contains a C# file with the name “SampleData.cs” that will provide Sample Data to our controller. Here is the code snippet for that:
- public static class SampleData
- {
- public static IEnumerable<Customer> GetDummyCustomer()
- {
- List<Customer> lstCutomers = new List<Customer>()
- {
- new Customer(){CustID=1,Name="Vishal",Address="Andheri",CustomerType=CustomerType.Regular},
- new Customer(){CustID=2,Name="Rahul",Address="Dadar",CustomerType=CustomerType.FirstTime},
- new Customer(){CustID=3,Name="Lincy",Address="Ghatkopar",CustomerType=CustomerType.WindowShopper},
- new Customer(){CustID=4,Name="Mahesh",Address="Worli",CustomerType=CustomerType.FirstTime},
- new Customer(){CustID=5,Name="Ollin",Address="Churchgate",CustomerType=CustomerType.Regular},
- };
- return lstCutomers;
- }
- }
Inside the action Index method write the following block of lines:
- public class HomeController : Controller
- {
- public ActionResult Index()
- {
- return View(SampleData.GetDummyCustomer());
- }
- }
Here is the snippet for Index.cshtml:
- @using DynamicView.Models
- @model IEnumerable<Customer>
- @{
- ViewBag.Title = "Index";
- }
- <h2>
- Index</h2>
- <table>
- <thead>
- <tr>
- <td>
- Customer ID
- </td>
- <td>
- Name
- </td>
- <td>
- Address
- </td>
- <td>
- Customer Type
- </td>
- </tr>
- </thead>
- <tbody>
- @foreach (var cust in Model)
- {
- <tr>
- <td>
- @cust.CustID
- </td>
- <td>
- @cust.Name
- </td>
- <td>
- @cust.Address
- </td>
- <td>
- @* @if (cust.CustomerType == CustomerType.Regular)
- {
- <font color="green">@cust.CustomerType</font>
- }
- else if (cust.CustomerType == CustomerType.FirstTime)
- {
- <font color="pink">@cust.CustomerType</font>
- }
- else
- {
- <font color="gray">@cust.CustomerType</font>
- }*@
- @switch (cust.CustomerType)
- {
- case CustomerType.Regular:
- <font color="green">@cust.CustomerType</font>
- break;
- case CustomerType.FirstTime:
- <font color="pink">@cust.CustomerType</font>
- break;
- default:
- <font color="gray">@cust.CustomerType</font>
- break;
- }
- </td>
- </tr>
- }
- </tbody>
- </table>
Here is the snapshot of it.
2. Partial Views
We can create partial view as normal or as strongly typed views. For demonstrating this we'll just add a normal partial view that will provide us with a way to filter the customer's data by their name. For adding a partial view, click on the shared folder inside the view and add a new partial view by the name “FilterCustomer”. Here is the code snippet for the partial view.
- @using (Html.BeginForm("Filter", "Home", FormMethod.Post, new { id = "filterForm" }))
- {
- <div style="width: 250px; background-color: White; color: #0e0e6c;">
- <table>
- <caption>
- Filter By Name</caption>
- <tr>
- <td>
- Name:
- </td>
- <td>
- <input type="text" name="custName" value="@ViewBag.CustomerName" />
- </td>
- </tr>
- <tr>
- <td>
- <input type="submit" value="Filter" id="btnFilter" name="btnSubmit" />
- </td>
- <td>
- <input type="submit" value="Clear Filter" id="btnClear" name="btnSubmit" />
- </td>
- </tr>
- </table>
- </div>
- }
Also we've added a new function to our home controller that does the job of filtering the data based on customer name ed from the partial view. Here is the snapshot of that.
- [HttpPost]
- public ActionResult Filter(string custName)
- {
- string buttonSource = Request.Form["btnSubmit"];
- if (buttonSource.Equals("Filter") && !string.IsNullOrEmpty(custName))
- {
- List<Customer> lstCustomers = SampleData.GetDummyCustomer().Where(x => x.Name.Equals(custName, StringComparison.InvariantCultureIgnoreCase)).ToList();
- ViewBag.CustomerName = custName;
- return View("Index", lstCustomers);
- }
- else
- {
- ViewBag.CustomerName = null;
- return View("Index", SampleData.GetDummyCustomer());
- }
- }
Here is the snapshot of the outcome:
You can also create a partial view as a strongly typed partial view. We'll further make modification in the code above so that it makes use of a label control to display about the searching criteria. I've added a new partial view with the name “FilterBy.cshtml” and also we made it strongly typed with string as the type. Here is the code for that.
- @model string
- <div style="border: 2px solid silver; float: left; text-transform: capitalize;">
- Filter applied on Name: " @Model "
- </div>
We'll include this partial view inside the main view, in other words Index.cshtml. Here is the piece of code that we added.
- @{
- string[] arr = Model.Select(x => x.Name).ToArray<string>();
- string filterBy = arr.Length > 1 ? "NONE" : arr[0];
- }
- <h2>
- Customer Details</h2>
- <table>
- <tr>
- <td>
- @Html.Partial("FilterCustomer")
- </td>
- </tr>
- <tr>
- <td>
- @Html.Partial("FilterBy", filterBy)
- </td>
- </tr>
- </table>
3. Sections
Ok now for demonstrating this let's try to create sections inside the index.cshtml file and also make a provision for displaying those sections using the _Layout.cshtml file. Inside the _Layout.cshtml file we by default have a RenderBody() method that does the work of displaying the body of the page/view on which the _Layout.cshtml is been applied. By default every _Layout.cshtml will have this method that acts as renderer to display the content of the view. Razor allows you to create multiple sections inside the _Layout.cshtml except the one that already exists. To define a new section we need to make use of the RenderSection method that does the work of rendering the view section by section. There could be multiple sections defined inside the _Layout.cshtml file as needed and some of them could be mandatory or optional. We'll do certain modifications in this _Layout.cshtml so that it displays the section as we desire. These are the changes we made in the _Layout.cshtml:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <title>@ViewBag.Title</title>
- @Styles.Render("~/Content/css")
- @Scripts.Render("~/bundles/modernizr")
- @Scripts.Render("~/bundles/jquery")
- @RenderSection("scripts", required: false)
- </head>
- <body>
- <div style="background-color: #0e0e6c; color: White;font-weight:bold; border-radius: 5px; width: 450px; text-align:center;">
- Below is the header Section</div>
- @RenderSection("Header")
- <div style="background-color: #0e0e6c;font-weight:bold; color: White; border-radius: 5px; width: 450px;text-align:center;">
- Below is the Content Section</div>
- @RenderSection("Content")
- <div style="background-color: #0e0e6c; color: White; border-radius: 5px; margin-top: 20px;
- width: 450px;text-align:center; font-weight:bold;">
- Below is the Footer Section</div>
- @RenderSection("Footer")
- </body>
- </html>
And these are the changes we did in the Index.cshtml file, where in accordance with the _Layout.cshtml file we defined the view in a number of sections. Since all the sections are mandatory we need to define all of them.
- @using DynamicView.Models
- @model IEnumerable<Customer>
- @{
- ViewBag.Title = "Index";
- }
- @{
- string[] arr = Model.Select(x => x.Name).ToArray<string>();
- string filterBy = arr.Length > 1 ? "NONE" : arr[0];
- }
- @section Header
- {
- <h2>
- Customer Details</h2>
- }
- @section Content
- {
- <table>
- <tr>
- <td>
- @Html.Partial("FilterCustomer")
- </td>
- </tr>
- <tr>
- <td>
- @Html.Partial("FilterBy", filterBy)
- </td>
- </tr>
- </table>
- <div class="CSSTableGenerator" style="width: 350px;">
- <table>
- <tr>
- <td>
- ID
- </td>
- <td>
- Name
- </td>
- <td>
- Address
- </td>
- <td>
- Customer Type
- </td>
- </tr>
- @foreach (var cust in Model)
- {
- <tr>
- <td>
- @cust.CustID
- </td>
- <td>
- @cust.Name
- </td>
- <td>
- @cust.Address
- </td>
- <td>
- @* @if (cust.CustomerType == CustomerType.Regular)
- {
- <font color="green">@cust.CustomerType</font>
- }
- else if (cust.CustomerType == CustomerType.FirstTime)
- {
- <font color="pink">@cust.CustomerType</font>
- }
- else
- {
- <font color="gray">@cust.CustomerType</font>
- }*@
- @switch (cust.CustomerType)
- {
- case CustomerType.Regular:
- <font color="green">@cust.CustomerType</font>
- break;
- case CustomerType.FirstTime:
- <font color="#0e0e6c">@cust.CustomerType</font>
- break;
- default:
- <font color="gray">@cust.CustomerType</font>
- break;
- }
- </td>
- </tr>
- }
- </table>
- </div>
- }
- @section Footer
- {
- Asp.Net MVC4 Example on placing dynamic content inside Views
- }
The output of that is displayed below.
NOTE: A view must contain all the mandatory sections defined inside the _Layout.cshtml file. In order to create an optional section we just need to the second parameter while creating the section inside the _Layout.cshtml file like the following. We just changed the @RenderSection(“Footer”) with the following line:
- @RenderSection("Footer", false)
The preceding line makes the footer section optional.
In order to deal with optional sections we can use the IsSectionDefined() function in the _Layout.cshtml file to check whether the user has placed code for the section or not. This can also help us to display the default value for the section if there is no section content supplied by the view. To check whetehr or not the footer content has been supplied by the view we can use the following statement
- @if (IsSectionDefined("footer"))
- @RenderSection("Footer", false)
- else
- {
- <div>
- Asp.Net MVC4 Example on placing dynamic content inside Views
- </div>
- }
4. HTML Helper Methods
A helper method provides a way to reuse a piece of code among multiple controls and also provides us a way to add a new functionality to the existing control helper methods. These are nothing but you extension methods in C#. There are the following various types of helper methods:
- Built-In Helper Method
- Internal Helper Method
- External Helper Method/Custom Helper Method
For a built-In helper method there are various methods available that help us to perform various things. Like the following:
- @using (Html.BeginForm("Filter", "Home", FormMethod.Post, new { id = "filterForm" }))
- {
-
- }
-
- @Html.TextBox(“txtName”,”Value”)
And many more are there that includes some strongly typed helper methods when model binding is used, for example:
- @Html.TextBoxFor(model=>model.Name)
-
- @Html.RadioButtonFor(model=>model.Gender)
Now let's take an example of the Internal Helper Method. We have made some changes in the Index.cshtml file; here is the code snippet for that.
- @using DynamicView.Models
- @model IEnumerable<Customer>
- @{
- ViewBag.Title = "Index";
- }
- @{
- string[] arr = Model.Select(x => x.Name).ToArray<string>();
- string filterBy = arr.Length > 1 ? "ALL" : arr[0];
- }
- @helper RenderCustomerType(CustomerType type)
- {
- switch (type)
- {
- case CustomerType.Regular:
- <font color="green">@type.ToString()</font>
- break;
- case CustomerType.FirstTime:
- <font color="#0e0e6c">@type.ToString()</font>
- break;
- default:
- <font color="gray">@type.ToString()</font>
- break;
- }
- }
- @section Header
- {
- <h2>
- Customer Details</h2>
- }
- @section Content
- {
- <table>
- <tr>
- <td>
- @Html.Partial("FilterCustomer")
- </td>
- </tr>
- <tr>
- <td>
- @Html.Partial("FilterBy", filterBy)
- </td>
- </tr>
- </table>
- <div class="CSSTableGenerator" style="width: 350px;">
- <table>
- <tr>
- <td>
- ID
- </td>
- <td>
- Name
- </td>
- <td>
- Address
- </td>
- <td>
- Customer Type
- </td>
- </tr>
- @foreach (var cust in Model)
- {
- <tr>
- <td>
- @cust.CustID
- </td>
- <td>
- @cust.Name
- </td>
- <td>
- @cust.Address
- </td>
- <td>
- @RenderCustomerType(cust.CustomerType)
- </td>
- </tr>
- }
- </table>
- </div>
- }
- @section Footer
- {
- Footer Section defined inside the Index.cshtml file
- }
As you can see we used an inline helper function instead of mixing the code inside the design. And we can easily call the helper function using the normal razor syntax. The output remains the same. The problem with the inline helper method is that they can only be used within that specific view itself. In order to make them reusable we need to use a C# extension function, in other words an external helper function. We'll add a new C# file inside the “Infrastructure” folder with the name “CustomHelper.cs”. Here is the code snippet for that:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using DynamicView.Models;
-
- namespace DynamicView.Infrastructure
- {
- public static class CustomHelper
- {
- public static MvcHtmlString RenderCustomerType(this HtmlHelper helper, CustomerType type)
- {
- TagBuilder builder = new TagBuilder("font");
- switch (type)
- {
- case CustomerType.Regular:
- builder.Attributes.Add("Color", "green");
- break;
- case CustomerType.FirstTime:
- builder.Attributes.Add("color", "#0e0e6c");
- break;
- default:
- builder.Attributes.Add("color", "gray");
- break;
- }
- builder.SetInnerText(type.ToString());
- return new MvcHtmlString(builder.ToString());
- }
- }
- }
Here is the change made in the index.cshtml file:
- @using DynamicView.Models
- @using DynamicView.Infrastructure
- @model IEnumerable<Customer>
- @{
- ViewBag.Title = "Index";
- }
- @{
- string[] arr = Model.Select(x => x.Name).ToArray<string>();
- string filterBy = arr.Length > 1 ? "All" : arr[0];
- }
- @*@helper RenderCustomerType(CustomerType type)
- {
- switch (type)
- {
- case CustomerType.Regular:
- <font color="green">@type.ToString()</font>
- break;
- case CustomerType.FirstTime:
- <font color="#0e0e6c">@type.ToString()</font>
- break;
- default:
- <font color="gray">@type.ToString()</font>
- break;
- }
- }*@
- @section Header
- {
- <h2>
- Customer Details</h2>
- }
- @section Content
- {
- <table>
- <tr>
- <td>
- @Html.Partial("FilterCustomer")
- </td>
- </tr>
- <tr>
- <td>
- @Html.Partial("FilterBy", filterBy)
- </td>
- </tr>
- </table>
- <div class="CSSTableGenerator" style="width: 350px;">
- <table>
- <tr>
- <td>
- ID
- </td>
- <td>
- Name
- </td>
- <td>
- Address
- </td>
- <td>
- Customer Type
- </td>
- </tr>
- @foreach (var cust in Model)
- {
- <tr>
- <td>
- @cust.CustID
- </td>
- <td>
- @cust.Name
- </td>
- <td>
- @cust.Address
- </td>
- <td>
- @Html.RenderCustomerType(cust.CustomerType)
- </td>
- </tr>
- }
- </table>
- </div>
- }
- @section Footer
- {
- Footer Section defined inside the Index.cshtml file
- }
5. Child Actions
These are action methods that we can invoke from the view. To invoke an action as a child action we need to set the attribute [ChildActionOnly] above the action method. The child action can only invoke partial views since these are called from within a view. To demonstrate this we'll try to create a partial view and a child action that will display the number of records being fetched from the database before or after filtering. For doing this I’m adding a new strongly typed partial view to the shared folder with the name “FetchCount.cshtml”. Here is the code snippet for that:
- @model int
- <div style="border: 1px solid silver; float: left; text-transform: capitalize; width: 180px;
- border-radius: 5px; text-align: left; float: left; margin-left:0px;">
- Records Fetched: <b>@Model</b>
- </div>
Here is the ChildAction Code in HomeController:
- public PartialViewResult FetchCount(int count)
- {
- return PartialView("FetchCount", count);
- }
Here is our latest index.cshtml file where we’ve made the final changes:
- @using DynamicView.Models
- @using DynamicView.Infrastructure
- @model IEnumerable<Customer>
- @{
- ViewBag.Title = "Index";
- }
- @{
- string[] arr = Model.Select(x => x.Name).ToArray<string>();
- string filterBy = arr.Length > 1 ? "NONE" : arr[0];
- }
- @*@helper RenderCustomerType(CustomerType type)
- {
- switch (type)
- {
- case CustomerType.Regular:
- <font color="green">@type.ToString()</font>
- break;
- case CustomerType.FirstTime:
- <font color="#0e0e6c">@type.ToString()</font>
- break;
- default:
- <font color="gray">@type.ToString()</font>
- break;
- }
- }*@
- @section Header
- {
- <h2>
- Customer Details</h2>
- }
- @section Content
- {
- <table style="width: 500px;">
- <tbody>
- <tr>
- <td>
- @Html.Partial("FilterCustomer")
- </td>
- </tr>
- <tr>
- <td style="float: left; display: inline-block;">
- @Html.Action("FetchCount", new { count = Model.Count() })
- @Html.Partial("FilterBy", filterBy)
- </td>
- </tr>
- </tbody>
- </table>
- <div class="CSSTableGenerator" style="width: 350px;">
- <table>
- <tr>
- <td>
- ID
- </td>
- <td>
- Name
- </td>
- <td>
- Address
- </td>
- <td>
- Customer Type
- </td>
- </tr>
- @foreach (var cust in Model)
- {
- <tr>
- <td>
- @cust.CustID
- </td>
- <td>
- @cust.Name
- </td>
- <td>
- @cust.Address
- </td>
- <td>
- @Html.RenderCustomerType(cust.CustomerType)
- </td>
- </tr>
- }
- </table>
- </div>
- }
- @section Footer
- {
- Footer Section defined inside the Index.cshtml file
- }
Here is the output for that where all things are working:
Now filter the records and you’ll see that the count has changed.