Is it possible to include a section defined in partial file? If so, how?
I have a partial called MetaTags.cshtml that contains a section MetaTags. I use a section because I want views to be able to override this with custom tags when needed - for example; for a blog post page the user can add custom facebook metatags. The section is in a partial to keep the _Layout.cshtml more manageable and for code separation.
The following usage doesn't work: _Layout.cshtml
<head>
#RenderSection("MetaTags", required: true)
<title>#ViewBag.Title - My ASP.NET Application</title>
...
</head>
If you are setting the section in View (not partial view), it should work.
There is no way of using section in partial, but you can use other way such as ViewBag or Custom Html helper to achieve this.
Below is a sample of ViewBag method:
in your _Layout
#Html.Partial("partial", new ViewDataDictionary { {"vb", ViewBag}})
#if (ViewBag.MetaInfo != null)
{
var metaDict = ViewBag.MetaInfo as IDictionary<string, string>;
if(metaDict != null)
{
foreach (var item in metaDict)
{
<meta name="#item.Key" content="#item.Value" />
}
}
}
</head>
then in partial view:
#{
ViewBag.LayoutPageVB = ((dynamic)ViewData["vb"]);
var metaDict = new Dictionary<string, string>();
metaDict.Add("title", "c");
ViewBag.LayoutPageVB.MetaInfo = metaDict;
}
you will get meta tag title being c
personally, I think this is not a good approach, and it is not the response of partial to update layout, but it is one way of achieving what you need.
Related
This question might be very basic but i'm very new to C#.I am returning list data from controller without using model. I want to use this and want to create a table.
// Mycontroller
public ActionResult Applied( string id)
{
try {
List<GrindersForJob> Grinders = JobAppliedRepository.GetgrindersByJobId(id);
return View(Grinders);
} catch (Exception ex) {
return View();
}
}
cshtml file =
#model IEnumerable<GrindersForJob>
<html>
<head>
<title>
</title>
</head>
<body>
#foreach(GrindersForJob item in Model)
{
<span>
#item.YourColumnName
</span>
}
</body>
</html>
Just send the list to your view
At the top of the view add
#model IEnumerable<GrindersForJob>
Now you can iterate over this list in the view using foreach loop
If you want to use a list for a table why don't you just pass it in as a table?
As already mentioned #model IEnumerable will take your List
But if you need a table you can pass a DataTable to your view.
#model System.Data.DataTable
Why sometimes #ViewBag.Title contains the right page title while #Page.Title is null? We are debugging our View/Layout code and we noticed this difference.
Thanks.
When your are using asp.Net MVC you have a few tools you can use to get data to your page.
The model you send to the view.
The ViewBag.
TempData.
Cookies.
Session.
Each one of theese has their own use cases
The example we will be using is a basic
List and update View
For a collection of companies
The Model
The model Should be used whenever you have a defined dataset being sent to a view and should contain the primary data for the page and usally a model is specific to a page or controller.
The ViewBag
The ViewBag should be used whenever you need to send general data to a page that is not defined on the model or data that is used across your view tree.
Temp Data
Temp Data is as the name states storage for temporary data this should be used in cases where you are unsure if the data will reach its destination or when you want to pass data between actions without adding parameters.
Cookies
Cookies are like Global variables the host data across your entire application.
Session
Sessions like cookies are global variables but differ in their lifetime sessions can be used for things like Storing form data between requests dont use session to do this rather use something like http://garlicjs.org/
Lets look at the example
We have the following ViewModel
public class Company
{
public int? id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
The layout page looks like this
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
//This looks for the Title property on the ViewBag and inserts it in the title tags
<title>#ViewBag.Title</title>
</head>
<body>
#Html.Action("Header", "PageElements")
#RenderBody()
</body>
</html>
The Header Action on the PageElements Contoller looks like this
public PartialViewResult Header()
{
ViewBag.CurrentLink = HttpContext.Current.Request.Cookies["CurrentLink"];
return PartialView();
}
The Header Partial View looks like this
<header>
<ul>
<li class="#(ViewBag.CurrentLink == "Home" ? "Current" : "")">
#Html.ActionLink("Home", "Index", "Home")
</li>
<li class="#(ViewBag.CurrentLink == "Companies" ? "Current" : "")">
#Html.ActionLink("Companies", "Index", "Companies")
</li>
</ul>
</header>
The controller action for update looks like this
public ViewResult Update(int id)
{
//Gets a company from the database
var model = db.GetCompany(id);
//Sets The Title property on the ViewBag to Update : CompanyName
ViewBag.Title = String.Format("Update : {0}", model.Name);
//Sends the company to the view
return View(model);
}
The update view looks like this
#model Application.Models.Company
#{
Layout = "_layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.HiddenFor(model => Model.id)
#Html.LabelFor(model => Model.Name)
#Html.TextBoxFor(model => Model.Name)
#Html.LabelFor(model => Model.Description)
#Html.TextAreaFor(model => Model.Description)
<button>Submit</button>
}
The Post Action for Update looks like this
[HttpPost]
public ActionResult Update(Company model)
{
//Attempts to update the model
if (model.Update())
{
//Set the message to update succeeded
TempData["Message"] = String.Format("{0} Successfully updated");
}
else
{
//Set the message to update failed
TempData["Message"] = String.Format("{0} filed to update");
}
return RedirectToAction("Index");
}
The Controller action for companies looks like this
public ViewResult Index()
{
//Index is the entrypoint for companies so we set the currentlink to companies while we are in companies
HttpContext.Current.Request.Cookies["CurrentLink"] = "Companies";
//Gets the success or failure message from the temp data
ViewBag.Message = TempData["Message"];
//Sets The Title property on the ViewBag to List of companies
ViewBag.Title = "List of companies";
var model = db.GetAllCompanies();
return View(model);
}
The view for the list looks like this
#model IEnumrable<Application.Models.Company>
#{
Layout = "_layout.cshtml";
}
<span>#ViewBag.Message</span>
<ul>
#foreach (var company in Model)
{
<li>#company.Name : #company.Description #Html.AnchorLink("Edit", "Update", new { company.id}) </li>
}
</ul>
Lets discuss how the flow of this application works
The Index Action gets triggered on companies.
We set the current link to companies in a Cookie
Then we set the Title in the ViewBag
We check the TempData for messages and put them into the ViewBag
Then we put the collection of companies into a Model and send it to the page.
Now razor kicks in and starts renders the page first
Then the layout renders and gets the Title value from the ViewBag
Then the header partial view renders and gets the currentlink value from a Cookie
We click the update button for a company
The update action runs
Gets the company from a database and sends it to the view in a Model
We change the title to update in the ViewBag
The page renders first and puts the company data on the form, from the Model
Then the layout renders and Gets the title value from the ViewBag
Then the header renders and gets the currentlink from a cookie (the value is still companies we did not have to change it)
Then we submit the form and add a message to the TempData
now we are back at the company list and we can get the success or failure message from the TempData
We used the Model to send Specific data to the view.
We used the ViewBag to send General data to the view and the layout.
We used TempData to pass data between actions without using parameters.
We used Cookies to Store data that wouldn't change for a while.
The correct is using ViewBag.Title, because it is on the context of your ViewBag property of your View while Page.Title is something that comes from asp.net webforms. That is the reason why Page.Title is null, there is no context for it.
How do you achieve this?
This View is automatically generated.
When I manual add a View for example: Contact view with Index.cshtml file in it.
I can modify this view by writing a controller Contact.
public class ContactController : Controller
{
public ActionResult Index()
{
#ViewBag.Test = "this text will be used in my Contact View";
return View();
}
}
So in my contact view i can do like this
<p> #Viewbag.Test </p>
And the text will be displayed.
But how do you achieve this for my _Layout.cshtml file in my Shared View?
I tried the same by adding a SharedController but not working this way
You can't have a controller for _Layout.cshtml. This file is used for any view's layout. For example, look at the _ViewStart.cshtml file in your Views folder:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
This tells basically all controllers to use that layout as a wrapper around the view returned by your controller actions.
Your _Layout.cshtml file already has a hint about one way to populate it with values:
<head>
...
<title>#ViewBag.Title</title>
...
</head>
If you do the following in a view, it will be rendered in the head/title section of the _Layout.cshtml file:
#{
#ViewBag.Title = "Home";
}
You don't need a controller for _Layout. Your contact view is added to _Layout to create one complete view. So you can use any of your ViewBag properties from your contact controller inside of _Layout also. _Layout has access to the same variables as your contact view.
Specifically, in your example:
public class ContactController : Controller
{
public ActionResult Index()
{
#ViewBag.Test = "this text will be used in my Contact View";
return View();
}
}
ViewBag.Test will also be accessible in _Layout the same way it is in your contact view.
I have a partial view and int it, there is no trace of any inheritance from any layout. But whenever I want to use it (render it) inside a view, the layout gets repeated once for the view, and once for the partial view. This post suggests to create an empty layout. But I think this is the workaround. Is there anyway to stop loading layout (master layout) for partial views. I don't understand, why when there is no code to use the master layout, why should it get loaded. It's just like creating a page in ASP.NET and seeing that it inherits from a master page without having <%# Master ... directive.
This is my partial view:
#* Recursive category rendering *#
#using Backend.Models;
#{
List<Category> categories = new ThoughtResultsEntities().Categories.ToList();
int level = 1;
}
#RenderCategoriesDropDown(categories, level)
#helper RenderCategoriesDropDown(List<Category> categories, int level)
{
List<Category> rootCategories = categories.Where(c => c.ParentId == null).ToList();
<select id='categoriesList' name='categoriesList'>
#foreach (Category rootCategory in rootCategories)
{
<option value='#rootCategory.Id' class='level-1'>#rootCategory.Title</option>
#RenderChildCategories(categories, level, rootCategory.Id);
}
</select>
}
#helper RenderChildCategories(List<Category> categories, int level, int parentCategoryId)
{
string padding = string.Empty;
level++;
List<Category> childCategories = categories.Where(c => c.ParentId == parentCategoryId).ToList();
foreach (Category childCategory in childCategories)
{
<option value='#childCategory.Id' class='level-#level'>#padding.PadRight(level, '-') #childCategory.Title</option>
#RenderChildCategories(categories, level, childCategory.Id);
}
level--;
}
I was able to reproduce this issue when rendering partial pages through ajax calls. The
return View("partialpage")
would always accompany with layout. I have overridden this behavior by explicitly calling
return PartialView("partialpage")
The layout might be coming from your ~/Views/_ViewStart.cshtml
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
You could try overriding this in your partial view like:
#{
Layout = null;
}
I would like my views to be able to specify a class for the <body> tag, which lies in my master page.
My first take was to do something like this:
<asp:Content ContentPlaceHolderID="BodyClassContent" runat="server">
view-item</asp:Content>
However, that would require this in the master page, which doesn't work:
<body class="<asp:ContentPlaceHolder ID="BodyClassContent" runat="server" />">
Any solutions to this?
In the layout you can do this on the body tag:
<body #RenderSection("BodyAttributes", false)>
and then in your view you can do this:
#section BodyAttributes {
id="login" class="login"
}
Edit: I also had to do this working with VB.NET and WebForms today and found a handy link for achieving the equivalent
I would suggest a different approach.
You create an hierachy of view models, starting with the MasterModel. When you instantiate a view object, you pass a body class to it.
public class MasterModel
{
string BodyCss { get; set; }
public MasterModel (string bodyCss)
{
BodyCss = bodyCss;
}
}
public class MyView1Model : MasterModel
: base ("body-view1")
{
}
Then in your master view which should be strongly typed to MasterView:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<MasterModel>" %>
you just write:
<body class="<%= Model.BodyCss %>"></body>
You could also specify the body id attribute in the View which wishes to set it:
#{
ViewBag.Title = "Test";
ViewData["BodyID"] = "test";
Layout = "~/Views/Shared/_Layout.cshtml";}
This helps to decouple the view from the controller, the controller (and/or view model) does not need to know about the id attribute of the body tag.
Set the id of the body tag in the master page like so:
<body id="#ViewData["BodyID"]">
Why don't you do in your masterpage:
<body class="<%=ViewData["bodyClass"].toString()%>">
and then set ViewData["bodyClass"] in your Controller actions? That should be equivalent...