PagedList MVC stay on the same page after a post - asp.net-mvc

I have a view where the side menu (where PagedList is populating data with no problems) that are also links to display another set of data on the same view. Once these links are clicked, I want to have the data on that field to be changed and have the side menu (PagedList) to stay on the same page. I can pass the id value to my controller and display the data, but the "page number" value is not being passed.
Any ideas on how to accomplish that.
Here's what my code looks like
[HttpPost]
public ActionResult Index(string id, int? page)
{
try
{
id = Request.Form["newsLinkButton"];
ViewBag.Id = id;
using (myEntities db = new myEntities())
{
var getNews = db.News.Where(x => x.Show == "Yes").OrderByDescending(x => x.Date).ToList();
return View(getNews.ToPagedList(page ?? 1, 5));
}
}
catch (Exception ex)
{
return View(ex);
}
}
<div class="sidebar blue-sidebar news-sidebar no-mobile">
<h2>Recent News</h2>
#using (Html.BeginForm("Index", "News", FormMethod.Post))
{
<form id="newsForm" name="newsForm">
<ul class="white-text">
#foreach (var item in Model.OrderByDescending(x => x.Date))
{
<li>
<button id="newsLinkButton" name="newsLinkButton" type="submit" value="#item.Id">#item.Title</button>
</li>
}
</ul>
</form>
<div class="center-block">
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }),
new PagedListRenderOptions()
{
DisplayLinkToFirstPage = PagedListDisplayMode.Always,
DisplayLinkToPreviousPage = PagedListDisplayMode.Always,
DisplayLinkToLastPage = PagedListDisplayMode.Always,
DisplayLinkToNextPage = PagedListDisplayMode.Always,
MaximumPageNumbersToDisplay = 3, // number of pages in line
DisplayEllipsesWhenNotShowingAllPageNumbers = false
})
</div>
}
</div>
Once again, thanks for your help in advance. I appreciate.

I found out that the property PageNumber is what holds the value for the page number (yeah, I know huh?). Therefore, I created #{ Session["page"] = Model.PageNumber; } and passed on to the controller. Problem solved. I hope this can help someone else in the future.

Related

handle 'open in a new window' when clicked on ajax.ActionLink which point to action that returns partial view

Hello I've got an action which gets some data from database and returns a partial view
In the partial view there are ajax.actionLinks which when clicked execute the same ImportShow action but this time with new data; and as you see in the cshtml - then update only the with the new data.
The problem I'm trying to solve is - if a user clicks 'Open in new Window' or 'open in new tab' in the new window you will see loaded only this partial view. And I can't think of a way how to make redirect and reload the whole page only in this cases. (after all the link point to an action method that RETURNS A PARTIAL VIEW).
public virtual ActionResult ImportShow(String id, String menuID, string articlegroupID, int? counter)
{
GroupMenu p_GroupMenu = new GroupMenu();
p_GroupMenu.MenuHistory = p_GetMenuHistory.ToList();
p_GroupMenu.MenuLeft = p_GetMenuLeft.ToList();
return PartialView("ImportShow", p_GroupMenu);
}
As
model MvcBeaWeb.GroupMenu
<div class="importPartUpdate">
<ul id="products">
#{
if (Model != null)
{
foreach (MvcBeaDAL.WebServiceBeaMenu item in Model.MenuLeft)
{
<li id="#item.ID">
<div class="imageTilesShow">
<a title= #item.SpecialWord>
<img src="#item.ImageFile" alt='#item.SpecialWord)' id="ImageProducts" class="imageTilesShow">
#Ajax.ActionLink(#item.SpecialWord, "ImportShow", new { id = Model.LanguageName,menuID=#item.ID},new AjaxOptions { UpdateTargetId = "importPartUpdate", HttpMethod = "GET", InsertionMode = InsertionMode.Replace })
</a>
</div>
</li>
}
}
}
</ul>
</div>
There are a few posts that having this issue before, you can check out this and this. Basically what happens is: when you click the "ajax" link, it is a AJAX call, therefor the partial view was rendered and everything works as expected. However, when you right click to view the page a new tab or new window in the browser, it is NOT a AJAX call, but you're returning a partial view, the new tab or window will still return a partial view. That's why you only see the partial view.
To illustrate what I meant:
here's the code snippet.
public class HomeController : Controller
{
List<Person> people = new List<Person>()
{
new Person { Name = "Larry", Age = 10},
new Person { Name = "Jessie", Age = 11},
new Person { Name = "Ben", Age = 12},
new Person { Name = "Victor", Age = 13},
new Person { Name = "Tom", Age = 14},
new Person { Name = "Suresh", Age = 15},
new Person { Name = "Jim", Age = 16},
};
public ActionResult Index()
{
return View();
}
public ActionResult GetPerson()
{
Random r = new Random();
int i = r.Next(0, people.Count);
if (Request.IsAjaxRequest())
{
return PartialView(people[i]); //return partial if it's a ajax call
}
else
{
return View(people[i]); // return view if it's NOT a ajax call
}
}
}
Index View:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Ajax.ActionLink("replace", "GetPerson", new AjaxOptions { UpdateTargetId = "replaceMe", HttpMethod = "Get", InsertionMode = InsertionMode.Replace})
<div id = "replaceMe"></div>
Partial View:
#model MvcApplication1.Controllers.Person
<div>
Name : #Model.Name <br />
Age : #Model.Age
</div>

