PagedList in asp.net mvc 3 not returning anything to any page after 1 - linq

I am using paged list in a search actionresult to page my results. It returns events within a date range or category.The first page returned is fine, but the second never has any results. There are over 7 results that the query returns when I check a breakpoint. Here is my search controller;
public ActionResult Search(DateTime? q, DateTime? e, int? EventCategoryId, Event model)
{
var SearchEvents = db.Events
.OrderByDescending(r => r.start)
.Where(r =>
r.start >= q &&
r.end <= e &&
r.EventCategoryId == EventCategoryId ||
r.start >= q ||
r.EventCategoryId == EventCategoryId);
ViewBag.QValue = model.start;
ViewBag.EValue = model.end;
ViewBag.ListValue = model.EventCategoryId;
ViewBag.EventCategoryId = new SelectList(db.EventsCategories, "Id", "Name", ViewBag.ListValue);
var pageIndex = model.Page ?? 1;
var results = SearchEvents.ToPagedList(pageIndex, 4);
ViewBag.Names = results;
return View(results);
}
}
And here is my view;
#model IPagedList<RealKaac.Models.Event>
#{
ViewBag.Title = "SearchResults";
}
#using PagedList;
#using PagedList.Mvc;
#using System.Linq;
<div id="Content">
<h2>SearchResults</h2>
<div id="EventSearch">
#using (Html.BeginForm("Search", "Event"))
{
<p class="EventPar">Start Date: </p>
<input id ="datepicker" type ="text" name ="q" value="#ViewBag.QValue" />
<p class="EventPar">End Date: </p>
<input id ="datepickerend" type ="text" name ="e" value ="#ViewBag.EValue" />
<p class="EventPar">Category:</p>
#Html.DropDownList("EventCategoryId")
<input id="EventButton" style="padding:1px;" type ="submit" name ="Search" value = "Search" />
}
</div>
<div id="IndexEvents">
#foreach (var item in Model)
{
<div class ="event">
<div class="eventname">
#*<p>#Html.DisplayFor(modelItem => item.Name)</p> *#
</div>
<div class = "etitle">
<p>#Html.DisplayFor(modelItem => item.title)</p>
</div>
<div class="eventsdesc">
<p>#Html.DisplayFor(modelItem => item.EventDescription)</p>
</div>
<ul class ="datelink">
<li style="margin-right:120px;">This event starts on the
#Html.DisplayFor(modelItem => item.start) and ends on the
#Html.DisplayFor(modelItem => item.end)</li>
<li>Click #Html.DisplayFor(modelItem => item.EventWebsite) for more info.</li>
</ul>
</div>
}
</div>
</div>
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new { page }))
I'm fairly new at this so I'm probably missing something small, any advice is greatly appreciated.
Thanks.

The Url.Action has only one parameter to it (the page #), which doesn't match the controller's method. It isn't providing access to any of the other parameters (such as the start/end dates). So, the second "page" query for the next 4 Events likely isn't working. If you created a SearchModel that had the necessary properties on it, you should easily be able to send the values from one page to the next.
public ActionResult Search(SearchModel search) {
//... (your search code)
// ... then, before returning the View,
ViewBag.SearchParameters = search;
return View(results);
where SearchModel has properties like:
public class SearchModel {
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int Page? { get; set; }
// etc.
}
Then, you could:
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new SearchModel { search = ViewBag.SearchParameters }))
Or you could just use named parameters:
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new { page = page, q = ViewBag.QValue, /* ... etc. */ }))

Related

Displaying null IEnumerable with EditorTemplate

