Fix pagination in ASP.NET Core - c#

I am trying to fixed pagination in my project. But I do not get the correct result. I want to make this result 123 ... 12
PS I study with book Adam Freeman Pro ASP.NET Core MVC 2.
Project in GitHub https://github.com/JasARGHUN/StoreProject-
Pagin model PagingInfo.cs:
public class PagingInfo
{ public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages =>
(int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); }
Descriptor class PageLinkTagHelper.cs
[HtmlTargetElement("div", Attributes = "page-model")]
public class PageLinkTagHelper : TagHelper
{
private IUrlHelperFactory urlHelperFactory;
public PageLinkTagHelper(IUrlHelperFactory helperFactory)
{
urlHelperFactory = helperFactory;
}
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public PagingInfo PageModel { get; set; }
public string PageAction { get; set; }
[HtmlAttributeName(DictionaryAttributePrefix = "page-url-")]
public Dictionary<string, object> PageUrlValues { get; set; } = new Dictionary<string, object>();
public bool PageClassesEnabled { get; set; } = false;
public string PageClass { get; set; }
public string PageClassNormal { get; set; }
public string PageClassSelected { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
TagBuilder result = new TagBuilder("div");
for (int i = 1; i <= PageModel.TotalPages; i++)
{
TagBuilder tag = new TagBuilder("a");
PageUrlValues["productPage"] = i;
tag.Attributes["href"] = urlHelper.Action(PageAction, PageUrlValues);
if (PageClassesEnabled)
{
tag.AddCssClass(PageClass);
tag.AddCssClass(i == PageModel.CurrentPage
? PageClassSelected : PageClassNormal);
}
tag.InnerHtml.Append(i.ToString());
result.InnerHtml.AppendHtml(tag);
}
output.Content.AppendHtml(result.InnerHtml);
}
}
Pagination view model ProductsListViewModel
public class ProductsListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentCategory { get; set; }
}
Controller ProductController.cs
public class ProductController : Controller
{
private IProductRepository _repository;
public int PageSize = 4;
public ProductController(IProductRepository repository)
{
_repository = repository;
}
public ViewResult List(string category, int productPage = 1) =>
View(new ProductsListViewModel
{
Products = _repository.Products
.Where(p => category == null || p.Category == category)
.OrderBy(p => p.ProductID)
.Skip((productPage - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = productPage,
ItemsPerPage = PageSize,
TotalItems = category == null ?
_repository.Products.Count() :
_repository.Products.Where(e =>
e.Category == category).Count()
},
CurrentCategory = category
});}
View List.cshtml
#model ProductsListViewModel
#foreach (var p in Model.Products)
{
#await Html.PartialAsync("ProductSummary", p);
}
<div page-model="#Model.PagingInfo" page-action="List" page-classes-enabled="true"
page-class="btn" page-class-normal="btn-secondary"
page-class-selected="btn-primary" page-url-category="#Model.CurrentCategory"
class="btn-group pull-right m-1"></div>
Navigation menu Navigation.cshtml
#model IEnumerable<string>
<a class="btn btn-block btn-secondary border mb-1"
asp-action="List"
asp-controller="Product"
asp-route-category="">Home</a>
<div>
#foreach (string category in Model)
{
<a class="btn btn-sm btn-block border #(category == ViewBag.SelectedCategory ? "btn-info": "btn-light")"
asp-action="List"
asp-controller="Product"
asp-route-category="#category"
asp-route-productPage="1">#category</a>
}
</div>
I want to do something like this pagination

Ok, i tried and resolv my trouble, updated Controller, TagHelper class and View.
Maybe someone will come in handy.
Controller:
public async Task<IActionResult> List(string category, int page = 1)
{
IQueryable<Product> source = _repository.Products.Where(p => category == null || p.Category == category)
.OrderBy(p => p.ProductID);
var count = await source.CountAsync();
var items = await source.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
PagingInfo pagingInfo = new PagingInfo(count, page, pageSize);
ProductsListViewModel productsListView = new ProductsListViewModel
{
PagingInfo = pagingInfo,
Products = items
};
return View(productsListView);}
View:
<h3><page-link page-model="Model.PagingInfo" page-action="List"></page-link></h3>
TagHelper class:
public class PageLinkTagHelper : TagHelper
{
private IUrlHelperFactory urlHelperFactory;
public PageLinkTagHelper(IUrlHelperFactory helperFactory)
{
urlHelperFactory = helperFactory;
}
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public PagingInfo PageModel { get; set; }
public string PageAction { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
output.TagName = "div";
// набор ссылок будет представлять список ul
TagBuilder tag = new TagBuilder("ul");
tag.AddCssClass("pagination");
// формируем три ссылки - на текущую, предыдущую и следующую
TagBuilder currentItem = CreateTag(PageModel.PageNumber, urlHelper);
// создаем ссылку на предыдущую страницу, если она есть
if (PageModel.HasPreviousPage)
{
TagBuilder prevItem = CreateTag(PageModel.PageNumber - 1, urlHelper);
tag.InnerHtml.AppendHtml(prevItem);
}
tag.InnerHtml.AppendHtml(currentItem);
// создаем ссылку на следующую страницу, если она есть
if (PageModel.HasNextPage)
{
TagBuilder nextItem = CreateTag(PageModel.PageNumber + 1, urlHelper);
tag.InnerHtml.AppendHtml(nextItem);
}
output.Content.AppendHtml(tag);
}
TagBuilder CreateTag(int pageNumber, IUrlHelper urlHelper)
{
TagBuilder item = new TagBuilder("li");
TagBuilder link = new TagBuilder("a");
if (pageNumber == this.PageModel.PageNumber)
{
item.AddCssClass("active");
}
else
{
link.Attributes["href"] = urlHelper.Action(PageAction, new { page = pageNumber });
}
link.InnerHtml.Append(pageNumber.ToString());
item.InnerHtml.AppendHtml(link);
return item;
}
}
}

