How to render a Section in a Partial View in MVC3? - asp.net-mvc

In a MVC3 project, I have a "_Layout.vbhtml" file with this code
<!DOCTYPE html>
<html>
<head>
</head>
<body>
...
<script src="#Url.Content("~/Scripts/jquery-1.8.2.min.js")"></script>
#RenderSection("Scripts", false)
</body>
</html>
Then, I have a Partial View "ValidationScripts.vbhtml" in the Shared folder with
#Section Scripts
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.fix.js")"></script>
<script src="#Url.Content("~/Scripts/localization/messages_de.js")"></script>
End Section
If I call the Partial View from a View like this...
#ModelType MvcExample.MyModel
#Code
ViewData("Title") = "Test"
End Code
#Html.Partial("ValidationScripts")
<h2>Just a Test</h2>
...
the Section "Scripts" is not rendered on the page, and the output HTML is
<!DOCTYPE html>
<html>
<head>
</head>
<body>
...
<script src="#Url.Content("~/Scripts/jquery-1.8.2.min.js")"></script>
</body>
</html>
How can I render the Section in the Partial View ?

I had the same issue on top of duplicate scripts, so I created a couple of Extension methods:
public static class HtmlHelperExtensions
{
private const string _jSViewDataName = "RenderJavaScript";
private const string _styleViewDataName = "RenderStyle";
public static void AddJavaScript(this HtmlHelper htmlHelper,
string scriptURL)
{
List<string> scriptList = htmlHelper.ViewContext.HttpContext
.Items[HtmlHelperExtensions._jSViewDataName] as List<string>;
if (scriptList != null)
{
if (!scriptList.Contains(scriptURL))
{
scriptList.Add(scriptURL);
}
}
else
{
scriptList = new List<string>();
scriptList.Add(scriptURL);
htmlHelper.ViewContext.HttpContext
.Items.Add(HtmlHelperExtensions._jSViewDataName, scriptList);
}
}
public static MvcHtmlString RenderJavaScripts(this HtmlHelper HtmlHelper)
{
StringBuilder result = new StringBuilder();
List<string> scriptList = HtmlHelper.ViewContext.HttpContext
.Items[HtmlHelperExtensions._jSViewDataName] as List<string>;
if (scriptList != null)
{
foreach (string script in scriptList)
{
result.AppendLine(string.Format(
"<script type=\"text/javascript\" src=\"{0}\"></script>",
script));
}
}
return MvcHtmlString.Create(result.ToString());
}
public static void AddStyle(this HtmlHelper htmlHelper, string styleURL)
{
List<string> styleList = htmlHelper.ViewContext.HttpContext
.Items[HtmlHelperExtensions._styleViewDataName] as List<string>;
if (styleList != null)
{
if (!styleList.Contains(styleURL))
{
styleList.Add(styleURL);
}
}
else
{
styleList = new List<string>();
styleList.Add(styleURL);
htmlHelper.ViewContext.HttpContext
.Items.Add(HtmlHelperExtensions._styleViewDataName, styleList);
}
}
public static MvcHtmlString RenderStyles(this HtmlHelper htmlHelper)
{
StringBuilder result = new StringBuilder();
List<string> styleList = htmlHelper.ViewContext.HttpContext
.Items[HtmlHelperExtensions._styleViewDataName] as List<string>;
if (styleList != null)
{
foreach (string script in styleList)
{
result.AppendLine(string.Format(
"<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\" />",
script));
}
}
return MvcHtmlString.Create(result.ToString());
}
}
On any View or Partial View or Display/Edit Template you simply add what you need:
#{
Html.AddJavaScript("http://cdn.jquerytools.org/1.2.7/full/jquery.tools.min.js");
}
In your Layouts you render it where you want it:
<!DOCTYPE html>
<html lang="en">
<head>
#Html.RenderStyles()
#Html.RenderJavascripts()
The only issue you may have is the order in which the scripts are rendered if you get to complex. If that becomes an issue, simply add the scripts to the bottom of your views/templates, and simply reverse the order in the extension method before rendering them.

You can't use sections in partial views. You can go for custom helpers as mentioned here.

I think you should be using helpers for this http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx
If you can upgrade to MVC4 you could use the new bundling and minification feature: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification . It is specifically designed to address what you are trying to achieve (including scripts).
Alternatively you could use http://combres.codeplex.com/ with MVC3

If I understand correctly you have a structure
Layout.cshtml
View - X
PartialView Y (called inside View-X)
then you need to define the
#section Script{ .... } in the View-X and NOT PartialView Y since View-X has its View.Layout set to Layout.cshtml

