Append HTML code to a existing Razor section - asp.net-mvc-3

Is it possible to append the HTML code to a existing razor section?
Below is my scenario:
My _layout.cshtml contains something like this:
#RenderSection("BottomSection", required: false)
and in one of the view - _article.cshtml, I have defined the section like below:
#section BottomSection
{
<script src='~/Scripts/module/article_details.js' type='text/javascript'></script>
<script src='~/Scripts/module/modal.js' type='text/javascript'></script>
#MvcHtmlString.Create(Model.ExtraStuff)
}
and in a partial view named _counter.cshtml, which is used by the above view; I would like to add more HTML code to the same section i.e., BottomSection.
I tried declaring the BottomSection section again in the partial view:
#section BottomSection{
<text>More data</text>
}
But it didn't worked out.
Is there any way to achieve this - dynamically append more code to an already defined razor section in MVC 4?
Please note that the partial view doesn't expects any data from the parent view/model.
And I'm using MVC 4 with .Net Framework 4.0/VS2010.

I don't know how to append stuff to sections (in fact I would like to know that myself), but I know a trick that might produce similar result. Instead of using sections one can use TempData. TempData is a lot like ViewBag, but once a variable is set it'll live there for current user until one tries to access it again (it can live through a few successive requests for current user, so extra caution is advised). Below is an example of how it could be used.
In layout:
#Html.Raw(new MvcHtmlString((string)TempData["BottomSection"]));
In the view:
#{
var bottomSection = (string)TempData["BottomSection"];
if (bottomSection == null)
{
bottomSection = "";
}
bottomSection += "<script src='~/Scripts/module/article_details.js' type='text/javascript'></script>\n";
bottomSection += "<script src='~/Scripts/module/modal.js' type='text/javascript'></script>\n";
bottomSection += Model.ExtraStuff + "\n";
TempData["BottomSection"] = bottomSection;
}
In the partial view:
#{
var bottomSection = (string)TempData["BottomSection"];
if (bottomSection == null)
{
bottomSection = "";
}
bottomSection += "More data";
TempData["BottomSection"] = bottomSection;
}
This can be further improved by writing a helper for those pseudo sections and\or by moving the contents of the sections a separate partials (look below).
bottomSection += Html.Partial("_StuffToAddToSection").ToString();
Helper class:
public static class PseudoSectionsHelper
{
public static MvcHtmlString AppendToPseudoSection<T>(this TempDataDictionary TempData, string sectionName, T model, Func<T, HelperResult> content, bool addNewLineCharacter = true)
where T : class
{
return AppendToPseudoSection(TempData, sectionName, content(model).ToString(), addNewLineCharacter);
}
public static MvcHtmlString AppendToPseudoSection(this TempDataDictionary TempData, string sectionName, MvcHtmlString content, bool addNewLineCharacter = true)
{
return AppendToPseudoSection(TempData, sectionName, content.ToString(), addNewLineCharacter);
}
public static MvcHtmlString AppendToPseudoSection(this TempDataDictionary TempData, string sectionName, string content, bool addNewLineCharacter = true)
{
var section = (string)TempData[sectionName];
if (section == null)
{
section = "";
}
else if (addNewLineCharacter)
{
section += "\n";
}
section += content;
TempData[sectionName] = section;
// We return empty MvcHtmlString to be able to use this helper inline (without declaring code block #{ some code... } in view)
return new MvcHtmlString("");
}
public static MvcHtmlString PseudoSection(this TempDataDictionary TempData, string sectionName)
{
var section = (string)TempData[sectionName];
return new MvcHtmlString(section);
}
}
Use example
In layout add:
#TempData.PseudoSection("BottomSection")
In view:
#TempData.AppendToPseudoSection("BottomSection", Model, #<text>
<script src='~/Scripts/module/article_details.js' type='text/javascript'></script>
<script src='~/Scripts/module/modal.js' type='text/javascript'></script>
#MvcHtmlString.Create(Model.ExtraStuff)
</text>)
or
#{
TempData.AppendToPseudoSection("BottomSection", Model, #<text>
<script src='~/Scripts/module/article_details.js' type='text/javascript'></script>
<script src='~/Scripts/module/modal.js' type='text/javascript'></script>
#MvcHtmlString.Create(Model.ExtraStuff)
</text>);
}
or even
#TempData.AppendToPseudoSection("BottomSection", Html.Partial("BottomSectionScriptsAndStuff"))
And in partial:
#TempData.AppendToPseudoSection("BottomSection", "More data")