I have a complex object which has a IEnumerable property and that I want to display in my view.
For that purpose I've created an EditorTemplate for that list.
So, in my view I have the following code:
<div id="tabMed" class="tab-pane">
<fieldset>
<div>
#Html.EditorFor(m => m.MyList)
</div>
</fieldset>
</div>
m.List is an IEnumerable type and the Editor template has inside some fields for displaying the object.
This works fine when the list has 1 or more objects; it will display and repeat the template for every object in that list.
Now the problem:
Let's supose that I want to edit the object that contains that list and the IEnumerable property is null. The Editor template won't appear at all, leaving no possibility to create the first object in that list.
Is there any approach to show an empty template if the list is null without having to something like this?:
<div id="tabMed" class="tab-pane">
<fieldset>
<div>
#if (Model.MyList.Count() > 0)
{
#Html.EditorFor(m => m.MyList)
}
else
{
// empty object form template here...
}
</div>
</fieldset>
</div>
Edit: The EditorTemplate look like this:
#model Jazz.Models.MyList.MyObject
<div class="control-group">
#Html.LabelFor(model => Model.Name, new { #class="control-label"})
<div class="controls">
#Html.TextBoxFor(m => m.Name, new { #class="collection-item", autocomplete = "off", maxlength = 64 })
</div>
</div>
The MyList is the list that I want to display
The MyObject class is declared inside MyList
So, the model would be like this:
public class MyList
{
public MyList()
{
this.list = new List<MyObject>();
}
public int id{ get; set; }
public List<MyObject> list{ get; set; }
public class MyObject
{
public MyObject(){}
public string Name{ get; set; }
}
}
I've put the example in a generic way so it's simplier to show the problem.

Best way to fill dropdownlist in mvc3 application

I have an Index view in an MVC3 application with a #model which is ienumerable. In this model I have an accountID which I want to use to populate my dropdownlist in the view filter with the accounts so that the user will be able to filter for accounts.
Which is the best way to achieve this?
Thanks in advance.
This is the view:
#model IEnumerable<MoneyAdmin.Model.ContaAReceber>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("_SubmenuAdmin")
<div class="tituloCadastro">
Lista de Contas a Receber
</div>
<div class="buttonContainer novo">
#Html.ActionLink("Nova Conta", "Create")
</div>
<div class="filtros">
#using (Html.BeginForm()) {
<div class="filterField">
<label>Data Inicial:</label>
#Html.TextBox("dataInicial", #DateTime.Now.ToShortDateString())
</div>
<div class="filterField">
<label>Data Final:</label>
#Html.TextBox("dataFinal", #DateTime.Now.ToShortDateString())
</div>
<div class="filterField">
<label>Tipo de Conta:</label>
#Html.DropDownList("contaID")
</div>
<input type="submit" value="Atualizar" />
}
</div>
And the controller method:
public ViewResult Index(string dataInicial, string dataFinal, string contaID)
{
var crs = from cr in db.contasareceber.Include("contas")
select cr;
if (!string.IsNullOrEmpty(dataInicial) && !string.IsNullOrEmpty(dataFinal))
{
DateTime di = DateTime.Parse(dataInicial);
DateTime df = DateTime.Parse(dataFinal);
crs = crs.Where(cr => cr.dataPagamento >= di && cr.dataPagamento <= df);
}
return View(crs.ToList());
}
I think the best way to achieve what you're after would be to use a ViewModel. You'd load the stuff you want to display in your View through this. So you'd create a dropdownlist with your accountlist which will be loaded in your controller. You'll also have your IEnumerable ContaAReceber in there which will also be loaded in your controller. Then your controller will pass the ViewModel to the View. Sort of hard to give you an exact answer as you haven't shown us your Model. But you can use this as a guide.
ViewModel:
public class ContaAReceberViewModel
{
public int ContaAReceberID {get;set;}
public List<SelectListItem> ContaAReceberList {get;set;}
public IEnumerable<ContaAReceber> ContaAReceber {get;set;}
}
Dropdownlist in Razor View :
#Html.DropDownListFor(m => m.ContaAReceberID, Model.ContaAReceberList)
You can use ViewBag instead of creating a ViewModel to transport your data.
ViewModel
public class ContaFilterViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
ActionResult
public ViewResult Index(string dataInicial, string dataFinal, string contaID)
{
var crs = from cr in db.contasareceber.Include("contas")
select cr;
// select uniquely all available Contas
ViewBag.UniqueContas = crs.Select(x => new ContaFilterViewModel() { Id = x.ContaId, Name = x.ContaName}).Unique().ToList();
if (!string.IsNullOrEmpty(dataInicial) && !string.IsNullOrEmpty(dataFinal))
{
DateTime di = DateTime.Parse(dataInicial);
DateTime df = DateTime.Parse(dataFinal);
crs = crs.Where(cr => cr.dataPagamento >= di && cr.dataPagamento <= df);
}
// return filtered Contas
return View(crs.ToList());
}
View
<div class="filterField">
<label>Tipo de Conta:</label>
#Html.DropDownList("contaID", new SelectList((ContaFilterViewModel)ViewBag.UniqueContas, "Id, "Name"))
</div>
Thanks for everyone!
I found the answer as like this.
Populate the viewbag in the controller like that:
ViewBag.Contas = new SelectList(db.contas, "contaID", "nome");
Then use it in the dropdownlist like that:
#Html.DropDownList("Contas");
Simple and it works!
Thanks for everyone!