When i try to execute foreach statement i am getting -Object reference not set to an instance of an object

i am working on mvc project. In my controller i am calling my stored procedure from terms class and I am returning Index page if it returns true or return terms page if it returns false.
Calling stored procedure in terms page :
public class Accept
{
public void Check()
{
using (var ctx = new termsEntities())
{
ctx.usp_ChkTerms(8, new ObjectParameter("Accepted", typeof(bool)));
ctx.SaveChanges();
}
}
}
Now i am calling this in my controller :
public ActionResult App()
{
// calling Stored procedure from Model to class
var accept = new Accept();
accept.Check();
// checking if accepted is true then return view else return another view
AppEntities Accepted = new AppEntities();
AppTerm user = new AppTerm();
AppHist history = new AppHist();
user = (from AppTerm app in Accepted.AppTerms
where app.userID == 8
select app).ToList().FirstOrDefault();
if (user != null)
{
if (user.Accepted)
{
return View("Index");
}
else
{
return View("terms");
}
}
And this is the code i am using in my terms view :
#{
ViewBag.Title = "terms";
}
<html>
<body>
<ul>
#foreach ( var item in Model)
{
<div class="Page" onclick="location.href='#Url.Action("Info", new { id = item.ID })'">
span class="Col1">
<br />
#item.ID
</span>
<span class="Title">#item.Name</span>
}
</ul>
</body>
</html>
Here when condition is true it is displaying Index page but when condition falls and when it tries to display terms page i am getting Object reference not set to an instance of an object and error is pointing to foreach loop. so what mistake i am doing here? i need help..
It is ugly, but you may try
<div class="Page" onclick='location.href="#Url.Action("Info", new { id = item.ID })"'>
<div class="Page" onclick="location.href='#Url.Action("Info", new { id = item.ID })'">
Change this to:
<div class="Page" onclick="location.href='#Url.Action('LinkText','Info', new { id = item.ID })'">
Note the quote marks around Info
edit:
Added extra argument to link.

How do I call a partial view that expects a IEnumerable collection in a view that gets a model object?