Maybe i dont understand your question but why you dont using nested partial views???
for example :
PartialView1
`<script src='~/Scripts/module/article_details.js' type='text/javascript'></script>
<script src='~/Scripts/module/modal.js' type='text/javascript'></script>
#MvcHtmlString.Create(Model.ExtraStuff)
#{Html.RenderPartial("PartialView2",Model.ExtraStuff );}`
PartialView2
`<text>More data</text>`

Using Ajax you can load the partial view and can render in your target division.
Try using jquery ajax
$.ajax({
type: 'GET',
url: '#Url.Action("Action","Controller")',
cache: false,
timeout: 20000,
contentType: "application/json; charset=utf-8",
success: function (_results) {
$("#TargetDiv").html(_results);
},
error: function (_results) {
}
});

Related

AJAX pagedlist with partial view

I can't quite figure out how to get a partial view to render a paged list using ajax.
The closest I've got it to working is the example from Using paging in partial view, asp.net mvc
I'm basically trying to create a page with a list of comments per user where the page can be changed in the same way as the answers tab on the stackoverflow users page.
The paging works fine the on the first pager click, but then the the partial view is all that is returned once I click on the pager again.
Controller:
public class ProductController : Controller
{
public IQueryable<Product> products = new List<Product> {
new Product{ProductId = 1, Name = "p1"},
new Product{ProductId = 2, Name = "p2"},
new Product{ProductId = 3, Name = "p3"},
new Product{ProductId = 4, Name = "p4"},
new Product{ProductId = 5, Name = "p5"}
}.AsQueryable();
public object Index()
{
return View();
}
public object Products(int? page)
{
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 3); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return PartialView("_Products");
}
}
Views:
Index.cshtml:
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<h2>List of Products</h2>
<div id="products">
#Html.Action("Products", "Product")
</div>
#section scripts{
<script type="text/javascript">
$(function() {
$('#myPager').on('click', 'a', function() {
$.ajax({
url: this.href,
type: 'GET',
cache: false,
success: function(result) {
$('#products').html(result);
}
});
return false;
});
});
</script>
}
_Products.cshtml:
#using PagedList.Mvc;
#using PagedList;
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
<div id="myPager">
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Products", new { page }))
</div>
Model
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
Can anyone show me what I'm doing wrong?
I ended up using the unobtrusive ajax example from the pagedlist source [https://github.com/troygoode/PagedList][1]
partial view:
#using PagedList;
#using PagedList.Mvc;
<ul id="names" start="#ViewBag.Names.FirstItemOnPage">
#foreach(var i in ViewBag.Names){
<li>#i</li>
}
</ul>
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Index", new { page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing( new AjaxOptions(){ HttpMethod = "GET", UpdateTargetId = "unobtrusive"}))
Index:
#{
ViewBag.Title = "Unobtrusive Ajax";
}
#using PagedList;
#using PagedList.Mvc;
#Styles.Render("~/Content/PagedList.css")
<h2>Unobtrusive Ajax</h2>
<p>Example of paging a list:</p>
<div id="unobtrusive">
#Html.Partial("UnobtrusiveAjax_Partial")
</div>
Controller:
public class UnobtrusiveAjaxController : BaseController
{
// Unobtrusive Ajax
public ActionResult Index(int? page)
{
var listPaged = GetPagedNames(page); // GetPagedNames is found in BaseController
if (listPaged == null)
return HttpNotFound();
ViewBag.Names = listPaged;
return Request.IsAjaxRequest()
? (ActionResult)PartialView("UnobtrusiveAjax_Partial")
: View();
}
}
Just in case, since the original question wasn't answered. I guess the problem was that on click handlers weren't reattached to the new pager elements generated by AJAX request. I also don't like unobstrusive AJAX solution in this case, since pager id is hardcoded in the nested view while passing it in some other way may be too cumbersome.
<script type="text/javascript">
// better not to clutter global scope of course, just for brevity sake
var attachHandlers = function() {
$('#myPager a').click(function() {
$('#myPager').load(this.href, function() {
attachHandlers();
});
return false;
});
};
$(document).ready(function () {
attachHandlers();
});
</script>

