For the task I have to download several items from several xml documents (RssReader) then display random 4 elements on the page. I have already done all the code, the only problem is that the page displays the same 4 items, if I set the trap in the debuggeer in the view/controller everything loads correctly. I do not know what the problem may be, it's my beginnings in ASP.NET for all the clues to the code, thank you very much!
View code:
<table class="table">
#foreach (var item in Model)
{
<tr>
<td>
#item.Title
</td>
<td>
#item.PubDate
</td>
</tr>
}
</table>
Model Code:
public class RssItem
{
public string Title { get; set; }
public string PubDate { get; set; }
}
Controller Code:
public class PortfolioController : Controller
{
// GET: Portfolio
public ActionResult Index()
{
var linkList = new List<string> {
"https://news.google.com/rss?hl=pl&gl=PL&ceid=PL:pl",
"https://news.google.com/rss?hl=pl&gl=PL&ceid=PL:pl",
"https://news.google.com/rss?hl=pl&gl=PL&ceid=PL:pl",
"https://news.google.com/rss?hl=pl&gl=PL&ceid=PL:pl"
};
List<RssItem> rssItems = new List<RssItem>();
List<RssItem> randomRSS = new List<RssItem>();
foreach (string linkRss in linkList)
{
XElement xml = XElement.Load(linkRss);
var query = xml.Descendants("item").Select(item =>
new RssItem
{
Title = item.Element("title").Value,
PubDate = item.Element("pubDate").Value,
}).ToList();
foreach (var item in query)
{
rssItems.Add(item);
}
}
for (int i = 0; i < 4; i++)
{
Random random = new Random();
int randomInt = random.Next(rssItems.Count);
randomRSS.Add(rssItems[randomInt]);
}
return View(randomRSS);
}
}
Without traps in debugger:
With traps in view and controller (debugger mode):
Why do four requests? You can take the items from a single request and display random 4. But in order for Random to work it needs to be outside the for loop.
Random random = new Random();
for (int i = 0; i < 4; i++)
{
int randomInt = random.Next(rssItems.Count);
randomRSS.Add(rssItems[randomInt]);
}
But you could still end up with duplicates if you do not remove the rss from the list since random can create duplicates, see this answer for solutions
Random number generator with no duplicates
Related
I have been struggling to initialize my public static list myListPerson.
I would like to have one person in my list who is the admin every time i run the mvc project.
Im not sure what i did wrong in my code,please help point out my error when trying to initialize my public list.
Below is my controller;
public class HomeController : Controller
{
// holds all new people registered in system in this list
public static List<Person> myListPerson = new List<Person>();
Customer myCustomer = new Customer();
Employee myEmployee = new Employee();
//increamental customer number for ViewCustomer
int Cus_Number = 1000;
public ActionResult ViewCustomer(string Name, string Surname, string Email, int Cell, int IDNumber)
{
Cus_Number = Cus_Number + 1;
myCustomer.Name = Name;
myCustomer.Surname = Surname;
myCustomer.Email = Email;
myCustomer.Cell = Cell;
myCustomer.IDNumber = IDNumber;
myCustomer.CustomerID = "Cus" + Cus_Number;
myListPerson.Add(myCustomer);
return View(myListPerson);
}
// GET: Index
public ActionResult Index()
{// I tried set admin values here but it still does not work when i check my view which displays people in list
myEmployee.Name = "1";
myEmployee.Surname = "1";
myEmployee.Email = "1";
myEmployee.Cell = 0;
myEmployee.IDNumber = 0;
myEmployee.EmployeeID = "Emp" + 1000;
myListPerson.Add(myEmployee);
return View(myListPerson);
}
Below is my view shows my list of employee
#model List<FurryFeista.ViewModels.Person>
#using FurryFeista.ViewModels;
<table id="employeetable">
<tr>
<th>Employee ID</th>
<th>Employee Name</th>
<th>Employee Surname</th>
<th>Employee ID Number</th>
<th>Contact</th>
<th>Email Adress</th>
<th>Username</th>
</tr>
#foreach (Person i in Model)
{
if (i is Employee)
{
Employee user = (Employee)i;
<tr>
<td>#user.EmployeeID</td>
<td>#user.Name</td>
<td>#user.Surname</td>
<td>#user.IDNumber</td>
<td>#user.Cell</td>
<td>#user.Email</td>
<td>#user.Username</td>
</tr>
}
}
<tr>
</table>
I have joined two tables(Projects, Task_Distribution) and pass the values to View as viewbag. But I can't access the value in view(such as: #item.Project_Id) and if I write like #item it displays the result like This image
How can I access only the value?
here is my controller code:
public class TableController : Controller
{
// GET: Table
public ActionResult Table()
{
Database_testEntities1 db1 = new Database_testEntities1();
List<Project> p = new List<Project>();
List<Task_Distribution> t = new List<Task_Distribution>();
var query = (from PI in db1.Projects join TI in
db1.Task_Distribution on PI.Project_Id equals TI.Project_Id select new { PI.Project_Id, TI.Employee_Id }).ToList();
ViewBag.table = query;
return View();
}
}
And this is my view
#{
ViewBag.Title = "Table";
}
<h2>Table</h2>
<table>
#foreach (var item in ViewBag.table)
{
<tr>
<td>
#item
</td>
</tr>
}
</table>
Can you guys give me a solution, please?
See this part of your LINQ expression,
select new { PI.Project_Id, TI.Employee_Id }
You are projecting the result to an anonymous object. So what you have in the ViewBag is a list of these anonymous objects. When you loop through them and executes the code #item, razor will call the ToString method on that which renders the property name and values(which is what you are seeing)
You can create a view model to represent this data and do a projection using that
public class ProjectEmployeeVm
{
public int ProjectId { set;get;}
public int EmployeeId { set;get;}
}
Now in your LINQ expression,
select new ProjectEmployeeVm { ProjectId = PI.Project_Id,EmployeeId = TI.Employee_Id }
Now in your view,you can use cast your viewbag item to a list of this view model
#foreach (var item in ViewBag.table as List<ProjectEmployeeVm>)
{
<p>#item.ProjectId<p>
<p>#item.EmployeeId <p>
}
Also now since you have a view model, why not use that to transfer data to view instead of using dynamic view bag which is prone to errors.
var items = (from PI in db1.Projects join TI in
db1.Task_Distribution on PI.Project_Id equals TI.Project_Id
select new ProjectEmployeeVm { ProjectId = PI.Project_Id,
EmployeeId = TI.Employee_Id
}).ToList();
return View(items);
Now in your view,
#model List<ProjectEmployeeVm>
#foreach (var item in Model)
{
<p>#item.ProjectId<p>
<p>#item.EmployeeId <p>
}
I have this class as a part of EF Model:
class Person {
public int Id { get; set; }
[MaxLength(100, ErrorMessage="Name cannot be more than 100 characters")]
public string Name { get; set; }
}
And I have this method in my controller:
public IActionResult ChangeName(int id, string name) {
var person = db.Persons.Find(id);
if(person == null) return NotFound();
person.Name = name;
db.SaveChanges();
return Json(new {result = "Saved Successfully"});
}
Is there any way to validate person after changing the Name property using the annotation MaxLength rather than manually check for it. Becuase sometimes I might have more than one validation and I don't want to examine each one of them. Also, I might change these parameters in the future (e.g. make the max length 200), and that means I have to change it everywhere else.
So is it possible?
Your method works as long as there is one validation error per property. Also, it's quite elaborate. You can use db.GetValidationErrors() to get the same result. One difference is that errors are collected in a collection per property name:
var errors = db.GetValidationErrors()
.SelectMany(devr => devr.ValidationErrors)
.GroupBy(ve => ve.PropertyName)
.ToDictionary(ve => ve.Key, ve => ve.Select(v => v.ErrorMessage));
Okay, I found a solution to my problem, I created a method that takes the model and checks for errors:
private IDictionary<string, string> ValidateModel(Person model)
{
var errors = new Dictionary<string, string>();
foreach (var property in model.GetType().GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
var validationAttribute = attribute as ValidationAttribute;
if(validationAttribute == null) continue;
var value = property.GetValue(model);
if (!validationAttribute.IsValid(value))
{
errors.Add(property.Name, validationAttribute.ErrorMessage);
}
}
}
return errors;
}
UPDATE:
As stated by #Gert Arnold, the method above returns only one validation per property. Below is the fixed version which returns a list of errors for each property
public static IDictionary<string, IList<string>> ValidateModel(Person model)
{
var errors = new Dictionary<string, IList<string>>();
foreach (var property in model.GetType().GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
var validationAttribute = attribute as ValidationAttribute;
if (validationAttribute == null) continue;
var value = property.GetValue(model);
if (validationAttribute.IsValid(value)) continue;
if (!errors.ContainsKey(property.Name))
errors[property.Name] = new List<string>();
errors[property.Name].Add(validationAttribute.ErrorMessage);
}
}
return errors;
}
So I have this little "Restaurant" webapp going on where I'm trying to construct a View-page where an user can click on a created (restaurant) table (dropdown that refreshes automatically after clicking) and check what dishes are ordered on that table. At the bottom, the user will find the total price.
My view works 100% fine but ONLY if I have 1 table in the database. If I have multiple tables (restaurant tables) (created in my DropCreate-Seed method), the view will only show the dropdown with available tables, not the dishes inside of them.
What could cause this problem?
CODE
TABLE
public class Table
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual List<Dish> Dishes { get; set; }
}
SEED METHOD
context.Tables.Add(new Table { Id = 1 ,Name = "table1", Dishes = context.Dishes.ToList() });
context.Tables.Add(new Table { Id = 2, Name = "table2", Dishes = context.Dishes.ToList() });
context.Tables.Add(new Table { Id = 3, Name = "table3", Dishes = context.Dishes.ToList() });
context.SaveChanges();
TABLE CONTROLLER
public ActionResult Order(int? tableID)
{
//Create a ViewBag variable contained with all the tables (show name)
List<table> tableL = db.tables.ToList();
List<SelectListItem> tableSL = new List<SelectListItem>();
foreach (var table in tableL)
{
TableSL.Add(new SelectListItem { Text = table.Name.ToLower(), Value = table.Name.ToString() });
}
ViewBag.tableL = tableSL;
//At the start or at problems, get the first table and return it
if (tableID == null || db.tables.Find(tableID) == null)
{
return View(db.tables.First());
}
else
{
Table l = db.tables.Find(tableID);
return View(l);
}
}
VIEW
#model VBExamenTEST.Models.Table
#{
ViewBag.Title = "Order";
}
<h2>Order</h2>
#using (Html.BeginForm("Bedien", "Bedien", FormMethod.Post))
{
#Html.DropDownList("tableID", (List<SelectListItem>)ViewBag.TableL, new {onchange = "this.form.submit()" });
}
#if(Model != null)
{
double total = 0;
<table>
<tr>
#foreach(var g in Model.Dishes)
{
total += g.Price;
<td>
#Html.DisplayFor(Model=>g.Name)
</td>
}
</tr>
</table>
<p>Total:#total euro </p>
}
So my code works perfectly if I only have one table added in my DB, whenever I add more, only the dropdown shows up, the dishes are not shown on click, and the total equals 0.
Where can my problem be situated?
I know how to hook up ajax paging to a grid or a webgrid in asp.net mvc. But how can I accomplish ajax paging, using custom paging for large data sets for another format outside of a table grid.
Is that even possible using an mvc helper or mvc.pagedlist?
I used to be a webforms guys and it was so easy to hook up a listview where you could use divs to create whatever layout you want for individual items, you could then hook up a datapage and wrap it all in an update panel.
Basically I want a list of items that I can page through via ajax but with having large data sets I can just pull down all the items and page via jquery, I need to do custom paging on the server side and only return the items for a specific page.
By reusing a partial view and some ajax, this is very easily done in MVC.
Add this model as a property to your page's ViewModel to handle the pagination:
namespace Models.ViewModels
{
[Serializable()]
public class PagingInfoViewModel
{
public int TotalItems { get; set; }
public int ResultsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages {
get { return Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(this.TotalItems) / this.ResultsPerPage)); }
}
public string LinkTextShowMore { get; set; }
public string LinkTextShowingAll { get; set; }
/// <summary>
/// Paging url used by the jQuery Ajax function
/// </summary>
public string UrlGetMore { get; set; }
public PagingInfoViewModel(string linkTextShowMore, string linkTextShowingAll, int resultsPerPage)
{
this.LinkTextShowMore = linkTextShowMore;
this.LinkTextShowingAll = linkTextShowingAll;
this.ResultsPerPage = resultsPerPage;
}
}
}
Add the following code to your partial view to handle the pagination:
//Start Pagination
//determine the value for the X for "Showing X of Y"
{
int currentTotal = 0;
if ((Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage) < Model.PagingInfo.TotalItems) {
//the current max item we are displaying is less than the total number of policies
//display the current max item index\
currentTotal = Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage;
} else {
//the current is greater than the total number of policies
//display the total number of policies
currentTotal = Model.PagingInfo.TotalItems;
}
if (Model.PagingInfo.TotalPages == 0 || Model.PagingInfo.CurrentPage == Model.PagingInfo.TotalPages)
{
#<li>
<h3>#Model.PagingInfo.LinkTextShowingAll</h3>
<p><strong>Showing #currentTotal Of #Model.PagingInfo.TotalItems</strong></p>
</li>
} else {
#<li id="GetMore">
<a href="#" id="lnkGetMore">
<h3>#Model.PagingInfo.LinkTextShowMore</h3>
<p><strong>Showing #(currentTotal) Of #Model.PagingInfo.TotalItems</strong></p>
</a>
</li>
#<script type="text/javascript" lang="javascript">
$('#lnkGetMore').click(function () {
$.ajax({
url: "#Model.PagingInfo.UrlGetMore",
success: function (data) {
$('#ProducerList li:last').remove();
$('#ProducerList').append(data);
$('#ProducerList').listview('refresh');
}
});
return false;
});
</script>
}
}
Now, the javascript at the end is specifically for a UI that uses ul's and li's, but can easily be customized for your needs.
The UrlGetMore property is set on the back end when the model is passed to the view. I am sure there is a more elegant way of doing this. Here is the code I used:
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore == Url.RouteUrl("RouteItemList", new { page = view.PagingInfo.CurrentPage + 1 })
And finally, here is the action that handles both the initial View and the subsequent Partial View (ajax call)
public ActionResult List(UserModel user, ViewModel view, int page = 1)
{
IQueryable<model> models = this.RetrieveModels(user, view);
if ((models != null) && models.Count > 0) {
view.PagingInfo.CurrentPage = page;
view.PagingInfo.ResultsPerPage = user.Preferences.ResultsPerPage;
view.PagingInfo.TotalItems = models.Count;
view.items = models.Skip((page - 1) * user.Preferences.ResultsPerPage).Take(user.Preferences.ResultsPerPage).ToList();
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore = Url.RouteUrl("RouteList", new { page = view.PagingInfo.CurrentPage + 1 });
}
if (page == 1) {
return View(view);
} else {
return PartialView("ListPartial", view);
}
}
HTH.
You could create simple HtmlHelper simillar to this:
public static class HtmlPaginHelper
{
public static MvcHtmlString PagerNoLastPage(this AjaxHelper ajaxHelper,
int page,
int pageSize,
bool isLastPage,
Func<int, string> pageUrl,
Func<int, AjaxOptions> pageAjaxOptions)
{
var result = new StringBuilder();
var firstPageAnchor = new TagBuilder("a");
firstPageAnchor.SetInnerText("<<");
var prevPageAnchor = new TagBuilder("a");
prevPageAnchor.SetInnerText("<");
var nextPageAnchor = new TagBuilder("a");
nextPageAnchor.SetInnerText(">");
var currentPageText = new TagBuilder("span");
currentPageText.SetInnerText(string.Format("Page: {0}", page));
if (page > 1)
{
firstPageAnchor.MergeAttribute("href", pageUrl(1));
firstPageAnchor.MergeAttributes(pageAjaxOptions(1).ToUnobtrusiveHtmlAttributes());
prevPageAnchor.MergeAttribute("href", pageUrl(page - 1));
prevPageAnchor.MergeAttributes(pageAjaxOptions(page - 1).ToUnobtrusiveHtmlAttributes());
}
if (!isLastPage)
{
nextPageAnchor.MergeAttribute("href", pageUrl(page + 1));
nextPageAnchor.MergeAttributes(pageAjaxOptions(page + 1).ToUnobtrusiveHtmlAttributes());
}
result.Append(firstPageAnchor);
result.Append(prevPageAnchor);
result.Append(currentPageText);
result.Append(nextPageAnchor);
return MvcHtmlString.Create(result.ToString());
}
}
... and then use it in your Razor view:
grid results go here...
#Ajax.PagerNoLastPage(Model.Query.Page,
Model.Query.PageSize,
Model.Data.IsLastPage,
i => Url.Action("Index", RouteValues(i)),
i => new AjaxOptions
{
UpdateTargetId = "content",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
Url = Url.Action("Grid", RouteValues(i))
})
where RouteValues(i) is defined for example like this:
#functions {
private object PageRouteValues(int i)
{
return new
{
payId = Model.Query.PayId,
clientCode = Model.Query.ClientCode,
fromDate = Model.Query.FromDate,
tillDate = Model.Query.TillDate,
payNum = Model.Query.PayId,
checkNum = Model.Query.CheckNum,
payType = Model.Query.PayType,
payStatus = Model.Query.PayStatus,
page = i,
pageSize = Model.Query.PageSize
};
}
}
Is that even possible using an mvc helper or mvc.pagedlist?
Yes, but of course you have to coordinate the client-side requests with server-side actions to handle the actual data paging. In that sense, it's not as simple as as WebForms, but it's still possible.
Here's an example of using PagedList to render each returned item in its own table, separated by horizontal rules. You should easily be able to modify the HTML in the example to produce any rendering you want.