I am trying to build a version of WordPress in MVC4. Currently I am working on the page view.
I've decided that in this view I should also include a menu of all the other pages that have been created.
This is my method for the showPage view:
public ActionResult showPage(string myTitle)
{
var query = from a in db.Pages
where a.title == myTitle
select a;
PageModels item = new PageModels();
item = query.FirstOrDefault<PageModels>();
if (item != null)
{
return View(item);
}
else
{
item.content = "No page with title :" + myTitle + " found.";
return View(item);
}
}
This is my method for the partial I am trying to render:
public ActionResult List()
{
return View(db.Pages.ToList());
}
This is how my view looks like:
#model Dynamic_Web_Pages.Models.PageModels
#{
ViewBag.Title = "Page Preview";
}
#Html.Partial("_ListPartial", new IEnumerable<Dynamic_Web_Pages.Models.PageModels>)
<div class="content">#Html.Raw(Model.content)</div>
Finally this is my partial:
#model IEnumerable<Dynamic_Web_Pages.Models.PageModels>
<div class="dropdown">
#foreach (var page in Model)
{
if(page.parent == 0)
{
<div class="btn-group">
<a class="btn dropdown-toggle" id="#Html.DisplayFor(modelItem => page.id)" role="button" data-toggle="dropdown" data-target="#" href="/#Html.DisplayFor(modelItem => page.title)" >#Html.DisplayFor(modelItem => page.title)
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="#Html.DisplayFor(modelItem => page.id)">
#foreach (var child in Model)
{
if (child.parent == page.id)
{
<li><a class="child" href="/#Html.DisplayFor(modelItem => child.title)" >#Html.DisplayFor(modelItem => child.title)</a></li>
}
}
</ul>
</div>
}
}
</div>
I get the following error in my view:
Cannot create an instance of the abstract class or interface 'System.Collections.Generic.IEnumerable<Dynamic_Web_Pages.Models.PageModels>'
What should be the second argument of the #Html.Partial be?
Your view accepted a single instance of PageModels
#model Dynamic_Web_Pages.Models.PageModels
#{
ViewBag.Title = "Page Preview";
}
and yet you are passing that in your partial that accepts an IEnumerable and so you got that exception.
Now based on our conversation in your comments, you can load the partial using jquery:
<script>
$("#navmenu").load('#Url.Action("List")');
</script>
where you replace this code:
#Html.Partial("_ListPartial", new IEnumerable<Dynamic_Web_Pages.Models.PageModels>)
// with this
<div id="navmenu"></div>
remove new IEnumerable
and just keep it as
Html.Partial("_ListPartial") in the view
So you should return PartialView instead of view see below code
public ActionResult List()
{
return PartialView (db.Pages.ToList());
}

Use A Cookie To Send PersonID To Different Page