Change your for loop
for (PageModel.CurrentPage; i <= PageModel.CurrentPage + 2; i++)
That will give you 3 results based on the current page.
Then you can add a button the first and the last page on either end of the html.
There's probably a more elegant approach but that should work.

controller/
public IActionResult Index(int page=1)
{
ViewBag.Page = page;
ViewBag.TotalPage = (int)Math.Ceiling(_context.Books.Include(x => x.Author).Include(x => x.Genre).Count() / 2d);
var data = _context.Books.Include(x => x.Author).Include(x => x.Genre).Skip((page - 1) * 2).Take(2).ToList();
return View(data);
}
cshtml/
<div aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item #(page==1?"disabled":"")"><a class="page-link" asp-action="index" asp-route-page="1"><<</a></li>
<li class="page-item #(page==1?"disabled":"")"><a class="page-link" asp-action="index" asp-route-page="#(page-1)"><</a></li>
#if (page == 1)
{
for (int i = page; i <= page + 2; i++)
{
<li class="page-item #(page==i?"active":"")"><a class="page-link" asp-action="index" asp-route-page="#i">#i</a></li>
}
}
else if (page == totalPages)
{
for (int i = page - 2; i <= page; i++)
{
<li class="page-item #(page==i?"active":"")"><a class="page-link" asp-action="index" asp-route-page="#i">#i</a></li>
}
}
else
{
for (int i = page - 1; i <= page + 1; i++)
{
<li class="page-item #(page==i?"active":"")"><a class="page-link" asp-action="index" asp-route-page="#i">#i</a></li>
}
}
<li class="page-item #(page==totalPages?"disabled":"")"><a class="page-link" asp-action="index" asp-route-page="#(page+1)">></a></li>
<li class="page-item #(page==totalPages?"disabled":"")"><a class="page-link" asp-action="index" asp-route-page="#totalPages">>></a></li>
</ul>
</div>

Related

ASP.NET Core MVC paging binding

