Razor is the new syntax/view engine that replaces the old Web Forms (ASPX) syntax/view engine. It is simpler and easier to read.
Razor pages have file extension *.cshtml or *.vbhtml, depending on which language you use. These notes are all based on the C# (.cshtml) syntax.
Razor views are templates that are filled in with data to produce web pages.
Literal strings are plain HTML. C# code starts with an "@".
@{ var message = "Good morning"; }
<p>Message says @message.</p>
@{ var student = new Student("Bob", 19); }
<p>Student name is @student.Name and age is @student.Age.</p>
<p>Student is in @student.CountClasses() classes.</p>
The Model and the ViewBag are both available in the view.
@* comments here *@
@{
//more comments
/* multiline comments */
}
These lines are executed. Nothing is automatically added to the output.
@{
//proper C# code;
}
These results are added directly to the output.
Implicit Code Expressions
Plain text @... Plain text
Explicit Code Expressions
Plain text @(...) Plain text
Email Address: the Razor Engine assumes text formatted as "a@b.c" is an email address, and will not implicitly interpret it as a code expression.
Escape "@" (at sign) with another "@".
@@Plain text
Explicitly specify string literals:
@:Plain text
The new view dialog in Visual Studio:
View engine: Razor
Strongly-Typed View: you can set the model type for the view
Scaffold Template: the view can be generated with some default code to get you started
"~" (tilda) at the beginning of an HTML path means the root of the web page.
Ex: "~/Views/Contact/Index.cshtml"
If you're path does not start with a "~", it is an absolute path.
Using relative paths is highly recommended
In Visual Studio, right-click in the action method, select "Add View".
Or, in the action method, type Ctrl-M + Ctrl-V.
This will add a view to Project/Views/<controller name>/<action name>.cshtml
File ~/Views/ViewStart.cshtml will run before each view, by default.
This is where the environment variable "Layout" is set to its default value of "~/Views/Shared/_Layout.cshtml".
The environment variable "Layout" is a string filepath to the layout that should be used with the current view.
Use no layout:
@model MyModel
@{
Layout = null;
//or
Layout = "";
}
The Razor Engine can usually tell when to switch between C# code and HTML in these code blocks.
When it sees an HTML tag, it knows to switch to HTML interpretation.
If you try to start with plain text, it will give an error:
@for(var i=0; i<10; i++)
{
Plain Text causes error
@:Plain Text does not cause error
<p>Line @i</p>
}
<body>
@for(var i=0; i<10; i++)
{
<p>Line @i</p>
}
</body>
<body>
@foreach(var item in list)
{
<p>Item @item.Name</p>
}
</body>
<body>
@if(age < 50)
{
<p>the age is @age</p>
}
</body>
<body>
@if(hour < 12)
{
<p>It is morning</p>
}
else
{
<p>It is afternoon</p>
}
</body>
<body>
@switch(x)
{
case 0: y = "text"; break;
case 1: y = "other text"; break;
default: y = "";
}
<p>@y</p>
</body>
@Helpers are functions that do not return a value. They output directly to the page.
@helper DisplayPrice(decimal price)
{
if(price == 0)
{
<span>Free!</span>
}
else
{
<span>@String.Format("{0:C2}", price)</span>
}
}
@foreach(Product x in list)
{
<p>@x.Name: @DisplayPrice(x.Price)</p>
}
If you call a helper from within a helper, always use the "@" symbol. Even if the syntax highlighter indicates that the method call is recognized as such, it'll get skipped.
@helper A()
{
@B();
}
@helper B()
{
@:Reached B
}
Note that you cannot code-step through a @helper (at least in VS2015), but the code does run.
Functions do return values.
@functions
{
string FormatPrice(decimal price)
{
if(price == 0)
return "Free!";
return String.Format("{0:C2}", price);
}
}
@foreach(Product x in list)
{
<p>@x.Name: @FormatPrice(x.Price)</p>
}
You can create global helpers and functions (usable in any View) by adding them to a *.cshtml file in folder Project/App_Code.
Project/App_Code/GlobalHelpers.cshtml
@helper DisplayAuthor(Author author) {
<a href="@Url.Action("Details", "Author")">@author.LastName</a>
}
@functions {
public static string SayTest() {
return "test";
}
}
Any view file:
@GlobalHelpers.DisplayAuthor(book.Authors.First())
@GlobalHelpers.SayTest()
Note that the helper/function is accessed based the name of the file it is in.
When RenderSection("SectionName") is called, this is the what is rendered:
@section SectionName {
//stuff
}
A strongly-typed view expects a particular data type to be passed in as its model. Declare the data type at the top of the file, like this:
@model DataType
The variable "Model" is automatically available within your view.
When using a lambda expression, do not use "Model". Use something like "model" or "m" instead. It can cause problems to step on the name of the actual model variable.
String literals are added to the output as they are.
Some plain text.
<p>Some HTML tags.</p>
Output string literals:
@Response.Write("text")
@Html.Raw("text")
Everything the HtmlHelper outputs is HTML encoded. This protects you from cross-site scripting attacks.
HtmlHelper methods automatically operate on the Model.
Generate a link to the specified action:
@Html.ActionLink(text, actionName)
@Html.ActionLink(text, actionName, new { id = model.Id }) //pass additional information
@Html.ActionLink(text, actionName, controllerName, new { id = model.Id }, null) //link to a different controller
Generate the display name of the model field based on the field's attributes:
@Html.DisplayNameFor(model => model.Name)
Generate the value of the model field, correctly formatted based on field type:
@Html.DisplayFor(model => model.Name)
@using(Html.BeginForm())
{
<input type="submit" value="Submit" />
}
This form will default to posting back to the current URL, with current URL parameters included.
"BeginForm" has nothing to dispose at the end of this "using" block, it just knows that's when to close the form tag.
BeginForm overloads:
@using(Html.BeginForm("Action")) { }
@using(Html.BeginForm("Action", "Controller")) { }
@using(Html.BeginForm("Action", "Controller", FormMethod.Post)) { }
@using(Html.BeginForm("Action", "Controller", FormMethod.Post, new { id="formId", name="formName", data_custom="becomes data-custom attribute" })) { }
Generate a label for the model field, based on field attributes:
@Html.LabelFor(model => model.Name)
Generate an input control for the model field, correctly formatted based on field type:
@Html.EditorFor(model => model.Name)
Generate a validation message section for the model field, based on field attributes:
@Html.ValidationMessageFor(model => model.Name)
Generate a validation section for messages not associated with a particular field:
@Html.ValidationSummary(true)
Passing a "false" argument means that every validation message will be displayed in this section.
Generate a hidden form field for the model field:
@Html.HiddenFor(model => model.Name)
In this example, there is information you want to display as part of your global Layout, regardless of what Model the View is using.
"Html.Action" will generate an entirely new Request that will result in a new Partial View and Model. The results of all this will be included in that View that makes the SubRequest.
This all happens during the current HTTP Request - there is not a new HTTP Request sent from the user's browser.
<!-- In View -->
<div>
@Html.Action("MyAction", "MyController")
</div>
//in controller
[ChildActionOnly] //this action cannot be called directly with an HTTP Request
public ActionResult MyAction()
{
var model = GetModel();
return PartialView("partialViewName", model);
}
Returns partial view result as a string:
<tag>
@Html.Partial("ViewName", model)
</tag>
Returns nothing, sends results directly to response object:
<tag>
@{
Html.RenderPartial("ViewName", model);
}
</tag>
AJAX provides asynchronous HTTP Requests.
Razor includes an AJAX helper called "Ajax".
All ASP.Net MCV Ajax uses the "Unobtrusive JavaScript" approach to JavaScript.
This means that javascript is not mixed into HTML elements (ex: <a onclick="handleClick()" />).
It also means that even if the client has javascript disabled in their browser, you're web page will still function.
Example of form tag generated by "Ajax.BeginForm":
<!-- these are "Data Dash Attributes" -->
<!-- Data Dash Attributes are private data for the application, and are ignored by the browser -->
<form action="/" data-ajax="true" data-ajax-method="get" data-ajax-mode="replace" data-ajax-update="#countryList" method="post">
</form>
If javascript is enable, this form will have events attached to it, based on the "data-ajax" attributes, so that this request will be sent as an AJAX request.
If javascript is not enabled, this request will still run as a normal form submission, and the entire page will reload with the new data.
Form validation works the same way.
Generates a link for an asynchronous request that will update the screen instead of navigating to a new page.
@Ajax.ActionLink(...)
Example of using AJAX to load search results without reloading the entire page:
//inside a *.cshtml page
@using(Ajax.BeginForm(new AjaxOptions {
HttpMethod="get",
InsertionMode=InsertionMode.Replace, //the results of this request will replace a section of the current HTML page
UpdateTargetId="countryList" //the HTML element whose contents will be replaced has id "countryList"
}))
{
<input type="search" name="searchTerm"/>
<input type="submit" value="Search by Name"/>
}
<div id="countryList">
@foreach(MyModel item in Model)
{
//render item
}
</div>
Because the AJAX results will be inserted directly into the current page, make sure that the AJAX results come from a Partial View.
Distinguish AJAX request within controller:
//in controller
public ActionResult MyMethod(MyModel model)
{
...
if(Request.IsAjaxRequest()) //checks an HTTP Header
{
return PartialView("_MyView", model);
}
return View(model);
}
Make a strongly-typed view by specifying the expected model.
//at top of *.cshtml view file
@model MyProject.Models.MyModel
//to access the model
<h1>@Model.MyAttribute</h1>
Simple, a method that returns a string. They are used to abstract standard HTML tags.
Assembly: System.Web.Mvc.Html
Html.ActionLink(...)
Html.BeginForm(...) and Html.EndForm(...)
Html.Hidden(...)
Html.Password(...)
Html.CheckBox(...) and Html.CheckBoxFor(...)
Html.DropDownList(...) and Html.DropDownListFor(...)
Html.ListBox(...) and Html.ListBoxFor(...)
Html.RadionButton(...) and Html.RadioButtonFor(...)
Html.TextArea(...) and Html.TextAreaFor(...)
Html.TextBox(...) and Html.TextBoxFor(...)
The "xFor" versions are strongly-typed (updates when the object changes), the others are weakly-typed. Both render to the same HTML.
Weakly typed:
<label for="FirstName">First Name:</lable>@Html.TextBox("FirstName")
Strongly typed:
@model Student
<label for="FirstName">First Name:</lable>@Html.TextBox(m => m.FirstName)
/*
In the lambda, "m" is the model, the Student object that was passed to this view.
Do not call it "model" in the lambda, the variable name will conflict with the implicit "model" variable.
*/
You can make your own Helpers with static methods.
namespace MyApp.Helpers
{
public static class MyHelpers
{
public static string Label(string target, string text)
{
return String.Format("<label for='{0}'>{1}</label>", target, text);
}
}
}
...
@MyHelpers.Label("target", "text")
You can also use extension methods on the Html class.
namespace MyApp.Helpers
{
public static class LabelExtensions
{
public static string MyLabel(this HtmlHelper helper, string target, string text)
{
return String.Format("<label for='{0}'>{1}</label>", target, text);
}
}
}
...
@Html.MyLabel("target", "text")