I have a webgrid defined within a partial view. (This is an MVC 4 project.) The webgrid isn't the only thing in the partial view, so the webgrid is bound to a List within the model of the partial view.
The grid populates and sorts as it should when a column header is clicked, but when I repopulate the grid by a call to an action method (via a form post set up using Ajax.BeginForm) and then click on a column header, the grid contents disappear. (The action method queries a database using search criteria provided on the form by the user.)
What could be causing this? How can it be resolved?
The partial view starts with:
#model DonationImport.Models.GiftWithSplits
The contents of the partial view are within a form designated by:
#using (Ajax.BeginForm("SearchConstit", "Batch", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "constitSearchArea" }))
The WebGrid is defined as follows:
#{
var constitGrid = new WebGrid(source: Model.SearchResults, rowsPerPage: 100, ajaxUpdateContainerId: "constitGrid");
<div style="overflow-x: scroll; width: 100%;">
<div style="width: 1910px;">
#constitGrid.GetHtml(htmlAttributes: new { id = "constitGrid" },
columns: constitGrid.Columns(
constitGrid.Column(format: #<text><button onclick="selectConstituent('#item.Constituent_ID')" >select</button></text>, style: "searchResultsColumnWidth"),
constitGrid.Column("Constituent_ID", header: "ConstitID", style: "searchResultsColumnWidth", format: #<text>#Html.ActionLink((string)item.Constituent_ID, "PriorGifts", new { constitID = item.Constituent_ID }, new { target = "Prior Payments" })</text>),
constitGrid.Column("IsActive", header: "Active", style: "searchResultsColumnWidth"),
constitGrid.Column("LastName", header: "Last", style: "searchResultsColumnWidth"),
constitGrid.Column("FirstName", header: "First", style: "searchResultsColumnWidth"),
constitGrid.Column("MiddleInitial", header: "M.I.", style: "searchResultsNarrowColumnWidth"),
constitGrid.Column("Spouse", header: "Spouse", style: "searchResultsColumnWidth"),
constitGrid.Column("EmailAddress", header: "E-mail", style: "searchResultsWideColumnWidth"),
constitGrid.Column("AddressLine1", header: "Address Line 1", style: "searchResultsWideColumnWidth"),
constitGrid.Column("City", header: "City", style: "searchResultsWideColumnWidth"),
constitGrid.Column("State", header: "State", style: "searchResultsColumnWidth"),
constitGrid.Column("Zip", header: "Zip", style: "searchResultsWideColumnWidth"),
constitGrid.Column("SearchResultsText", header: "Search Results", style: "searchResultsWideColumnWidth"),
constitGrid.Column("IsActivePledge", header: "Pledge", style: "searchResultsNarrowColumnWidth"),
constitGrid.Column("ReceiptWarning", header: "Receipt Warning", style: "searchResultsWideColumnWidth"),
constitGrid.Column("IsMember", header: "Mbr", style: "searchResultsNarrowColumnWidth")),
alternatingRowStyle: "altrow")
</div>
</div>
}
When one clicks on:
<input type="submit" value="Search" />
within the form, the action method called is as follows:
[HttpPost]
public PartialViewResult SearchConstit(DonationImport.Models.GiftWithSplits g)
{
GiftWithSplits giftWithSplits = new GiftWithSplits(); // model (object) to be returned to the partial view
// send back gift data which we are currently using
giftWithSplits.GiftToVerify = g.GiftToVerify;
// search using provided data
string middleInitial = empty2null(g.GiftToVerify.SourceMiddleName);
if (!string.IsNullOrWhiteSpace(middleInitial))
middleInitial = middleInitial.Substring(0, 1); // just supply the initial, not the entire name
string zip = empty2null(g.GiftToVerify.SourceZip);
if (!String.IsNullOrWhiteSpace(zip))
zip = zip.Substring(0, 5); // we want only the first 5 digits of the zip
giftWithSplits.SearchResults = db.SearchDonor(null, g.GiftToVerify.DonationSourceCode, empty2null(g.SourceAcctMemo), null, empty2null(g.GiftToVerify.SourceLastName),
empty2null(g.GiftToVerify.SourceFirstName), middleInitial, empty2null(g.GiftToVerify.SourceAddress1),
empty2null(g.GiftToVerify.SourceCity), empty2null(g.GiftToVerify.SourceState), zip, empty2null(g.GiftToVerify.SourceCountry),
empty2null(g.GiftToVerify.SourceEmailAddress), empty2null(g.GiftToVerify.SourcePhone)).ToList();
if (giftWithSplits.SearchResults.Count == 0)
{
SearchDonor_Result emptyResult = new SearchDonor_Result();
emptyResult.Constituent_ID = "[None Found]";
giftWithSplits.SearchResults.Add(emptyResult);
}
return PartialView("_ConstitSearch", giftWithSplits);
}
As you can probably tell, I am a beginner in this MVC approach.
Additional thoughts (added later)...
It seems the source of the problem is that the links generated by the WebGrid HTML help for the column headers are based on the URL related to the action method which produced the grid. When the grid is first displayed, the link is: /Batch/Verify/34?sort=FirstName&sortdir=ASC since the grid was build as a part of the entire Verify view (coming out of the Verify action method). But, when one searches for manually-entered search criteria, the grid is build from the SearchConstit action method which populates only a partial view, so the URL in the column header link is now: /Batch/SearchConstit?sort=FirstName&sortdir=ASC.
Also, the "Search" button is associated with a POST because it needs to pass data from the form fields to use as search criteria; whereas, the WebGrid column headers are using a GET, and apparently there is no way to force them to POST. So, the problem seems to boil down to how to pass the search criteria from the form fields without posting the form.
I can think of a possible solution using Session variables, but I'm hesitant to do it that way.
Another option might be to abandon the use of the WebGrid.
Any ideas?
I found your question, when i was searching solution for same problem. I also faced same problem. I have used web grid to display data.
I have used filter/pagination. I used text box for search in grid also.
I am making post call for search. Webgrid was disappearing when i clicked filter and paging button. I google a lot and didn't find any solution. Finally i found solution so thought of posting.
You need to use get ajax call instead of post call that will solve you problem. Do not use beginform post for search.
Index.cshtml is my main view. Here i m rendering partial view (_GridPartialView.cshtml). Index view has one webgrid and search text box.
I am using ajax call to search in webgrid. Ajax code is mention below.
**Index.cshtml:**
#model List<Login>
#{
ViewBag.Title = "User";
}
<h2 style="background-color: grey">User</h2>
<table>
<tr>
<td>
<input type="text" id="txtSearch" placeholder=" Search..." onkeyup="Search()" />
#Html.ActionLink("Create New User", "CreateUser")</td>
</tr>
<tr>
<td>
<div id="divPartialView">
#Html.Partial("~/Views/Shared/_GridPartialView.cshtml", Model)
</div>
</td>
</tr>
</table>
<script type="text/javascript">
function Search() {
var searchVal = $("#txtSearch").val();
$.ajax({
type: "GET",
url: '/User/Search',
data: { searchString: searchVal },
dataType: 'html',
success: function (data) {
$('#divPartialView').html(data);
}
});
}
</script>
_GridUserPArtialView.cshtml: This is partial view used in index view.
#model List<Login>
<script src="../../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<style type="text/css">
.webGrid { margin: 4px; border-collapse: collapse; width: 500px; background-color:#FCFCFC;}
.header { background-color: #C1D4E6; font-weight: bold; color: #FFF; }
.webGrid th, .webGrid td { border: 1px solid #C0C0C0; padding: 5px; }
.alt { background-color: #E4E9F5; color: #000; }
.gridHead a:hover {text-decoration:underline;}
.description { width:auto}
.select{background-color: #389DF5}
</style>
#{
var grid = new WebGrid(null, canPage: true, rowsPerPage: 5, selectionFieldName: "selectedRow", ajaxUpdateContainerId: "grid");
grid.Pager(WebGridPagerModes.NextPrevious);
grid.Bind(Model, autoSortAndPage: true, rowCount: Model.Count);}
<div id="grid">
#grid.GetHtml(
tableStyle: "webGrid", mode: WebGridPagerModes.All,
firstText: "<< First",
previousText: "< Prev",
nextText: "Next >",
lastText: "Last >>",
headerStyle: "header",
alternatingRowStyle: "alt",
selectedRowStyle: "select",
columns: grid.Columns(
grid.Column("UserName", "User Name", style: "description"),
grid.Column("FirstName", "First Name"),
grid.Column("LastName", "Last Name"),
grid.Column("Action", format: #<text>
#if (#item.LoginUserName != "administrator"){
#Html.ActionLink("Edit", "Edit", new { id=item.LoginUserName})
#Html.ActionLink("Delete","Delete", new { id = item.LoginUserId},new { onclick = "return confirm('Are you sure you wish to delete this user?');" })
}
</text>, style: "color:Gray;" , canSort: false)
))
</div>
**UserController.cs**: This is Search action method inside. usercontroller. It is HTTPGET.
[HttpGet]
public PartialViewResult Search(string searchString)
{
List<Login> userListCollection = new List<Login_User>();
userListCollection = Login_User_Data.GetAllUsers();
if (Request.IsAjaxRequest())
{
if (!string.IsNullOrEmpty(searchString))
{
Log.Info("UserController: Index() Started");
var searchedlist = (from list in userListCollection
where list.FirstName.IndexOf(searchString,StringComparison.OrdinalIgnoreCase) >=0
|| list.LoginUserName.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0
|| list.LastName.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0
select list
).ToList();
return PartialView("~/Views/Shared/_GridPartialView.cshtml", searchedlist);
}
else
{
Log.Info("UserController: Search(Login_User user) Ended");
return PartialView("_GridPartialView", userListCollection);
}
}
else
{
return PartialView("_GridPartialView", userListCollection);
}
Log.Info("UserController: Search() Ended");
}
Hope this will help you. Let me know if you have any concern.
From: www.Dotnetmagics.com
The Solution is pretty simple, you need to do a GET, whenever you sort or page the web gird, it will try to get data and hit the a HttpGet Action, this will work as follows:
[HttpGet]
public ActionResult YourActionMethod()
{
return PartialView("YourView",YourModel);
}
The best part is, upon sorting, the request will send a parameter named as "sortBy" too, you can use this here and decide what you want to do with the binded Model with the grid. You can inspect what URL the Sort header will hit by using the "Developer Tools" in your browser.
Note : By default the action method it would be hitting would be same as the controller name.
Related
I'm trying to implement search functionality in my Form View. The search window opens in a popup (in a partialView) and asks for search queries(figure). Now the user enters all the search fields and POST request is made and eventually popup window displays a table of search result.
Form View (which has the button to open popup window)
#Ajax.ActionLink("Search current form", "SearchAction", new { #id = "SearchBtn" }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "result", InsertionMode = InsertionMode.Replace, OnSuccess = "openPopup" }, new { #class ="btn btn-primary"})<br />
<div id="result" style="display:none"></div>
<script type="text/javascript">
$(document).ready(function () {
$("#result").dialog({
autoOpen: false,
title: 'Search Window',
resizable:0,
width: 1000,
height: 700,
modal: true
});
});
function openPopup() {
$("#result").dialog("open");
}
</script>
SearchForm View (implemented as partial view)
#using (Html.BeginForm("SearchAction", "ViewModel", FormMethod.Post, new { #id = "searchform" }))
{
//some form elements
<div class="text-center">
<input type="submit" value="Go" class="btn btn-primary"/>
</div>
}
<div class="alert-danger">#ViewBag.emptyResult</div>
#if (Model != null)
{
//display the search results
}
Now to retain the popup I have to bind Go button to a ajax action in the same way as Form View. Also by reading this How to pass formcollection using ajax call to an action? I came to know that Ajax actions posts JSON data into the controller as opposed to key value pair which is easily accessible by FormCollection. So my question is how do I implement submit button(Ajax.Actionlink) in my search form so that it posts data into controller using FormCollection and retains the popup window as well.
Turns out I just needed to define a placeholder for the result table in my search popup.
<div id="showData" class="text-center table table-bordered bg-light"></div>
Now get your search results using Ajax call
function GetSearchResult() {
var searchParams = [];
//get search queries from textbox ids
$.ajax({
type: 'POST',
dataType: "json",
traditional: true,
data: {
s: searchParams
},
url: "/{controller name} /{action}",
success: function (result) {
var col = [];
if (isJson(result)) {
var sR = JSON.parse(result);
//create a html table using javascript and fill it which the result you got
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table); //table is what you created dynamically using javascript
}
else {
alert("No results found, Please try again");
}
}
});
}
Add this action in your controller
[HttpPost]
public JsonResult AjaxMethod(string value, string Id)
{
var updatedList = GetSearchResults(); //get search result from your repository
return Json(updatedList);
}
And as far as creating a html table thorugh javascript is concerned this helped me a lot!
I have a WebGrid in a Partial Page, and the Partial Page is called from the Controller inside another page, everything is working fine, except for the paging. When I try to go to next page records, it reverts back to the main page, without the Webgrid.
My Controller Code:
[HttpPost]
public ActionResult StatisticsHistory(FormCollection fc)
{
List<StatisticsHistoryData> hisData = new List<StatisticsHistoryData>();
........
if (hisData != null)
{
hisData = StatisticsHistoryMethods.HistoryDetailsBetweenDates(fromDate, toDate, environmentId);
return PartialView("_StatusHistoryGrid", hisData);
}
else
{
}
return StatisticsHistory();
}
My Webgrid in the Partial Page:
#model IEnumerable<AppFabricAdmin.Models.StatisticsHistoryData>
#{var Grid = new WebGrid(source: Model, canPage:true, rowsPerPage:2, canSort: true);
}
#{
if (Model.Count() > 0)
{
#Grid.GetHtml(tableStyle: "webGrid", headerStyle: "header", alternatingRowStyle: "alt", mode: WebGridPagerModes.All,
columns: Grid.Columns(Grid.Column("Environment", format: #<b>#item.Environment</b>, style: "AIDColumn"),
Grid.Column("Initial Status", format: #<b>#item.InitialStatus</b>, style: "FirstNameColumn"),
Grid.Column("Final Status", format: #<b>#item.FinalStatus</b>, style: "LastNameColumn"),
Grid.Column("Action Taken", format: #<b>#item.ActionTaken</b>, style: "EmailIDColumn"),
Grid.Column("Comments", format: #<b>#item.Comments</b>, style: "EmailIDColumn"),
Grid.Column("Time Stamp", format: #<b>#item.TimeStamp</b>, style: "EmailIDColumn")
))
}
else
{
<p>
No records found.</p>
}
I have also tried using #using (Ajax.BeginForm()) in the main page and hv also tried using ajaxUpdateContainerId:"grid" in the Grid, but nothing works. Pls help.
I have inserted the link buttons "Add New Record" and "Save All" at the bottom of my Webgrid. But I want them to be in the footer of the WebGrid. I have searched for this a lot, but found nothing. Can someone tell me how to add a link or button in the "footer" of a WebGrid.
Here is some code of my WebGrid
#model IEnumerable<MvcGrid2.Models.Employee>
#{
WebGrid grid = new WebGrid(
source: Model,
rowsPerPage: 4);
}
#grid.GetHtml(htmlAttributes: new { id = "WebGrid1" },
tableStyle:"gridTable",
headerStyle: "gridHead",
footerStyle: "gridFooter",
rowStyle: "gridRow",
alternatingRowStyle: "gridAltRow",
mode: WebGridPagerModes.All,
firstText: "<<",
previousText: "<",
nextText: ">",
lastText: ">>",
columns: grid.Columns(
#* grid.Column( columnName : "EmployeeId",
format: #<text>#item.EmpId</text>),*#
grid.Column(columnName: "Employee Id",
format: #<span>
<span id="spanEmpId_#(item.EmpId)">#item.EmpId</span>
#Html.TextBox("EmpId_" + (int)item.EmpId, (int)item.EmpId, new { #style = "display:none" })
</span>),
grid.Column(columnName: "Employee Name",
format: #<span>
<span id="spanEmpName_#(item.EmpId)">#item.EmpName</span>
#Html.TextBox("EmpName_" + (int)item.EmpId, (string)item.EmpName, new { #style = "display:none" })
</span>),
grid.Column(columnName: "Designation",
format: #<span>
<span id="spanEmpDesg_#(item.EmpId)" >#item.Designation</span>
#Html.TextBox("EmpDesg_" + (int)item.EmpId, (string)item.Designation, new { #style = "display:none" })
</span>),
grid.Column(columnName: "Action",
format: #<text>
Edit
Update
Cancel
Update
Cancel
Delete
</text>)
))
WebGrid doesn't have a modifiable footer per se. However if you view the tutorial on ASP.NET you'll see a way to make that happen in css.
What you can do is make your last row the same css class as your footer, or you can insert your buttons/links with javascript. Neither approach is clean, but as far as I can tell there is not a better way to accomplish your goal without rewriting the control. Many people have suggested looking into Telerik's controls, if you have/can get a license for their stuff.
I am new to MVC3. I have coded an application in which I display data into a webgrid with checkboxes. I am trying to find out how to send a particular row id the controller action method when I click on a checkbox. Any help appreciated.
This all depends on what you are trying to do. Typically, in the view, I will add a data attribute like data-rowid="###" and then use jQuery to capture the .click event. Then in the .click event, retrieve the clicked elements value for data-rowid and call .ajax to post the data to the controller.
This is a complete example where I have a WebGrid with the last column containing a "Remove" link that uses Ajax to call an action on the server. On completion of the Ajax request, the corresponding row is removed from the table. The MvcHtmlString is used to inject a span tag into the column. It contains an id value that is subsequently used to identify the row to be removed from the table.
<div id="ssGrid">
#{
var grid = new WebGrid(canPage: false, canSort: false);
grid.Bind(
source: Model,
columnNames: new[] { "Location", "Number", "Protection", "Methodology" }
);
}
#grid.GetHtml(
tableStyle: "webGrid",
headerStyle: "header",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("Location", "Location"),
grid.Column("Number", "Number"),
grid.Column("Protection", "Protection"),
grid.Column("Methodology", "Methodology"),
grid.Column(
format: (item) =>
new MvcHtmlString(string.Format("<span id='ssGrid{0}'>{1}</span>",
item.SecondarySystemId,
#Ajax.RouteLink("Remove",
"Detail", // route name
new { action = "RemoveSecondarySystem", actionId = item.SecondarySystemId },
new AjaxOptions {
OnComplete = "removeRow('ssGrid" + item.SecondarySystemId + "')"
}
)
)
)
)
)
)
</div>
<script>
function removeRow(rowId) {
$("#" + rowId).closest("tr").remove();
}
</script>
Below I have given the controller, model and view. After run, grid is displaying with values, but I need to edit the values and delete the values on same page. I have searched and seen some example, in that for edit, delete they creating separate index but mine need is to edit and delete should done on same page instead of another page. Please give me a solution.
Controller:
public ActionResult Index()
{
var PersonList = new List<Person>()
{
new Person(){Name="A", Age=20,Id =1},
new Person(){Name="B",Age=45,Id =2},
new Person(){Name="C", Age=30,Id =3},
new Person(){Name="D",Age=55,Id =4},
new Person(){Name="E", Age=30,Id =5},
new Person(){Name="F",Age=25,Id =6},
new Person(){Name="G", Age=30,Id =7},
new Person(){Name="H",Age=25,Id =8},
};
return View(PersonList);
}
Class :
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
View :
#model IEnumerable<edit.Models.Person>
#{
ViewBag.Title = "Index";
}
<html>
<head>
<title>Index</title>
<style type="text/css">
.webGrid { margin: 4px; border-collapse: collapse; width: 300px; }
.header { background-color: #E8E8E8; font-weight: bold; color: #FFF; }
.webGrid th, .webGrid td { border: 1px solid #C0C0C0; padding: 5px; }
.alt { background-color: #E8E8E8; color: #000; }
.person { width: 200px; font-weight:bold;}
</style>
</head>
<body>
#{
var grid = new WebGrid(Model, canPage: true, rowsPerPage: 5);
grid.Pager(WebGridPagerModes.NextPrevious);
#grid.GetHtml(tableStyle: "webGrid",
headerStyle: "header",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("Name", "Given Name", canSort: true, format:#<b>#item.Name</b>, style: "person"),
grid.Column("Age", "How Old?", canSort: true)
));
}
</body>
</html>
#Yasser, it is very dangerous to implement a DELETE via a GET link. A search engine crawling the page might delete all your information.
It is much better to implement a POST operation. Here is an example:
In the View:
#functions{
string Delete(dynamic p)
{
string actionController = Url.Action("Delete", "Admin", new {id=p.AccountId});
return "<form style='display:inline;' method='post' action='" + actionController + "'><input type='submit' value='Delete' onclick=\"return confirm('Are you sure?')\"/></form>";
}
}
...
grid.Column(header: "", format: p => Html.Raw(Delete(p)))
In the Controller:
[HttpPost]
public ActionResult Delete(int id)
{
PerformDelete(id);
return RedirectToAction("Index");
}
Here is something you can start with,
You will have to first generate two action link called "Edit" and "Delete" along with each record in the webgrid.
See this tutorial for that.
something like this
grid.Column(format: (item) => Html.ActionLink("Edit", "ActionName", new { param1 = "send id here", param2 = "xtra param" }))
grid.Column(format: (item) => Html.ActionLink("Delete", "ActionName2", new { param1 = "hello", param2 = "bye" }))
Hope this helps.
Here you go...
http://www.dotnet-tricks.com/Tutorial/mvc/E2S9150113-Enhancing-WebGrid-with-Insert-Update-and-Delete-Operations.html
I think you are looking for this.
You can try this inline editable gridview using asp.net mvc and knockoutjs:
www.anhbui.net/blog?id=kojs-1