I want to add paging to my table in ASP.NET Core MVC.
Issue: how can I link TotalRecords, PageNo, & PageSize from controller to the view? In Razor page its simple to bind them, but I don't know how to do this in MVC.
TotalRecords = Query.AsQueryable().Count();
PageNo = p;
PageSize = s;
Controller:
// Display Data
public async Task<IActionResult> DisplayData(int p = 1, int s = 20)
{
try
{
IQueryable<MyModel> query = await _services.GetData();
// Add Paging
query = await AddPaging(p, s, query);
// Display Data
List<MyModel> myData = await query.AsNoTracking().ToListAsync();
return View(myData); // return view with data
}
catch
{
return View(); // return empty view
}
}
// add paging
private async Task<IQueryable<MyModel>> AddPaging(int p, int s, IQueryable<MyModel> Query)
{
TotalRecords = Query.AsQueryable().Count();
PageNo = p;
PageSize = s;
Query = Query.Skip((p - 1) * s).Take(s);
return Query;
} // end of paging
View:
#model List<MyModel>
<table>
....
</table>
<paging total-records="Model.TotalRecords"
page-no="Model.PageNo"
show-prev-next="false"
show-first-last="false">
</paging>
I suggest to solve this problem with OOP standards.
First make a class that will do pagination.
Example:
public class Paginate<T>
{
public Paginate(List<T> datas, int currentPage, int totalPage)
{
Datas = datas;
CurrentPage = currentPage;
TotalPage = totalPage;
}
public List<T> Datas { get; set; }
public int CurrentPage { get; set; }
public int TotalPage { get; set; }
public bool HasPrev
{
get
{
return CurrentPage > 1;
}
}
public bool HasNext
{
get
{
return CurrentPage < TotalPage;
}
}
}
Then send data to view by this class.
public async Task<IActionResult> Index(int page = 1, int take = 5)
{
var datas = await _context.Datas
.Skip((page - 1) * take)
.Take(take)
.AsNoTracking()
.ToListAsync();
int dataCount = await _context.Datas.CountAsync();
int initCount = (int)Math.Ceiling((decimal)count / take);
Paginate<Categories> result = new Paginate<Datas>(datas , page, initCount );
return View(result);
}
Last step is sending initial datas to controller from view.
First your view will take data from pagination class.
#model Paginate<List<MyModel>>
Then your pagination component will send data to controller:
<ul>
<li class="#(Model.HasPrev?"":"disabled")">
<a asp-action="Index" asp-route-page="#(Model.CurrentPage-1)">Previous</a>
</li>
#for (int i = 1; i <= Model.TotalPage; i++)
{
<li class="#(i==Model.CurrentPage ? "disabled":"")">
<a asp-action="Index" asp-route-page="#i">#i</a>
</li>
}
<li class="#(Model.HasNext?"":"disabled")">
<a asp-action="Index" asp-route-page="#(Model.CurrentPage+1)">Next</a>
</li>
</ul>
If you analize all code I think it will be useful. Or you can take the business logic and apply it to your DisplayData and AddPaging methods.

ASP.NET CORE passing an ID to controller to show page