Hey guys so atm when the user goes to the holiday page, they can do 1 of 2 things
1)use a drop down box to select 'person name' and click 'view' this will display all the current holidays for this person
2)click 'create new' which will bring the user to a create page which allows them to add a new holiday(from here they select person name from drop and and select what date from calender)
This all works, however if the user originally follows the first path of selecting a person name and clicking view(it will display their holidays) if they then take the path of 2 and click 'create' it will jump to the create page. however the drop down box will be back at 'select' i would like the existing person selected from the previous drop down to display in this drop down.
A cookie or url/parameter?
anyway Im stuck please help
I've tried a cookie.
[code]
[HttpGet]
public ViewResult Index(string sortOrder, int? currentPersonID)
{
var holidays = db.Holidays.Include("Person");
HolidayList model = new HolidayList();
if (currentPersonID.HasValue)
{
model.currentPersonID = currentPersonID.Value;
}
else
{
model.currentPersonID = 0;
}
model.PList4DD = db.People.ToList();
//hyperlink to sort dates in ascending order
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "date" : "";
var dates = from d in db.Holidays
where d.PersonId == currentPersonID.Value
select d;
switch (sortOrder)
{
case "date":
dates = dates.OrderBy(p => p.HolidayDate);
break;
}
model.HList4DD = dates.ToList();
var cookie = new HttpCookie("cookie_name", "currentPersonID");
Response.AppendCookie(cookie);
return View(model);
}
public ActionResult Create()
{
var cookie = Request.Cookies["cookie_name"];
if (cookie != null)
{
string value = cookie.Value;
//int? value = cookie.Value;
}
ViewBag.cookie = cookie.Value;
ViewBag.Id = new SelectList(db.People, "Id", "Name");
return View();
}
//tried to use the currentPersonID in index as an int but it woudlnt allow me.
[/code]
My View
[code]
#model HolidayBookingApp.Models.startANDend
#{
ViewBag.Title = "Create";
}
<p>
<span>#ViewBag.cookie</span>
<h2>Create</h2>
<form action ="ListHolidays" id="listHolidays" method="post">
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Holiday</legend>
<div>
#Html.LabelFor(model => model.PersonId, "Person")
</div>
<div>
#Html.DropDownListFor(model => model.PersonId,
new SelectList(ViewBag.Id, "Value", "Text"),
"---Select---"
)
#Html.ValidationMessageFor(model => model.PersonId)
</div>
<div>
#Html.LabelFor(model => model.HolidayDate)
</div>
<div>
#Html.TextBoxFor(model => model.HolidayDate)
#Html.TextBoxFor(model => model.endDate)
<script>
// Date.format = 'dd/m/yyy';
$("#HolidayDate").addClass('date-pick');
$("#endDate").addClass('date-pick');
//$('.date-pick').datePicker//({dateFormat: 'dd-mm-yy'}).val();
// clickInput: true
$(function () {
//3 methods below dont allow user to select weekends
$('.date-pick').datePicker(
{
createButton: false,
renderCallback: function ($td, thisDate, month, year) {
if (thisDate.isWeekend()) {
$td.addClass('weekend');
$td.addClass('disabled');
}
}
}
)
.bind('click',
function () {
$(this).dpDisplay();
this.blur();
return false;
}
)
.bind('dateSelected',
function (e, selectedDate, $td) {
console.log('You selected ' + selectedDate);
}
);
// HolidayDate is start date
$('#HolidayDate').bind('dpClosed',
function (e, selectedDates) {
var d = selectedDates[0];
if (d) {
d = new Date(d);
$('#endDate').dpSetStartDate(d.addDays(0).asString());
}
}
);
//end date is end date
$('#endDate').bind('dpClosed',
function (e, selectedDates) {
var d = selectedDates[0];
if (d) {
d = new Date(d);
$('#HolidayDate').dpSetEndDate(d.addDays(0).asString());
}
}
);
});
</script>
#Html.ValidationMessageFor(model => model.HolidayDate)
</div>
<p>
<input type="submit" value="Create"/>
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#*
<p>Current Person Selected is:
#TempData["currentPersonID"]
</p>*#
[code]
Once I get this going how can i get my drop down to store the value?
Any help?
Thanks
try to use the helper SelectList and pass to view a Model with a list of item and the id of the selected item.
#Html.DropDownList("name", new SelectList(Model.SomeList, "ItemValueId", "ItemDescription", Model.ItemValueId), new { #class = "someclass" })
To me, a cookie is a way of storing information across many different page and also if the user returns back after some time.
I would prefer using query string as the information needs to be passes from one page to other page. You can use javascript or jquery on 'Create' button click event, look to see if dropdown has a value, put it in a query string and redirect.
And I would suggest reading below:
http://www.codeproject.com/Articles/43457/Session-Cookie-Query-String-Cache-Variables-Unifie

I cannot get just the selected values from dropdownlists in my view back in my controller