how to load a partial view ona selector using jquery?

var uri="#Url.Content("/Views/Shared/_LogOnPartial")";
$("#logindisplay").load(uri);
it give me error
Files with leading underscores ("_") cannot be served.
why the url don't work.
You can use following code to load Partial Views in ~/Views/Shared/ Folder.
<script type="text/javascript">
$(document).ready(function () {
$("#btnclick").click(function () {
var uri = '#Url.Content("_LogOnPartial")';
$("#logindisplay").load(uri);
});
});
</script>
HTML is,
<input type="button" id="btnclick" value="Load View" />
<div id="logindisplay">
</div>
You should use
"#Html.Partial("/Views/Shared/_LogOnPartial)
to load partial instead of #Url.Content("/Views/Shared/_LogOnPartial")
best practice is to define path for partials, Layouts & views earlier
in App_Start/RouteConfig write method like below
public static void RegisterViewEngines(ICollection engines)
{
/*
* {0} = view name
* {1} = controller name
*/
engines.Clear();
engines.Add(new RazorViewEngine
{
ViewLocationFormats = new[] { "~/Views/{0}.cshtml" },
MasterLocationFormats = new[] { "~/Views/Shared/{0}.cshtml" },
PartialViewLocationFormats = new[] { "~/Views/Partial/{0}.cshtml" }
});
}
then execute it in Globa.asax like
RouteConfig.RegisterViewEngines(ViewEngines.Engines);
then you can easily call partial like this
#Html.Partial("_LogOnPartial");

ASP.NET MVC 3: Append to sections

I'm trying to figure out if it is possible to append to a section. Here is my structure:
_Layout.cshtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<link href="#Url.Content("~/Content/style.css")" rel="stylesheet" type="text/css" />
#RenderSection("Css", false)
<script type="text/javascript" src="#Url.Content("~/Content/scripts/head.load.min.js")"></script>
</head>
<body class="bg_g">
#RenderBody()
<script type="text/javascript">
#RenderSection("Javascript", false)
</script>
</body>
</html>
Logon.cshtml
#{
Layout = "~/Views/Shared/_DMZ.cshtml";
ViewBag.Title = "Logon";
}
#section Javascript += {
// JAVASCRIPT CODE;
}
<div>
Stuff
#{ Html.RenderAction("Register", "Account"); }
#{ Html.RenderAction("Register2", "Account"); }
</div>
Register.cshtml
#{
Layout = null;
}
#section Javascript += {
// More javascript code
}
<div>
Register stuff
</div>
Register2.cshtml
#{
Layout = null;
}
#section Javascript += {
// Even More javascript code
}
<div>
Register stuff part 2
</div>
Hopefully that explains what I'm really trying to do. I would also like to do the same thing with my css section. It would be even better if I could also get it to render the Javascript like this:
head.js(
"#Url.Content("~/Content/scripts/jquery-1.6.2.min.js")",
"#Url.Content("~/Content/scripts/jquery.tools.min.js")",
"#Url.Content("~/Content/lib/jquery-validation/jquery.validate.js")",
// Loop through all javascript files included from the sub views and add them just like above
function () {
loginTabs.init();
// Loop through all javascript functions that have been added to the InitFunctions section?
}
)
Maybe sections aren't the correct solution to this problem, but I know that there has to be a way to accomplish something like this. Any Ideas?
A bit of a late entry - but hopefully still useful to someone out there:
I don't know if there is a native method to achieve it but i have been using this for some time now and it's really helpful:
public static IHtmlString Resource( this HtmlHelper HtmlHelper, Func<object, HelperResult> Template, string Type )
{
if( HtmlHelper.ViewContext.HttpContext.Items[Type] != null ) ( (List<Func<object, HelperResult>>)HtmlHelper.ViewContext.HttpContext.Items[Type] ).Add( Template );
else HtmlHelper.ViewContext.HttpContext.Items[Type] = new List<Func<object, HelperResult>> { Template };
return new HtmlString( String.Empty );
}
public static IHtmlString RenderResources( this HtmlHelper HtmlHelper, string Type )
{
if( HtmlHelper.ViewContext.HttpContext.Items[Type] == null ) return new HtmlString( String.Empty );
var Resources = (List<Func<object, HelperResult>>)HtmlHelper.ViewContext.HttpContext.Items[Type];
foreach( var Resource in Resources.Where( Resource => Resource != null ) )
{
HtmlHelper.ViewContext.Writer.Write( Resource( null ) );
}
return new HtmlString( String.Empty );
}
The usage is:
//This is how you save it
#Html.Resource(#<style>.test{color: #4e4e4e}</style>, "css")
//This is how you read it
#Html.RenderResources("css")