I'm trying to show my data in pages using DbContext .
This is my Controller :
public async Task<IActionResult> Index(int? page)
{
int _page;
if (page.HasValue)
{
_page = page.Value;
}
else
_page = 1;
return View(await _context.Agent.Skip(_page-1).Take(10).ToListAsync());
And my layout page that contain the link :
<li><a asp-area="" asp-controller="Agents" asp-action="Index" asp-route-id="1" >סוכן</a></li>
Two things:
A) It doesn't seems to work, no matter what I pass in asp-route-id the same list is beeing shown . What am I doing wrong ?
B) After it will work, how do I present the pages for the user to choose ? (E.G. 1,2,3..90)
Thanks.
Your parameter is named page but you provide an id route value. Rename the page parameter in action method to id and it should work.
Michal answered you first question. Concerning you second question you could use third-party tag helpers like pioneer-pagination
Or write you own implementation of pagination logic for view like below(mostly based on official documentation), not perfect one implementation of pagination, but still working:
Model for page:
public class PageViewModel
{
public int PageNumber { get; }
public int TotalPages { get; }
public PageViewModel(int count, int pageNumber, int pageSize)
{
PageNumber = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
}
public bool HasPreviousPage => (PageNumber > 1);
public bool HasNextPage => (PageNumber < TotalPages);
}
Code for tag helper:
public class PagerTagHelper : TagHelper
{
private IUrlHelperFactory urlHelperFactory;
public PagerTagHelper(IUrlHelperFactory helperFactory)
{
urlHelperFactory = helperFactory;
}
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public PageViewModel PageModel { get; set; }
public string PageAction { get; set; }
public string PageController { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
output.TagName = "div";
// we put our links in list
TagBuilder tag = new TagBuilder("ul");
tag.AddCssClass("pagination");
//show link for 1st page
if (PageModel.PageNumber>2)
{
TagBuilder fstItem = CreateTag(1, urlHelper);
tag.InnerHtml.AppendHtml(fstItem);
}
// create 3 links to current, next and previous page.
TagBuilder currentItem = CreateTag(PageModel.PageNumber, urlHelper);
// link to previous page if NOT 1st page
if (PageModel.HasPreviousPage)
{
TagBuilder prevItem = CreateTag(PageModel.PageNumber - 1, urlHelper);
tag.InnerHtml.AppendHtml(prevItem);
}
tag.InnerHtml.AppendHtml(currentItem);
// link to next page, if NOT last page
if (PageModel.HasNextPage)
{
TagBuilder nextItem = CreateTag(PageModel.PageNumber + 1, urlHelper);
tag.InnerHtml.AppendHtml(nextItem);
}
//show code for last page
if (PageModel.TotalPages > 4 && PageModel.PageNumber!=PageModel.TotalPages-1 && PageModel.HasNextPage)
{
TagBuilder lstItem = CreateTag(PageModel.TotalPages, urlHelper);
tag.InnerHtml.AppendHtml(lstItem);
}
output.Content.AppendHtml(tag);
}
TagBuilder CreateTag(int pageNumber, IUrlHelper urlHelper)
{
TagBuilder item = new TagBuilder("li");
TagBuilder link = new TagBuilder("a");
if (pageNumber == this.PageModel.PageNumber)
{
item.AddCssClass("active");
}
else
{
link.Attributes["href"] = urlHelper.Action(PageAction, PageController, new { page = pageNumber });
}
link.InnerHtml.Append(pageNumber.ToString());
item.InnerHtml.AppendHtml(link);
return item;
}
}
And use it in View like (don't forget to import tag-helper in _ViewImports.cshtml):
<pager page-model="#Model.PageViewModel" page-action="Index" page-controller="Home"></pager>
And code for controller:
public async Task<IActionResult> Index(int page=1)
{
int pageSize = 10;
var count = await _context.Agent.CountAsync();
var items = await _context.Agent.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
PageViewModel pageVm= new PageViewModel(count, page, pageSize);
IndexViewModel vm= new IndexViewModel
{
PageViewModel = pageVm,
Agents =items // need to create table in view from Agent properties
};
return View(vm);
}
And code for the IndexViewModel:
public class IndexViewModel
{
public IEnumerable<Agent> Agents { get; set; }
public PageViewModel PageViewModel { get; set; }
}
Update:
If you want display all PageLinks in your view you can just use for loop in Tag Helper Process method like below:
public override void Process(TagHelperContext context, TagHelperOutput output)
{
IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
output.TagName = "div";
// we put our links in list
TagBuilder tag = new TagBuilder("ul");
tag.AddCssClass("pagination");
for (int i = 1; i <= PageModel.TotalPages; i++)
{
TagBuilder linkItem = CreateTag(i, urlHelper);
tag.InnerHtml.AppendHtml(linkItem);
}
output.Content.AppendHtml(tag);
}
Update 2:
In order to use this approach don't forget to change the model for your index page to IndexViewModel and register tag helper in _ViewImports.cshtml. My working view (simplified) looks like this.
#* Using and tag helper imported in _ViewImports.cshtml *#
#model IndexViewModel
#{
ViewData["Title"] = "Home";
}
<div>
<table class="table">
<tr><th>Name</th><th>Age</th></tr>
#foreach (var a in Model.Agents)
{
<tr><td>#a.Name</td><td>#a.Age</td></tr>
}
</table>
</div>
<pager page-model="#Model.PageViewModel" page-action="Index" page-controller="Home"></pager>
Update 3:
If you use IndexViewModel like I wrote above, but don't use foreach like in my example, you should change #Html.DisplayNameFor(model => model.Name) to #Html.DisplayNameFor(model => model.Agents.Name)
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers imports only built-in ASP.NET Core tag helpers. In order to import custom tags you should use #addTagHelper *, YourAssemblyName where YourAssemblyName is the name of you project ('AgentApp` for example), see A minimal Tag Helper section in documentation for more info.

Null model with ajax post