MVC 3 Get result from partial view into model

I'm sure this is easy, but maybe I haven't searched well ...
I want to know how to get results from a partial view back to the model and/or controller.
If the user enters a FirstName, Gender (from drop down) and Grade (from drop down), I only find then FirstName and Gender in the model. I want to know how to get the Grade from the drop down in the partial view all the way back into the model, so I can see it in the controller.
Please look for this question in the controller code:
What do I need to do to get the GradeLevel from the partial class to be here: <<<<<
Note: this is not the exact code. There may be small, insignificant typo's.
EDIT: Apparently you can't add a long comment, so I will add here:
Thank you, Tom and Mystere Man. Tom got me thinking as to why it doesn't work. I didn't think through the model binding. With the design I proposed, the HTML gets rendered and the Grade drop down has this id: "Grade". The property on the model I want to bind to is: "GradeLevelID". If I change the helper in the partial view to be #Html.DropDownList("GradeLevelID" ... it works perfectly.
But that is not a good solution. My idea was to abstract the partial view from the main view. Hard coding the name blows that! I did work up a slightly improved solution. In the main view, I change the #Html.Partial statement to pass the model property name to the partial. Like such:
#Html.Partial("GradeDropDown", (SelectList)Model.GradeSelectList, new ViewDataDictionary { { "modelPropertyName", "GradeLevelID" } })
Then I could change the partial view to say
#model System.Web.Mvc.SelectList
#Html.DropDownList((string)ViewData["modelPropertyName"], Model)
But that also seems like a goofy way to approach things. Thanks for the help. I'll look at EditorTemplates.
Here is my model:
public class RegisterModel{
public MemberRegistration MemberRegistration{
get{
if (HttpContext.Current.Session["MemberRegistration"] == null){
return null;
}
return (MemberRegistration)HttpContext.Current.Session["MemberRegistration"];
}
set{
HttpContext.Current.Session["MemberRegistration"] = value;
}
}
public string FirstName{
get{
return MemberRegistration.FirstName;
}
set{
MemberRegistration.FirstName = value;
}
}
public SelectList GenderSelectList{
get{
List<object> tempList = new List<object>();
tempList.Add(new { Value = "", Text = "" });
tempList.Add(new { Value = "M", Text = "Male" });
tempList.Add(new { Value = "F", Text = "Female" });
return new SelectList(tempList, "value", "text", MemberRegistration.Gender);
}
}
[Required(ErrorMessage = "Gender is required")]
public string Gender{
get{
return MemberRegistration.MemberPerson.Gender;
}
set{
MemberRegistration.MemberPerson.Gender = value;
}
}
public SelectList GradeLevelSelectList{
get{
List<object> tempList = new List<object>();
tempList.Add(new { Value = "", Text = "" });
tempList.Add(new { Value = "1", Text = "1st" });
tempList.Add(new { Value = "2", Text = "2nd" });
tempList.Add(new { Value = "3", Text = "3rd" });
tempList.Add(new { Value = "4", Text = "4th" });
return new SelectList(tempList, "value", "text", MemberRegistration.GradeLevel);
}
}
[Required(ErrorMessage = "Grade is required")]
public Int32 GradeLevel{
get{
return MemberRegistration.GradeLevel;
}
set{
MemberRegistration.GradeLevel = value;
}
}
}
Here is my main view:
#model RegisterModel
#using (Html.BeginForm())
{
<p class="DataPrompt">
<span class="BasicLabel">First Name:</span>
<br />
#Html.EditorFor(x => x.FirstName)
</p>
<p class="DataPrompt">
<span class="BasicLabel">Gender:</span>
<br />
#Html.DropDownListFor(x => x.Gender, Model.GenderSelectList)
</p>
<p class="DataPrompt">
<span class="BasicLabel">Grade:</span><span class="Required">*</span>
<br />
#Html.Partial("GradeDropDown", (SelectList)Model.GradeLevelSelectList)
</p>
<p class="DataPrompt">
<input type="submit" name="button" value="Next" />
</p>
}
Here is my partial view (named "GradeDropDown"):
#model System.Web.Mvc.SelectList
#Html.DropDownList("Grade", Model)
Here is my controller:
[HttpPost]
public ActionResult PlayerInfo(RegisterModel model)
{
string FirstName = model.Registration.FirstName;
string Gender = model.Registration.Gender;
>>>>> What do I need to do to get the GradeLevel from the partial class to be here: <<<<<
Int32 GradeLevel = model.Registration.GradeLevel;
return RedirectToAction("Waivers");
}
I don't even know why you are using a partial view. All you're doing is using one helper method, you could replace the partial view with the helper method in the view and it would be less code.
Second, you should be using Html.DropDownListFor() instead of Html.DropDownList(), then it will correctly name the html controls for you.
Just do this:
<p class="DataPrompt">
<span class="BasicLabel">Grade:</span><span class="Required">*</span>
<br />
#Html.DropDownListFor(m => m.GradeLevel, (SelectList)Model.GradeLevelSelectList)
</p>
try this to get the correct naming for the elements when they get posted.
On your main view
#Html.Partial("GradeDropDown", Model) //Pass the Model to the partial view
Here is your partial view (named "GradeDropDown"):
#model RegisterModel
#Html.DropDownList("Grade", (SelectList)Model.GradeLevelSelectList)