Read selected value of drop down in controller

Requirment: I have a drop down and a table on my cshtml page. The drop down displays a list of vendors and the details corresponding to selected vendor are displayed in table. I am submitting the form using jquery when the value of the drop down changes.
Problem: How to cath selected value of drop down in controller?
Code:
#Html.DropDownList("VendorList", new SelectList(Model.vendorList, "vendorId", "vendorName"))
#using (Html.BeginForm("VendorDetails", "VendorLookUp", FormMethod.Post, new { id = "vendorDetailsForm" }))
{
<div class="margin-10-top" >
<table id= "VendorDetail" class="VendorDetail">
........ details of vendor.........
</table>
</div>
}
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
$('#VendorList').change(function () {
$('#vendorDetailsForm').submit();
});
});
</script>
the code in my controller is:
[AcceptVerbs("POST")]
public ActionResult SearchResult(FormCollection collection)
{
try
{
string vendorName = collection["searchItem"].ToString();
vendorName = vendorName.Trim();
List<Vendor> vendorList = Queries.compiledVendorQuery(dbContext, vendorName).ToList<Vendor>();
if(vendorList.Count() == 0)
return View("EmptySearch");
Vendor selectedVendor = vendorList[0];
VendorDetails vendorDeatils = Queries.compiledVendorDetailsQuery(dbContext, selectedVendor.vendorId.ToString()).FirstOrDefault();
VendorResult vendorResult = new VendorResult();
vendorResult.vendorList = vendorList;
vendorResult.vendorDetails = vendorDeatils;
return View(vendorResult);
}
catch (Exception e)
{
return View("EmptySearch");
}
}
[AcceptVerbs("POST")]
public ActionResult VendorDetails(FormCollection collection)
{
**here comes the control after postback
require value of the selected item**
Vendor selectedVendor = ??
VendorDetails vendorDeatils = Queries.compiledVendorDetailsQuery(dbContext, selectedVendor.vendorId.ToString()).FirstOrDefault();
VendorResult vendorResult = new VendorResult();
vendorResult.vendorDetails = vendorDeatils;
return View(vendorResult);
}
Since you're not really using the FormCollection, you could just use an int (or whatever the ID is on your model) in your action method:
[HttpPost]
public ActionResult VendorDetails(int vendorId = 0)
{
Vendor selectedVendor = // Load from your data source using vendorId
... // Do the rest of your work
}
In your HTML, move your #Html.DropDownListFor() into your form, rename it to the argument name, then submit the form as normal. Since the display doesn't seem to have any affect on what gets sent to the server, I would pull this out and just leave the #Html.DropDownListFor() in the form:
#using (Html.BeginForm("VendorDetails", "VendorLookUp", FormMethod.Post, new { id = "vendorDetailsForm" }))
{
#Html.DropDownList("vendorId", new SelectList(Model.vendorList, "vendorId", "vendorName"))
}
<div class="margin-10-top" >
<table id= "VendorDetail" class="VendorDetail">
........ details of vendor.........
</table>
</div>
<script type='text/javascript'>
$(document).ready(function () {
$('#vendorId').change(function () {
$('#vendorDetailsForm').submit();
});
});
</script>
Edit
Take a look at this article about MVC's model binding for an idea of how vendorId gets injected from the submitted form. Basically, the binder will match property names with the name attribute (by default) to your model. In this case, our model is just an int.