all this was great info, however if you look at his original code, Section is capitalized therefore not being recognized.
it should be #section blahblah not #Section

Related

How to Remove Client-Side Validation on a textboxfor for multiple emails?

I am creating an MVC application in which I will have an input field for a list of emails. In order to do so, I added multiple in order to allow for the user to enter a comma separated list of emails. By doing it this way, I'm able to have input controls to check for the email(s) to be properly formatted (".+#gmail.com").
The problem is that when I test this, it automatically adds class="input-validation-error" (even if I set the #class="" prior) and will not allow me to post due to an invalid input, as a result. Is there any way to allow for this, or is my only option to make it an Email string property and parse it by commas into the EmailList property in the controller?
(Here is my code):
View:
#Html.TextBoxFor(model => model.EmailList, new { type = "email", placeholder
= "ex#gmail.com (',' Delimited)", title = "Make sure your email(s) are
formatted appropriately (and comma separated).", multiple = "" })
Model:
public List<string> EmailList { get; set; }
UPDATE:
I should also add that I am performing json serialization on post, so It needs to be in the form of a list. Ideally, I would be able to use the multiple for the input of type email tag, since it would allow for the necessary input controls that I would need without making me take it as a string and writing it to a list.
The new fiddle is here, click on it https://dotnetfiddle.net/ORYDVJ
View
#model Testy20161006.Controllers.MurphyModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Tut122</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
$("#emailField").change(function () {
var theList = {
emaillist: []
};
var array = $('#emailField').val().split(",");
$.each(array, function (i) {
theList.emaillist.push(
array[i]
);
});
$.ajax({
url: '/Home/Tut122',
traditional: true,
type: "POST",
contentType: "application/json",
data: JSON.stringify({ murphyModel: theList }),
success: function (data) {
console.log('success!!');
$("#theOutput").html(data)
}
});
})
})
</script>
</head>
<body>
#Html.TextBoxFor(model => model.EmailList, new
{
id = "emailField",
type = "email",
placeholder = "ex#gmail.com (',' Delimited)",
title = "Make sure your email(s) are formatted appropriately (and comma separated).",
multiple = ""
})
<span>The output data:</span>
<div id="theOutput">
</div>
</body>
</html>
Controller/View Model
public class MurphyModel
{
public List<string> EmailList { get; set; }
}
public class HomeController : Controller
{
[HttpPost]
public string Tut122(MurphyModel murphyModel)
{
//You need to get Newtonsoft.JSON
var json = JsonConvert.SerializeObject(murphyModel);
return json;
}
public ActionResult Tut122()
{
MurphyModel model = new MurphyModel();
return View(model);
}
https://dotnetfiddle.net/eadTjh
View
#model Testy20161006.Controllers.MurphyModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Tut122</title>
</head>
<body>
#if (ViewBag.Output != null)
{
<span>#ViewBag.Output</span>
}
#using (Html.BeginForm())
{
#Html.TextBoxFor(model => model.EmailList, new
{
type = "email",
placeholder = "ex#gmail.com (',' Delimited)",
title = "Make sure your email(s) are formatted appropriately (and comma separated).",
multiple = ""
})
<input type="submit" value="submit" />
}
</body>
</html>
Controller/View Model
namespace Testy20161006.Controllers
{
public class MurphyModel
{
//We don't want a list class, rather a string
public string EmailList { get; set; }
}
public class HomeController : Controller
{
[HttpPost]
public ActionResult Tut122(MurphyModel model)
{
var splitEmails = model.EmailList.Split(',');
var anEmail = "These are the different emails: ";
foreach (string email in splitEmails)
{
//set breakpoint here
anEmail+= email + " and ";
}
anEmail = anEmail.Substring(0, anEmail.Length - 5);
ViewBag.Output = anEmail;
return View(model); }
public ActionResult Tut122()
{
MurphyModel model = new MurphyModel();
return View(model);
}
add a js file hopefully u use jQuery
$(document).ready(function () {
$("#EmailList").removeClass("input-validation-error");
});
After Reviewing kblau's answer, I realized that was partially the reason. The other issue that I was running into (where MVC was stepping over any of my manually entered client-side validation) was the result of the unobtrusive validation:
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
After commenting this out, it allowed for me to have the input write to a string Email which I would then assign to a list of strings EmailList in the controller. This ended up working for me.

Open Rotativa print dialog automatically

I am using Rotativa to generate a PDF of a view with the intention of printing it.
I have the following code
public ActionResult PrintTicket(string tempTickID)
{
//var tick = ctx.vw_printTicket.Where(e => e.noTicket == tempTickID).FirstOrDefault();
// return View(tick);
var report = new ActionAsPdf("PrepareTicket", new { tempTickID = tempTickID });
return report;
}
ActionAsPdf allows me to open the "PrepareTicket" view as a pdf and then I can print.
Problem
The problem for me is this, The pdf takes over my entire page and while I can print I don't have access to my program's menus anymore cause it's now a PDF view.
Questions
Is it possible that I call the print dialog automatically instead of showing the pdf?
I think will work for my situation.
Regards
Hi I have tried to create a sample which will solve your issue.
Model
public class Ticketinfo
{
public string name { get; set; }
public int quantity { get; set; }
}
Created a Controller which has 3 Action Method
public class GenerateController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult PrintTicket(string tempTickID)
{
return new ActionAsPdf("RotativaPartialViewAsPdf", new { tempTickID = tempTickID });
}
public ActionResult RotativaPartialViewAsPdf(string tempTickID)
{
Ticketinfo Ticketinfo = new Ticketinfo()
{
name = "Demo",
quantity = 5
};
return PartialView("_RotativaPartialViewAsPdfl", Ticketinfo);
}
}
Partial View
#model WebApplication6.Models.Ticketinfo
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link href="~/Content/bootstrap.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<table class="table">
<tr class="info">
<td>Lot</td>
<td>Name</td>
<td>Quantity</td>
</tr>
<tr class="success">
<td>#Model.name</td>
<td>#Model.quantity</td>
</tr>
</table>
</div>
</body>
</html>
Index View
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<iframe src="#Url.Action("PrintTicket", "Generate", new {tempTickID = "1"})"
width="800px" height="600px">
</iframe>
</div>
</body>
</html>
Output

The model passed into dictionary is type 'TestApp.Models.Registration.RegistrationVm' but this dictionary requires a model item of type System.String

I have the Registration controller:
public class RegistrationController : Controller
{
private RegistrationVmBuilder _registrationVmBuilder = new RegistrationVmBuilder();
public ActionResult Index()
{
return View(_registrationVmBuilder.BuildRegistrationVm());
}
}
and the RegistrationBuilder class:
public class RegistrationVmBuilder
{
public RegistrationVm BuildRegistrationVm()
{
RegistrationVm registrationVm = new RegistrationVm
{
Courses = GetSerializedCourseVms(),
Instructors = GetSerializedInstructors()
};
return registrationVm;
}
}
RegistrationVm class:
public class RegistrationVm
{
public string Courses { get; set; }
public string Instructors { get; set; }
}
_Layout.cshtml:
#model string
<html ng-app="registrationModule">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/bootstrap.js"></script>
<script src="~/Scripts/registration-module.js"></script>
#RenderSection("JavascriptInHead")
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/bootstrap-state.min.css" rel="stylesheet" />
<title>College Registration</title>
</head>
<body>
#RenderBody()
</body>
</html>
And in index view:
#model TestApp.Models.Registration.RegistrationVm
The problem is when i run the app I got this error:
The model item passed into the dictionary is of type
'TestApp.Models.Registration.RegistrationVm', but this
dictionary requires a model item of type 'System.String'.
The shared _Layout.cshtml has a model declared (#model string), which is inherited by any view that uses it. So essentially your Index view's model (#model TestApp.Models.Registration.RegistrationVm) is ignored.
I don't know why you have #model string in your _Layout view (the model doesn't seem to be used in there), but if you want to use inheritance see Pass data to layout that are common to all pages.
Simply remove the model declaration from the shared layout.

Alert does not appear on page load in .cshtml page

I have a simple .cshtml page and My problem is when I run this page alert is not shown. Here is the .cshtml page code:
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ChartDataJSON</title>
<script type="text/javascript">
alert('hello');
</script>
</head>
<body>
<div>
<div id="chart1" ></div>
</div>
</body>
</html>
Also I have a controller class through which this view is generated. The lines written in that class are:
public class jqPlotController : Controller
{
//
// GET: /jqPlot/
public ActionResult Index()
{
return View();
}
public ActionResult ChartDataJSON()
{
var chartData = new List<jqplotModel>();
var point1 = new jqplotModel { Date = DateTime.Now.Date.ToString("yyyy-MM-dd h:mmtt"), Demand = Convert.ToDouble(1), Supply = Convert.ToDouble(3) };
var point2 = new jqplotModel { Date = DateTime.Now.AddDays(10).Date.ToString("yyyy-MM-dd h:mmtt"), Demand = Convert.ToDouble(2), Supply = Convert.ToDouble(4) };
var point3 = new jqplotModel { Date = DateTime.Now.AddDays(31).Date.ToString("yyyy-MM-dd h:mmtt"), Demand = Convert.ToDouble(6), Supply = Convert.ToDouble(6) };
var point4 = new jqplotModel { Date = DateTime.Now.AddDays(106).Date.ToString("yyyy-MM-dd h:mmtt"), Demand = Convert.ToDouble(4), Supply = Convert.ToDouble(2) };
chartData.Add(point1);
chartData.Add(point2);
chartData.Add(point3);
chartData.Add(point4);
return Json(chartData, JsonRequestBehavior.AllowGet);
}
}
Is this due to the “return” keyword in the controller class which stops alert pop up from appearing on the web page.
Has anybody faced this type of issue before. Can somebody help me in figuring out the problem?
Edit:
The view is added with respect to ChartDataJSON method of controller class.
Update: I have made following changes in view and the controller class is as mentioned above.
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ChartDataJSON</title>
<script type="text/javascript">
$.ajax("/jqPlot/ChartDataJSON")
.done(function (data) {
// do something with the data.
alert("success");
})
</script>
</head>
<body>
<div>
<div id="chart1" ></div>
</div>
</body>
</html>
When returning JSON result you will not render out any Views (HTML markup). To return the HTML markup you must write this
return View("name of cshtml file without .cshtml");
where [name of view] is the name of the cshtml file you were referring to (without the .cshtml extension), and the model can be the data you want to use in the cshtml file.
To retrieve the data using ajax you can use this command as a example
$.ajax( "/jqPlot/ChartDataJSON" )
.done(function(data) {
// do something with the data.
alert( "success" );
})
Update:
To load the view you have to call an action that returns and renders the view. Lets say your view is named Index.cshtml and its full path is "Views/jqPlotController/Index.cshtml
Then all you have to do is call the URL of the action, http://[nameofdomain]/Index .
Then the view should be loaded and the javascript function trigger and call the action ChartDataJSON, which will return the JSON result and in this case the alert is triggered if the call to the action is successfull.

Registering Javascript / CSS files within Partial Views with Output Caching only once

I would like to create partial views which would require the inclusion of Javascript/CSS files in order to function properly. The same partial view can be included multiple times within a page and hence if I put the embedding of the Javascript/CSS files within the Partial View HTML content, such files will be embedded N amount of times. I would want such files to be embedded only once.
Also, such Partial Views are being rendered using the Html.Action method so as to make use of output caching so the Action method will not execute all the time.
My idea was to register the javascript/css files within the Partial View with with some controller which manages such embedding and then outputs them at the scripts section of the page, at the bottom before the end </body> tag but since the Actions will be cached, such code is not guaranteed to be executed but can be loaded from Cache.
Any suggestions for best practices for such scenario?
You could define 2 custom helpers:
public static class HtmlExtensions
{
public static IHtmlString RenderRegisteredScripts(this HtmlHelper htmlHelper)
{
var ctx = htmlHelper.ViewContext.HttpContext;
var registeredScripts = ctx.Items["_scripts_"] as Stack<string>;
if (registeredScripts == null || registeredScripts.Count < 1)
{
return null;
}
var sb = new StringBuilder();
foreach (var script in registeredScripts)
{
var scriptBuilder = new TagBuilder("script");
scriptBuilder.Attributes["type"] = "text/javascript";
scriptBuilder.Attributes["src"] = script;
sb.AppendLine(scriptBuilder.ToString(TagRenderMode.Normal));
}
return new HtmlString(sb.ToString());
}
public static void RegisterScript(this HtmlHelper htmlHelper, string script)
{
var ctx = htmlHelper.ViewContext.HttpContext;
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var registeredScripts = ctx.Items["_scripts_"] as Stack<string>;
if (registeredScripts == null)
{
registeredScripts = new Stack<string>();
ctx.Items["_scripts_"] = registeredScripts;
}
var src = urlHelper.Content(script);
if (!registeredScripts.Contains(src))
{
registeredScripts.Push(src);
}
}
}
and then have a _Layout.cshtml in which you are calling the RenderRegisteredScripts helper at the end:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
</head>
<body>
#RenderBody()
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
#RenderSection("Scripts", required: false)
#Html.RenderRegisteredScripts()
</body>
</html>
Now you could have a view which renders some partial as many times as you like:
#for (int i = 0; i < 10; i++)
{
#Html.Partial("_Foo")
}
#Html.Action("Bar")
and inside this partial (_Foo.cshtml) use the RegisterScript helper to register dependent scripts for this partial:
#{Html.RegisterScript("~/scripts/foo.js");}
Now when you look at the generated HTML markup the script is included only once:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<script src="/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="/scripts/foo.js" type="text/javascript"></script>
</body>
</html>

Resources