Am i implementing a radio button list in MVC correctly?

I have an IList in my model. Which i am displaying as radio buttons.
But when i submit the form the value is not correct and the model state is not valid and where the value for the selected radio button should be there is 'Count =0'
This the option in model:
[Display(Name = "My enquiry is regarding: *")]
public IList<Industry> A1_EnquiryRegarding { get; set; }
controller:
populate list:
Industry blank = new Industry();
blank.Id = 0;
blank.Name = "Other";
IList<Industry> industryList = manager.GetIndustries();
industryList.Insert(industryList.Count, blank);
EnquiryModel.A1_EnquiryRegarding = industryList;
html:
<td>
<div class="editor-label">
<b> #Html.LabelFor(m => m.A1_EnquiryRegarding)</b>
</div>
<div class="editor-field">
#foreach (var radiobutton in Model.A1_EnquiryRegarding) {
#Html.RadioButtonFor(m => m.A1_EnquiryRegarding, radiobutton.Name)
<label>#radiobutton.Name</label>
<br></br>
}
#Html.ValidationMessageFor(m => m.A1_EnquiryRegarding)
</div>
</td>
where am i goign wrong? why am i not getting the correct selected value back?
Edit:
[HttpPost]
public ActionResult EnquiryForm(Enquiry Enquiry)
{
When you post back, your collection of complex object is not recreated. Instead, there is only one string value passed with the selected value of the radio. Your model for the update action should only include one name.
Implement your radiolist as follows:
#foreach (var radiobutton in Model.A1_EnquiryRegarding) {
#Html.RadioButton("selectedIndustry", radioButton.Name);
}
All your radio buttons should have the same name, but different values. That way, when you call your Post action, you just search for parameter "selectedIndustry".
[HttpPost]
public ActionResult MyPostAction(string selectedIndustry) {
}