tinymce in mvc 3 razor, Ajax.ActionLinks fail after first ajax call

I am using Tinymce inside an asp.net mvc 3 Razor application. An Ajax.ActionLink loads the tinymce editor via a call to a controller action named "GetContent". The GetContent method loads a text file from the file system. All is well. But, after I save the tinymce text via an $.ajax call, the Ajax.ActionLink no longer fires the controller method. In other words, something in the $.ajax post breaks the Ajax.ActionLink on the client so that it no longer calls the GetContent controller action.
Interestingly, the Ajax.ActionLink still loads the tinymce editor, but from the browser cache. In the example below I have 2 links "FileOne" and "FileTwo", which load two different text files. Before I call $.ajax the links load the file from disk. After I call $.ajax the links load the last "FileOne" or "FileTwo" from the browser cache.
This is the view. The $.ajax post occurs inside the tiny_mce_save_click() function, which is wired to the tinymce save button click:
#model TestTinyMCE.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#section JavaScript
{
<script src="#Url.Content("~/Scripts/tiny_mce/jquery.tinymce.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$().ready(function () {
init_tiny_mce();
});
function init_tiny_mce() {
$('textarea.tinymce').tinymce({
// Location of TinyMCE script
script_url: '/Scripts/tiny_mce/tiny_mce.js',
//javascript function called when tinymce save button is clicked.
save_onsavecallback: "tiny_mce_save_click",
encoding: "xml",
theme: "advanced",
plugins: "save",
theme_advanced_buttons1: "save",
theme_advanced_toolbar_location: "top"
});
}
function tiny_mce_save_click(tinyMceInstance) {
$.ajax({
type: 'POST',
url: '/Home/SaveContent',
data: $('form').serialize(),
success: function (data, status, xml) {
$('#results').html(data);
},
error: function (xml, status, error) {
$('#results').html(error);
}
});
return false;
}
</script>
}
#using (Html.BeginForm())
{
<ul>
#foreach (string fileName in Model.FileList)
{
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent" })</li>
}
</ul>
<div id="divContent">
#Html.Partial("GetContent", Model)
</div>
}
The partial view "GetContent" is:
#model TestTinyMCE.Models.HomeModel
#{
Layout = null;
}
<div id="divContent">
<fieldset id="fsContent">
<span id="results"></span><legend>Edit Content #Html.DisplayTextFor(m => m.FileName)</legend>
#Html.TextAreaFor(m => m.Content,
new Dictionary<string, object>{
{"class","tinymce"}, {"cols","80"}, {"rows","10"}}
)
#Html.HiddenFor(m => m.FileName)
</fieldset>
#if (#IsAjax)
{
<text>
<script type="text/javascript">init_tiny_mce();</script>
</text>
}
</div>
This is the controller. The GetContent method no longer gets called after the $.ajax post occurs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TestTinyMCE.Models;
namespace TestTinyMCE.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new HomeModel());
}
public ActionResult GetContent(HomeModel homeModel)
{
if (!string.IsNullOrEmpty(homeModel.FileName))
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
if (!System.IO.File.Exists(physicalPath))
homeModel.Content = string.Format("The file '{0}' does not exist.", physicalPath);
else
homeModel.Content = System.IO.File.ReadAllText(physicalPath);
}
return View(homeModel);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult SaveContent(HomeModel homeModel)
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
System.IO.File.WriteAllText(physicalPath, homeModel.Content);
ViewBag.Result = "The file was successfully saved.";
return View();
}
}
}
The problem is broswer caching. To prevent caching on the Ajax.ActionLink you must add AjaxOption HttpMethod = "POST". In the above code change ActionLink to
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent", HttpMethod = "POST" })</li>.
See http://forums.asp.net/t/1681358.aspx?Disable+cache+in+Ajax+ActionLink+extension+method+in+asp+net+MVC

Resources