I hava a view where I have a list of links, being each link a region where the companies has offices.
Everytime I select a region, I get a list of processes. For every process, I get a dropdowlist from where to choose a owner of the process and a list of checkboxs of tests to choose.
In my controller, I get string[] OwnerId as the values selected in the dropdowlists.
The thing is, I get all values from all dropdowlists, not just those that were selected. How can I get just the ones I selected??
This is my view
#using CTTModel
#using TestingTool.ViewModels
#model TestRunModel
#{
ViewBag.Title = "Create";
}
<h2>
Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Regions</legend>
#foreach (Region region in Model.Regions)
{
#Html.ActionLink(#region.Name, "Create", new { id = region.Id })<br />
}
<div class="editor-field">
#foreach (ProcessModel process in Model.Processes)
{
<h1>#process.Name</h1>
**List<User> users = ViewBag.Users;
<select id="OwnerId" name="OwnerId" >
#foreach (User user in users)
{
<option value="#user.Id">#user.Name</option>
}
</select>**
<table>
<tr>
#{
int cnt = 0;
foreach (TestModel testModel in process.Tests)
{
if (cnt++ % 3 == 0)
{
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
name="selectedTests"
value="#testModel.Id/#testModel.ProcessId/#testModel.RegionId"
#(Html.Raw(testModel.Active ? "checked=\"checked\"" : "")) />
#testModel.Name #:: #testModel.Description
#:</td>
}
#: </tr>
}
</table>
}
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<fieldset>
<legend>Test Screen</legend>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
And this is my controller. The Create Post does nothing yet, I'm trying to get the right values first.
//
// GET: /TestPreparation/Create
public ActionResult Create(int id = 1)
{
TestRunModel testRunModel = new TestRunModel();
foreach (Region region in _db.Regions)
{
testRunModel.Regions.Add(region);
}
TestRun testRun = _db.TestRuns.OrderByDescending(x => x.Id).First();
foreach (TestRunProcessRegion region in testRun.GetProcessesForRegion(_db.Regions.Single(i => i.Id == id)))
{
ProcessModel process = new ProcessModel
{
Code = region.ProcessRegion.Process.Code,
Description = region.ProcessRegion.Process.Description,
Name = region.ProcessRegion.Process.Name,
Process = region.ProcessRegion.Process.Id
};
foreach (SubProcess subProcess in region.ProcessRegion.Process.SubProcesses)
{
foreach (Risk risk in subProcess.Risks)
{
foreach (Test test in risk.Tests)
{
TestModel testModel = new TestModel
{
Id = test.Id,
Name = test.Name,
Description = test.Description,
ProcessId = region.ProcessRegion.Process.Id,
RegionId = region.ProcessRegion.Id
};
process.Tests.Add(testModel);
}
}
}
testRunModel.Processes.Add(process);
}
var users = new List<User>();
foreach (User user in _db.Users)
{
users.Add(new User
{
Id = user.Id,
Name = user.Name,
});
}
ViewBag.Users = users;
return View(testRunModel);
}
//
// POST: /TestPreparation/Create
[HttpPost]
public ActionResult Create(string[] OwnerId, string[] selectedTests, string[] processes)
{
if (ModelState.IsValid)
{
//_db.TestRunStatus.Add(testrunstatus);
//_db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
The reason why you are not getting any data back is because the method signature of your Post Action needs to be
public ActionResult Create(string OwnerId ...) //preferably int depending on what OwnerId is
This is because you only select one item out of the drop down box. So if you use this signature as opposed to string[], the Model binder will pass the selected value back to your action.
Having said this, it is better practice and the "MVC Way" to use,
Html.DropDownFor(x => x.UserID) it really makes things easier :)
This applies to all html input controls:
http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/examining-the-edit-methods-and-edit-view
UPDATE
I think the best thing to do would be to add an OwnerID to the ProccessModel class.
Becuase ProccessModel looks to be IEnumberable<ProccessModel> Processes contained in the ViewModel you can get the Model to Bind in the following way using the defult MVC model binder.
Phil Haack has bloged about binding to lists here:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Adapting from Phil's post I think you will have to do something like this:
<% for (int i = 0; i < Model.Processes.Count; i++) { %>
<%: Html.SelectListFor(model => model.Processes[i].OwnerID, (IEnumerable<SelectListItem>)ViewBag.Users) %>
<% } %>
Change the ViewBag.User to:
var users = _db.Users.Select(x => new SelectListItem(){
text = x.Name,
value = x.Value
});
Modify the Post Action:
[HttpPost]
public ActionResult Create(TestRunModel model)
{
foreach(var process in model.Porcesses)
{
process.OwnerID // This should be a user selected value
}
// code removed for brevity
}
I could help with getting the TestModel values if you like but I need to do some work now ;)

Resources