Problem with Edit View using ViewModel

I have a complex object that I want to use in an edit view. To simplify things I have created a ViewModel and have successfully created the edit view page, and everything renders correctly. When I hit save, everything falls apart.
The ViewModel is as follows:
public class ClosureEditViewModel
{
public Model.Closure Closure { get; set; }
public Model.School School { get; set; }
public Model.ClosureDetail CurrentDetails { get; set; }
}
Some of the View is as follows:
<div class="display-label">School</div>
<div class="display-field">
#Html.DisplayFor(model => model.Closure.School.Name)
</div>
<div class="display-label">Closed</div>
<div class="display-field">
#Html.DisplayFor(model => model.Closure.Logged)
</div>
....
<div class="editor-label">
#Html.LabelFor(model => model.CurrentDetails.DateOpening, "Date Opening (dd/mm/yyyy)")
</div>
<div class="editor-field">
#Html.TextBox("DateOpening", Model.CurrentDetails.DateOpening.ToString("dd/MM/yyyy"))
#Html.ValidationMessageFor(model => model.CurrentDetails.DateOpening)
</div>
....
<tr>
<td>
#Html.CheckBoxFor(model => model.CurrentDetails.Nursery, (Model.School.Nursery ? null : new { #disabled = "disabled" }))
</td>
The important parts of the controller are as follows:
public ActionResult Edit(int id)
{
Data.IClosureReasonRepository reasonRepository = new Data.SqlServer.Repositories.ClosureReasonRepository(UnitOfWork);
IEnumerable<Model.ClosureReason> reasons = reasonRepository.GetAll();
Model.Closure closure = ClosureRepository.GetClosure(id);
Model.ClosureDetail currentDetail = closure.ClosureDetails.Last();
ViewModels.ClosureEditViewModel editClosure = new ViewModels.ClosureEditViewModel() { Closure = closure, School = closure.School, CurrentDetails = closure.ClosureDetails.Last() };
ViewBag.ReasonId = new SelectList(reasons, "Id", "Name", currentDetail.ReasonId);
return View(editClosure);
}
[HttpPost]
public ActionResult Edit(ViewModels.ClosureEditViewModel newDetail)
{
//if (ModelState.IsValid)
//{
//}
Data.IClosureReasonRepository reasonRepository = new Data.SqlServer.Repositories.ClosureReasonRepository(UnitOfWork);
IEnumerable<Model.ClosureReason> reasons = reasonRepository.GetAll();
ViewBag.ReasonId = new SelectList(reasons, "Id", "Name", newDetail.CurrentDetails.ReasonId);
return View(newDetail);
}
When I hit save the following message appears:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 94: </td>
Line 95: <td>
Line 96: #Html.CheckBoxFor(model => model.CurrentDetails.P1, (Model.School.P1 ? null : new { #disabled = "disabled" }))
Line 97: </td>
Line 98: <td>
I just can't figure out why it is having problems with the School property but neither of the other two.
James :-)
It seems that Model.School is null when you render the view once again in the POST action. Make sure that it isn't null because in your view you don't have a single input field bound to the School property => this property will be null inside your POST controller action.
[HttpPost]
public ActionResult Edit(ClosureEditViewModel viewModel)
{
... some operations
// Make sure that viewModel.School is not null
// Remember that the checkbox is bound to CurrentDetails.P1 so
// when you post to this action there is nothing that will initialize
// the School property => you should do whatever you did in your GET
// action in order to initialize this property before returning the view
return View(viewModel);
}

Resources