I am trying to pass a model to the controller using an ajax post, but every time I do so, the model is null and I lose all the data I am trying to persist.
The ajax call:
$('#next').click(function (e) {
e.preventDefault();
var url = '#Url.Action("Manage")'
var _model = JSON.stringify(#Html.Raw(Json.Encode(Model)));
var _page = #(Model.pager.CurrentPage + 1);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: url,
data: JSON.stringify({ 'model': _model, 'page': _page }),
success: function(result) {
console.log(result);
}
});
});
When I look at the serialized object, it looks like a correctly formatted JSON object with no errors thrown in the developer console.
The link that is triggering this jQuery call is just a basic action link:
#Html.ActionLink(">", "Manage", "Announcements", null, new { id = "next" })
My model is a little more complicated...
public class ManageViewModel : IEnumerable<EditViewModel>
{
[Display(Name="Start Date")]
[DataType(DataType.DateTime)]
public DateTime? StartDate { get; set; }
[Display(Name="End Date")]
[DataType(DataType.DateTime)]
public DateTime? EndDate { get; set; }
public string SearchString { get; set; }
public Pager pager { get; set; }
public List<EditViewModel> Data { get; set; }
public List<CategoryViewModel> Categories { get; set; }
public ManageViewModel()
{
Data = new List<EditViewModel>();
Categories = new List<CategoryViewModel>();
}
public IEnumerator<EditViewModel> GetEnumerator()
{
return Data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
}
public class Pager
{
public int TotalItems { get; private set; }
public int CurrentPage { get; private set; }
public int PageSize { get; private set; }
public int TotalPages { get; private set; }
public int StartPage { get; private set; }
public int EndPage { get; private set; }
public Pager(int totalItems, int? page, int pageSize = 10)
{
int totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)pageSize);
int currentPage = page ?? 1;
int startPage = currentPage - 5;
int endPage = currentPage + 4;
if(startPage <= 0)
{
endPage -= (startPage - 1);
startPage = 1;
}
if(endPage > totalPages)
{
endPage = totalPages;
if(endPage > 10)
{
startPage = endPage - 9;
}
}
TotalItems = totalItems;
TotalPages = totalPages;
CurrentPage = currentPage;
PageSize = pageSize;
EndPage = endPage;
StartPage = startPage;
}
}
I can't convert the links in to a form because that breaks the pagination. Or maybe I'm just not understanding the full picture here.
Here is the section of the View where the pagination is occurring
if (Model.pager.EndPage > 1)
{
<div style="color:#337AB7; padding-bottom: 0px;">Page #Model.pager.CurrentPage of #Model.pager.TotalPages</div>
<ul class="pagination">
#if (Model.pager.CurrentPage > 1)
{
<li>
#Html.ActionLink("<<", "Manage", new { model = Model, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
<li>
#Html.ActionLink("<", "Manage", new { model = Model, page = Model.pager.CurrentPage - 1, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
#for (var _page = Model.pager.StartPage; _page < Model.pager.EndPage + 1; _page++)
{
<li class="#(_page == Model.pager.CurrentPage ? "active" : "")">
#Html.ActionLink(_page.ToString(), "Manage", new { model = Model, page = _page, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
#if (Model.pager.CurrentPage < Model.pager.TotalPages)
{
<li>
#Html.ActionLink(">", "Manage", "Announcements", null, new { id = "next" })
</li>
<li>
#Html.ActionLink(">>", "Manage", new { model = Model, page = Model.pager.TotalPages, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
</ul>
}
The action links attempting to pass the model, obviously don't work but I left them because I am focusing on getting one to work, the one listed previously, before getting all the others configured.
I have looked at the following SO posts and have had no luck with them:
Post an MVC model with AJAX?
Model properties null on Ajax post from list box change event
How to send a model in jQuery $.ajax() post request to MVC controller method
Pass Model To Controller using Jquery/Ajax
How to pass model in ajax post request?
Any ideas on how I might be able to do this? I need the model data to persist for searching/filtering which is done through a form. Thank you in advance for taking a look and giving your insights!
You need to include a parameterless constructor for your models. From the screenshot you have sent it seems there isn't a parameterless constructor for public Pager model.

How to convert a list of strings to multiple url links?

I currently am pulling a list of url's from a view using Entity Framework 5 and MVC 5. I need to populate a list of url's in the view from this database view. Currently I have:
Repository:
public class LinkRepository
{
private readonly LinkLibrary _entities = new LinkLibrary ();
public LinkRepository()
{
_entities = new LinkLibrary ();
}
public List<LinkModels> RetrieveStateLink(string year)
{
return
_entities.vw_URLLibrary.Where(s => s.YEAR.Equals(year) && s.URL_TYPE.Equals("United States")).Select(m => new LinkModels()
{
UrlLink = m.LinkLocation
}).ToList();
}
}
Model
public class LinkModels
{
public string LinkYear { get; set; }
public string LinkState { get; set; }
public string UrlLink { get; set; }
public string LinkType { get; set; }
public List<string> ListOfUrls{ get; set; }
}
Controller
public ActionResult GetStateLinks()
{
var stateLink = new List<string>();
var model = rr.RetrieveStateLinks("2014").Select(m=> m.UrlLink).ToList();
foreach (var s in model)
{
stateLink.Add(s);
}
var rm = new LinkModels();
rm.ListOfUrls = stateLink;
return View(rm.ListOfUrls);
}
View
<ol>
#foreach (var item in Model.ListOfUrls)
{
<li>
#Html.ActionLink("state", "GetStateLinks", new { mystring = item })
</li>
}
</ol>
I am unsure how to pass this into the view to where it will display:
Alabama.aspx
Georgia.aspx
You should simply write
<ol>
#foreach (var item in Model.ListOfUrls)
{
<li>
GetStateLinks
</li>
}
</ol>
There's no need to use Html.ActionLink, as it's purpose is to link directly to an MVC controller action, not a URL.

Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery`1[]' to type 'System.Collections.Generic.IEnumerable

I'm trying to pass few ViewModels to the same View via ViewData. Unfortunately I'm new to MVC and I do not have idea what's wrong with that.
Here is first DataViewModel:
public class TagsViewModel
{
public string TagName { get; set; }
public int TagId { get; set; }
}
And another one:
public class ShortPostViewModel
{
public int PostId { get; set; }
public string PostSubject { get; set; }
public DateTime? PostCreated { get; set; }
public string PostImage { get; set; }
public string PostAuthor { get; set; }
public byte? PostRating { get; set; }
public List<PostTagsViewModel> PostedTags { get; set; }
}
Here is repository:
public IEnumerable<BlogPostViewModel.ShortPostViewModel> GetLast20()
{
var last = from a in _db.blog_post
orderby a.Posted descending
select new BlogPostViewModel.ShortPostViewModel
{
PostId = a.ID,
PostAuthor = (from u in _db.users where u.ID == a.Author
select u.Login).FirstOrDefault(),
PostCreated = a.Posted,
PostImage = a.PostAvatar,
PostRating = a.Rating,
PostSubject = a.Subject,
PostedTags = (from b in _db.tags
join c in _db.posted_tags on b.ID equals c.TagID
where c.PostID == a.ID
select new PostTagsViewModel
{
TagId = b.ID,
TagName = b.TagName
}).ToList()
};
return last.Take(20);
}
And one more:
public IEnumerable<TagsViewModel> GetAll()
{
var t = from a in _db.tags
select new TagsViewModel
{
TagId = a.ID,
TagName = a.TagName
};
return t;
}
So here is Controller:
public ActionResult Index()
{
ViewData["ShortPost"] = _postRepository.GetLast20().AsEnumerable();
ViewData["Tags"] = _tagsRepository.GetAll().AsEnumerable();
return View();
}
So on the View:
<ul class="list-group">
#foreach (var item in (IEnumerable<ShortPostViewModel>)ViewData["ShortPost"])
{
<li class="list-group-item">
<img src="#item.PostImage" alt=""/>
<h3>#Html.ActionLink(#item.PostSubject, "Details", "BlogPost", new { id = item.PostId }, null)</h3>
Создано: #item.PostCreated. Автор: #item.PostAuthor. Оценка: #item.PostRating.
<p>
Темы статьи:
#foreach (var tag in #item.PostedTags)
{
<i class="glyphicon glyphicon-tag"></i> #Html.ActionLink(#tag.TagName, "Tag", "Search", new { id = tag.TagId }, null)
}
</p>
</li>
}
</ul>
</div>
<div class="col-md-4">
#foreach (var tag in (IEnumerable<TagsViewModel>)ViewData["Tags"])
{
<span class="label label-info"><i class="glyphicon glyphicon-tag"></i> #Html.ActionLink(#tag.TagName, "Tag", "Search", new { id = tag.TagId }, null)</span>
}
</div>
This all look just fine for me. Could you advise how should I fix that?
Instead of using several ViewData, I would recommend using a new ViewModel class that have a List<TagsViewModel> property and a List<ShortPostViewModel> property so you don't have to do the conversions in the view. Let's say the ViewModel is named CustomViewModel
public class CustomViewModel
{
public CustomViewModel()
{
this.ShortPosts = new List<ShortPostViewModel>();
this.Tags = new List<TagsViewModel>();
}
public List<ShortPostViewModel> ShortPosts { get; set; }
public List<TagsViewModel> Tags { get; set; }
}
then in your controller
public ActionResult Index()
{
CustomViewModel model = new CustomViewModel();
model.ShortPosts = _postRepository.GetLast20().ToList();
model.Tags = _tagsRepository.GetAll().ToList();
return View(model);
}
Make sure you have this at the top of your view code
#model CustomViewModel
You can enumerate the items of ShortPosts in your view as below
#foreach (var item in Model.ShortPosts)
and enumerate the items of Tags as below
#foreach (var tag in Model.Tags)

Categories

Resources