ASP.NET MVC Lesson 8. View, Razor, Error Page
- Tutorial
The purpose of the lesson . Learn to draw data in html, using Razor. Helpers. PageableData Dynamic forms. RedirectToLogin, RedirectToNotFoundPage. Error page. RssActionResult.
So, let's see how the part of the View is arranged.
In the controller, all action methods return an ActionResult type. And to display the result, we use:
The main parameters of the View can be:
The choice of which View to use is as follows:
let's start the study.
When creating a View, there is a choice between two engines: ASPX and Razor. We will not use the first in the future, so let's talk about Razor.
ASPX was a cumbersome tag engine
Razor uses the design
Inside {} there are tags - this is a marker of the fact that this is a template. For simple code execution inside the template, we use the @ {code} structure; for the correct output of data inside attributes or text, the construction is @ (string result):
To display non-tagged text, you need to use pseudo-tags:
To output html text, either MvcHtmlString should be returned, or use the Html .Raw (html-string-value) construct , otherwise the text will be displayed with escaped tags.
Consider the pagination of the table from the database. Let's analyze:
Create the Generic class PageableData (/Models/Info/PageableData.cs):
By default, the number of displayed values on the page is 20, but we can change this parameter in the constructor. We pass IQueryable and calculate the number of pages CountPage. Using PageNo, select the page:
In the controller we use:
In View we use this class:
Run, check (http: // localhost / User)
To continue, we will generate more data (just ctrl-c, ctrl-v in the table in Server Explorer)
Let's move on to creating the Helper paginator, which will give us the opportunity to scroll through this list.
Since we use bootstrap, we will also make a paginator based on it. In code, it looks like this:
We are only interested in the inside.
Helper is created as Extension for the System.Web.Mvc.HtmlHelper class. The plan is as follows:
The code will look like this:
Add namespace LessonProject.Helper to the declarations in the View. There are two ways to do this:
Add the paginator in the View:
Note the design.
This is the delegate that returns the link to the page. And Url.Action () - forms a link to the page / User / Index with parameter page = x.
Here's what happened (reduced the amount of output per page to 5 so that more pages are formed):
The next step to viewing the data is to create a search. The search will be simple, by coincidence of the substring in one of the data fields. The input parameter is searchString.
Create a class
The basis
So, let's see how the part of the View is arranged.
In the controller, all action methods return an ActionResult type. And to display the result, we use:
return View(modelData);
The main parameters of the View can be:
- Name, usually it matches the name of the action method. In case you need to call another named View, then the construction is used
return View(“ViewName”, modelData).
- Data to display in View. Optional parameter. When passed to the View, this data object will be denoted by Model. To bind a data type, the View specifies the expected data type:
@model LessonProject.Model.User
- Layout Optional parameter. When this parameter is specified, a container page will be found on this line and it will be called. The view part will be processed by the RenderBody () method
The choice of which View to use is as follows:
- Searched in the folder / Areas / [Area] / Views / [ControllerName] /
- Searched in / Areas / [Area] / Views / Shared /
- Searched in the folder / Views / [ControllerName] /
- Searched in the folder / Views / Shared /
let's start the study.
Razor
When creating a View, there is a choice between two engines: ASPX and Razor. We will not use the first in the future, so let's talk about Razor.
ASPX was a cumbersome tag engine
<% %>
for executing code and <%: %>
outputting data. Razor uses the design
@Model.Name
. Those. everything that starts with @
translates into the mode of either code execution or data output @foreach() {…}
, or @if() { … } else { … }
:@if (Model.Any())
{
Список
}
@foreach (var role in Model)
{
@role.ID
@role.Name
@role.Code
}
Inside {} there are tags - this is a marker of the fact that this is a template. For simple code execution inside the template, we use the @ {code} structure; for the correct output of data inside attributes or text, the construction is @ (string result):
@{
int i = 0;
}
@foreach (var role in Model)
{
@role.ID
@role.Name
@role.Code
i++;
}
To display non-tagged text, you need to use pseudo-tags:
@foreach (var role in Model)
{
@role.Name,
}
To output html text, either MvcHtmlString should be returned, or use the Html .Raw (html-string-value) construct , otherwise the text will be displayed with escaped tags.
PageableData
Consider the pagination of the table from the database. Let's analyze:
- The controller should receive in the parameters the value of the page that we will output
- By default, this will be the first page.
- In conclusion, we should know:
- List of database items to display
- Number of pages
- Current page
Create the Generic class PageableData (/Models/Info/PageableData.cs):
public class PageableData where T : class
{
protected static int ItemPerPageDefault = 20;
public IEnumerable List { get; set; }
public int PageNo { get; set; }
public int CountPage { get; set; }
public int ItemPerPage { get; set; }
public PageableData(IQueryable queryableSet, int page, int itemPerPage = 0)
{
if (itemPerPage == 0)
{
itemPerPage = ItemPerPageDefault;
}
ItemPerPage = itemPerPage;
PageNo = page;
var count = queryableSet.Count();
CountPage = (int)decimal.Remainder(count, itemPerPage) == 0 ? count / itemPerPage : count / itemPerPage + 1;
List = queryableSet.Skip((PageNo - 1) * itemPerPage).Take(itemPerPage);
}
}
By default, the number of displayed values on the page is 20, but we can change this parameter in the constructor. We pass IQueryable and calculate the number of pages CountPage. Using PageNo, select the page:
List = queryableSet.Skip((PageNo - 1) * itemPerPage).Take(itemPerPage);
In the controller we use:
public class UserController : DefaultController
{
public ActionResult Index(int page = 1)
{
var data = new PageableData(Repository.Users, page, 30);
return View(data);
}
…
In View we use this class:
@model LessonProject.Models.Info.PageableData
@{
ViewBag.Title = "Users";
Layout = "~/Areas/Default/Views/Shared/_Layout.cshtml";
}
Users
@foreach (var user in Model.List)
{
@user.ID
@user.Email
@user.AddedDate
}
Run, check (http: // localhost / User)
To continue, we will generate more data (just ctrl-c, ctrl-v in the table in Server Explorer)
Let's move on to creating the Helper paginator, which will give us the opportunity to scroll through this list.
Helper (PagerHelper)
Since we use bootstrap, we will also make a paginator based on it. In code, it looks like this:
We are only interested in the inside.
Helper is created as Extension for the System.Web.Mvc.HtmlHelper class. The plan is as follows:
- Output Prev (make active if necessary)
- Display links to the first three pages 1, 2, 3
- Print ellipsis, if necessary
- Display the active link of the current page
- Print ellipsis, if necessary
- Display the last three pages
- Print Next (make active if necessary)
- Enclose everything in ul and print as MvcHtmlString
The code will look like this:
public static MvcHtmlString PageLinks(this HtmlHelper html, int currentPage, int totalPages, Func pageUrl)
{
StringBuilder builder = new StringBuilder();
//Prev
var prevBuilder = new TagBuilder("a");
prevBuilder.InnerHtml = "«";
if (currentPage == 1)
{
prevBuilder.MergeAttribute("href", "#");
builder.AppendLine("" + prevBuilder.ToString() + " ");
}
else
{
prevBuilder.MergeAttribute("href", pageUrl.Invoke(currentPage - 1));
builder.AppendLine("" + prevBuilder.ToString() + " ");
}
//По порядку
for (int i = 1; i <= totalPages; i++)
{
//Условие что выводим только необходимые номера
if (((i <= 3) || (i > (totalPages - 3))) || ((i > (currentPage - 2)) && (i < (currentPage + 2))))
{
var subBuilder = new TagBuilder("a");
subBuilder.InnerHtml = i.ToString(CultureInfo.InvariantCulture);
if (i == currentPage)
{
subBuilder.MergeAttribute("href", "#");
builder.AppendLine("" + subBuilder.ToString() + " ");
}
else
{
subBuilder.MergeAttribute("href", pageUrl.Invoke(i));
builder.AppendLine("" + subBuilder.ToString() + " ");
}
}
else if ((i == 4) && (currentPage > 5))
{
//Троеточие первое
builder.AppendLine("... ");
}
else if ((i == (totalPages - 3)) && (currentPage < (totalPages - 4)))
{
//Троеточие второе
builder.AppendLine("... ");
}
}
//Next
var nextBuilder = new TagBuilder("a");
nextBuilder.InnerHtml = "»";
if (currentPage == totalPages)
{
nextBuilder.MergeAttribute("href", "#");
builder.AppendLine("" + nextBuilder.ToString() + " ");
}
else
{
nextBuilder.MergeAttribute("href", pageUrl.Invoke(currentPage + 1));
builder.AppendLine("" + nextBuilder.ToString() + " ");
}
return new MvcHtmlString("" + builder.ToString() + "
");
}
Add namespace LessonProject.Helper to the declarations in the View. There are two ways to do this:
- In the View itself
@using LessonProject.Helper;
- In Web.config (recommended)
… +
Add the paginator in the View:
@Html.PageLinks(Model.PageNo, Model.CountPage, x => Url.Action("Index", new {page = x}))
Note the design.
x => Url.Action("Index", new {page = x})
This is the delegate that returns the link to the page. And Url.Action () - forms a link to the page / User / Index with parameter page = x.
Here's what happened (reduced the amount of output per page to 5 so that more pages are formed):
SearchEngine
The next step to viewing the data is to create a search. The search will be simple, by coincidence of the substring in one of the data fields. The input parameter is searchString.
public ActionResult Index(int page = 1, string searchString = null)
{
if (!string.IsNullOrWhiteSpace(searchString))
{
//тут Поиск
return View(data);
}
else
{
var data = new PageableData(Repository.Users, page, 5);
return View(data);
}
}
Create a class
SearchEngine
that takes values IQueryable, и строку поиска, а возвращает данные по поиску (/Global/SearchEngine.cs):
Первым делом, создадим классы по очистке строки запроса, никаких тегов и убираем разделители типа [,], {,}, (,):
///
/// The regex strip html.
///
private static readonly Regex RegexStripHtml = new Regex("<[^>]*>", RegexOptions.Compiled);
private static string StripHtml(string html)
{
return string.IsNullOrWhiteSpace(html) ? string.Empty :
RegexStripHtml.Replace(html, string.Empty).Trim();
}
private static string CleanContent(string content, bool removeHtml)
{
if (removeHtml)
{
content = StripHtml(content);
}
content =
content.Replace("\\", string.Empty).
Replace("|", string.Empty).
Replace("(", string.Empty).
Replace(")", string.Empty).
Replace("[", string.Empty).
Replace("]", string.Empty).
Replace("*", string.Empty).
Replace("?", string.Empty).
Replace("}", string.Empty).
Replace("{", string.Empty).
Replace("^", string.Empty).
Replace("+", string.Empty);
var words = content.Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var sb = new StringBuilder();
foreach (var word in
words.Select(t => t.ToLowerInvariant().Trim()).Where(word => word.Length > 1))
{
sb.AppendFormat("{0} ", word);
}
return sb.ToString();
}
Создаем поиск:
public static IEnumerable Search(string searchString, IQueryable source)
{
var term = CleanContent(searchString.ToLowerInvariant().Trim(), false);
var terms = term.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var regex = string.Format(CultureInfo.InvariantCulture, "({0})", string.Join("|", terms));
foreach (var entry in source)
{
var rank = 0;
if (!string.IsNullOrWhiteSpace(entry.Email))
{
rank += Regex.Matches(entry.Email.ToLowerInvariant(), regex).Count;
}
if (rank > 0)
{
yield return entry;
}
}
}
В первой строке очищаем строку запроса. Создаем regex для поиска. В данном случае, мы ищем только в поле Email у пользователей.
Как это работает:
- При вводе слова в поиске, например, «cher [2]», вначале убираем разделители, получаем «cher 2».
- Создаем regex = (cher|2).
- Просматриваем весь список, переданный через
IQueryable
Если есть совпадение, то выносим его в IEnumerable - yield return entry