MVC4 Web Grid Sorting and Pagination Issue - asp.net-mvc-4

I am using Web Grid in MVC4 web application. I have a search functionality in the page. Web grid works fine i.e Sorting and Paging works fine until there is no search performed. When a search is performed, then sorting the web grid does not sort those searched results alone but sorts the entire list of items.
I debugged and found that during click of Web grid header for sorting, it redirects to HttpGet method and not HttpPost.I am pretty sure that if HTTPPOST is hit, then this problem would vanish.
I tried searching in google but could not find any specific answers. Any help or pointers would be greatly appreciated. Hope I am clear on my problem.
Controller:
public ActionResult Index()
{
var item = GetAllActors();
return View(item);
}
[HttpPost]
public ActionResult Index(string SearchContion, FormCollection collection)
{
var item = GetAllActors();
List<ActorBE> listOfItems = new List<ActorBE>();
if (item != null && collection != null)
{
if (!string.IsNullOrEmpty(SearchContion))
{
List<string> searchResults = item.FindAll(s => s.ActorName.IndexOf(SearchContion, StringComparison.OrdinalIgnoreCase) >= 0).Select(p => p. ActorName).ToList();
foreach (var data in searchResults)
{
ActorBE actor = new ActorBE ();
actor = item.Where(l => l.ActorName == data).FirstOrDefault();
listOfItems.Add(actor);
}
return View(listOfItems);
}
else
{
return View(item);
}
}
else
{
return View();
}
}
View:
#model IEnumerable<Tool.DataService.ActorBE>
#{
ViewBag.Title = "Actor";
Layout = "~/Views/Shared/_Layout.cshtml";
WebGrid grid = new WebGrid(rowsPerPage: 50, canPage: true, canSort: true);
grid.Pager(WebGridPagerModes.All);
grid.Bind(Model, rowCount: Model.ToList().Count());
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div style="padding: 2px 2px 2px 2px;">
<fieldset>
<legend>Search</legend>
<header>
<div class="content-wrapper">
<div class="float-left">
<label style="display:inline;margin-right:5px">Actor Name</label>
#Html.TextBox("SearchContion")
<input type="submit" value="Search" name="Search" style="border-radius:5px;margin-left:5px;"/>
</div>
</div>
</header>
</fieldset>
</div>
#grid.GetHtml(htmlAttributes: new
{ id = "grid" },
tableStyle: "webgrid",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
firstText:"First",
lastText:"Last",
nextText:"Next",
mode: WebGridPagerModes.All,
previousText:"Previous",
rowStyle: "webgrid-row-style", columns: grid.Columns
(
grid.Column("ActorID",header:"Actor ID, style:"column", canSort:true),
grid.Column("ActorName",header:"Actor Name", style:"width:200px", canSort:true),
grid.Column
("",
header:"Actions",
format:#<text>
#Html.ActionLink("Edit", "Edit", new { id = item.ActorID })
#if (item.IsActive)
{
#Html.ActionLink("Deactivate", "Delete", new { id = item. ActorID })
}
</text>
)
)
)
}
When user searches some actor name, the search results are happening properly. Once search is over, when the user clicks on web grid headers, then search results are not retained properly but the control again goes to HttpGET Method and not to the HTTPPOST Method. This s the main problem.
Guide me on how to solve this problem

As a work around what you can do is when search is performed save the state of the Grid on server so that you can check for it while rendering the grid again, a similar question was answered here https://stackoverflow.com/a/15528219/335105

Related

.net-core Site with Partial Vue.js Frontend Form Fields

I am trying to create a site that has partial implementation of Vue.js, I am looking at using Vue.js as from what I understand it does not require a SPA site like other JS frameworks and I believe this framework ticks the boxes required.
I have a basic form that I want to be used to Create, Update and Delete objects.
The data is received via a SAL which calls an API, all Create, Update and Delete calls will go through the same API.
I have been able to do a HttpGet and HttpPost to Get and Update the data and show it on a simple form.
However when I try to display just a blank form I get the following errors:
Error Received
The code I have is as followed:
.cshtml page
#model bms.accessbookings.com.Types.ViewModels.ShowVenueViewModel
#{
ViewData["Title"] = "Venue";
}
<div class="m-grid__item m-grid__item--fluid m-wrapper">
<div class="m-content">
<div class="row">
<div id="venueForm">
Venue ID: <input type="text" v-model="venue.venueId" />
<br/>
Venue Name: <input type="text" v-model="venue.venueName" />
<br/>
Address: <input type="text" v-
model="venue.address.addressLine1"/>
<br/>
Line 2: <input type="text" v-
model="venue.address.addressLine2"/>
<br/>
City: <input type="text" v-model="venue.address.city"/>
<button type="button" v-on:click="sendToServer"
style="padding: 0; border: none; background: none;
cursor: pointer;">
<i class="la la-save"></i>
</button>
</div>
</div>
</div>
</div>
#section Scripts {
<script src="/js/venueform.js" type="text/javascript"></script>
}
.cs ViewModel:
public class ShowVenueViewModel
{
public int VenueId { get; set; }
public string VenueName { get; set; }
public Address Address { get; set; }
}
Address element contains Line1, Line2, City etc
VenueController Get and Post:
[HttpGet]
[Route("GetVenue")]
public ShowVenueViewModel GetVenue(int venueId = 0)
{
ShowVenueViewModel viewModel = new ShowVenueViewModel
{
Address = new Address()
};
if (venueId > 0)
{
viewModel = _venueImplementation.GetShowVenueViewModel(venueId);
}
return viewModel;
}
[HttpPost]
[Route("SaveVenue")]
public ShowVenueViewModel SaveVenue([FromBody]ShowVenueViewModel venueViewModel)
{
return venueViewModel;
}
.js page:
$(document).ready(function() {
var venueId = window.location.pathname.substring(7);
const vm = new Vue({
el: '#venueForm',
data () {
return {
venue: {}
}
},
props: {
currentevent: Object
},
created() {
Object.assign(this.venue, this.currentevent || {});
},
mounted: function() {
axios.get('/venue/GetVenue', { params: { venueId: venueId } }).then(response => {
this.venue = response.data;
});
},
methods: {
sendToServer: function () {
var self = this;
console.log("Venue getting updated");
axios.post('/venue/SaveVenue', self.venue)
.then(response => {
this.venue = response.data;
console.log("Venue Updated");
});
}
}
});
});
At the moment if the venue has items it returns these items without a problem and displays them into the form, I can edit the inputs and "save" them which then returns the newly saved information (save functionality not yet connected to my BLL / SAL).
However when no Venue object is returned (empty) the form does not display at all, and so there is no way to enter details onto a blank form to "save" and create a new venue.
Still really new to vue.js and I find it hard to find guides that are not pointing to CLI or SPA style sites.
I may have a lot of things wrong here, but if there are any pointers to help me I would be very grateful.
Ok well the error you're getting comes from your template (.cshtml page). You need either to make sure venue.address always has a value, or, safer, test for the presence of venue.address.addressLine1 before displaying it.
When you get an error in a render function, vue can't tell you the line number. But you know it's in the template somewhere and it's generally not hard to find. Keep your templates short :-) (the one shown is fine).

Display mvc partial view with errors on parent page

I have a page with multiple forms, each as a partial. I want to post each partial on submit. If there are errors, I want the validation errors to show in the partial as part of the main page i.e. I don't want to just see the partial on it's own page if there are errors. Am I correct in saying this behavior is only possible with an ajax post? How would I return the model state errors WITHOUT an ajax post, just a normal form post?
Edit:
Still seeing the partial on it's own page
Partial -
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
{
#Html.ValidationMessage("InvalidUserNamePassword")
<fieldset class="fieldset">
<div>
<label for="form-field-user_id">User ID</label>
<span>
#Html.TextBoxFor(x => x.Username, new { #class = "form-field__input form-field__input--text", #id = "form-field-user_id"})
</span>
</div>
</fieldset>
<div class="form-field__button">
<button id="loginButton" type="submit" class="button button--primary">Login</button>
</div>
}
<script>
$('#loginButton').click(function () {
$.ajax({
type: "POST",
url: '#Url.Action("Login", "Account")',
data: $('form').serialize(),
success: function (result) {
if (result.redirectTo) {
window.location.href = result.redirectTo;
} else {
$("#LoginForm").html(result);
}
},
error: function () {
$("#LoginForm").html(result);
}
});
});
</script>
Controller -
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (!ModelState.IsValid)
{
return PartialView("~/Views/Account/_Login.cshtml", model);
}
return Json(new { redirectTo = Url.Action("Index", "Profile") });
}
Yes, you are correct in saying this behavior is only possible with an ajax post.
There are a few problems with your current script meaning that you will not get the desired results.
Firstly your button is a submit button meaning that it will do a normal submit in addition to the ajax call unless you cancel the default event (by adding return false; as the last line of code in your script). However it would be easier to just change the button type to type="button"
<button id="loginButton" type="button" class="button button--primary">Login</button>
The ajax call will now update the existing page, however it will add the returned partial inside the existing <form> element resulting in nested forms which is invalid html and not supported. Change your html to wrap the main views form in another element
<div id="LoginFormContainer">
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
{
....
<button id="loginButton" type="button" class="button button--primary">Login</button>
}
</div>
and then modify the script to update the html of the outer element
success: function (result) {
if (result.redirectTo) {
window.location.href = result.redirectTo;
} else {
$("#LoginFormContainer").html(result); // modify
}
},
Finally, your rendering dynamic content so client side validation will not work for the returned form. Assuming your properties have validation attributes (for example the [Required] attribute on the Userame property), you need to reparse the validator after loading the content
var form = $('#LoginForm');
....
} else {
$("#LoginFormContainer").html(result);
// reparse validator
form.data('validator', null);
$.validator.unobtrusive.parse(form);
}
You noted that you have multiple forms on the page, in which case your ajax options should be
data: $('#LoginForm').serialize(),
or if your declare var form = $('#LoginForm'); as per the above snippet, then data: form.serialize(), to ensure you are serializing the correct form.
Side note: There is no real need to change the id attribute of the textbox (it will be id=Username" by default and you can simply use
#Html.LabelFor(x => x.UserName, "User ID")
#Html.TextBoxFor(x => x.Username, new { #class = "form-field__input form-field__input--text" })
or just #Html.LabelFor(x => x.UserName) of the property is decorated with [Display(Name = "User ID")]

Popup containt become blank at the time of moving the popup positions by selecting popup header

I am opening the view into popup control by using ContentUrl property of popup.
But when I try to move the positions of popup by selecting popup header. popup containt become blank still I am moving or selected the popup header. Same issue is present for in devexpress demo which was provided online.
I refered the following demo of devexpress popup controal
http://demos.devexpress.com/MVCxDockAndPopupsDemos/PopupControl/ContentUrl
I have written the following code
HomeController
public class HomeController : Controller
{
public ActionResult SendProduct(string rowId)
{
Product objProduct = new Product();
return View(objProduct);
}
[HttpPost]
public ActionResult SendProduct(Product objProduct)
{
return View(objProduct);
}
}
Product model
public class Product
{
public int ProductId { get; set; }
[Required]
public string Name { get; set; }
}
Index.chtml
#{
ViewBag.Title = "Home Page";
}
#using DevExpress.Web.Mvc.UI
#using DevExpress.Web.ASPxGridView
#using UI.Infrastructure.Resources;
<script type="text/javascript">
function OnBeginCallback(s, e) {
e.customArgs["rowId"] = 123;
}
function Click() {
pcSendProduct.PerformCallback();
if (!pcSendProduct.IsVisible())
pcSendProduct.Show();
}
</script>
Enumalate menu click
<div>
#Html.DevExpress().Button(settings =>
{
settings.Name = "btnSend";
settings.Width = 80;
settings.Text = "Find";
settings.UseSubmitBehavior = false;
settings.ClientSideEvents.Click = string.Format("function(s, e) {{ Click(); }}");
}).GetHtml()
</div>
#Html.DevExpress().PopupControl(
settings =>
{
settings.Name = "pcSendProduct";
settings.Width = 1050;
settings.Height = 550;
settings.HeaderText = "Plan Customer Interaction";
settings.CloseAction = DevExpress.Web.ASPxClasses.CloseAction.CloseButton;
settings.Styles.Header.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
settings.Styles.Header.VerticalAlign = System.Web.UI.WebControls.VerticalAlign.Middle;
settings.Styles.Header.Font.Size = 10;
settings.Modal = true;
settings.ShowHeader = true;
settings.ShowCloseButton = true;
settings.CloseAction = DevExpress.Web.ASPxClasses.CloseAction.CloseButton;
settings.Left = 1245;
settings.Top = 300;
settings.Styles.ModalBackground.BackColor = System.Drawing.Color.Transparent;
//settings.ContentUrl = Url.Action("SendProduct", "Home");
settings.ShowLoadingPanel = true;
settings.ClientSideEvents.BeginCallback = "OnBeginCallback";
}).GetHtml()
SendProduct.cshtml
#model Demo.Models.Product
#{
ViewBag.Title = "SendProduct";
}
<h2>SendProduct</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
#Html.HiddenFor(model => model.ProductId)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
In above code when i click on button popup get open on brouser properly. but when I select popup header for changing the positions position or any reason popup containt will get dissapered or popup will show blank containt. When I deselect popup header popup containt view display on popup
Please please give me solution so that w hen user select popup header then popup containt will not become blank. Popup must show the view .
Here devexress team six year ago gave an explanation that it is impossible when you use ContentUrl property because the control render it in the iframe.
You can write view content in the ViewContext but your controller should return PartialView.
public class HomeController : Controller
{
public ActionResult SendProduct(string rowId)
{
Product objProduct = new Product();
return PartialView(objProduct);
}
[HttpPost]
public ActionResult SendProduct(Product objProduct)
{
return PartialView(objProduct);
}
}
Popup settings
settings.SetContent(() =>
{
ViewContext.Writer.Write((Html.Action("SendProduct", "Home").ToHtmlString()));
});
//settings.ContentUrl = Url.Action("SendProduct", "Home");

Creating dynamic view from a controller in MVC

I have this controller and view:
public ActionResult DynamicView()
{
return View();
}
_
#model ChatProj.Models.GroupNumber
#{
ViewBag.Title = "DynamicView";
}
<h2>DynamicView</h2>
<fieldset>
<legend>Create a room</legend>
<div class="editor-label">
#Html.LabelFor(model => model.GroupId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.GroupId)
#Html.ValidationMessageFor(model => model.GroupId)
</div>
<input type="submit" value="DynamicView" />
</fieldset>
This is what it looks like on the page.
That's fine and dandy, but I would like to pass that number to a controller, which then passes it to a view. I would like to pass it to this view:
#using PagedList.Mvc;
#using ChatProj.App_Code;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Grupprum 1";
}
<h2>Grupprum 1</h2>
<style>
ul {list-style-type:circle;}
</style>
<div class="container">
<div class="nano chat">
<div class="content">
<ul id="discussion">
</ul>
</div>
</div>
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" disabled="disabled" />
<input type="hidden" id="displayname" />
</div>
#section scripts {
<!--Script references. -->
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-1.1.3.js"></script>
<script src="~/Scripts/jquery.nanoscroller.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.-->
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
$(".nano").nanoScroller();
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
$(document).ready(function () {
$("#sendmessage").removeAttr("disabled");
$('#message').keypress(function (e) {
if (e.keyCode == 13)
$('#sendmessage').click();
});
});
// Get the user name and store it to prepend to messages.
// Set initial focus to message input box.
$('#message').focus();
$.connection.hub.qs = { "room": "Grupprum 1" };
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
Specifically I would want it at $.connection.hub.qs = { "room": "Grupprum 1" }; to replace the 1.
So I've created these controllers which are faulty and incomplete:
[HttpPost]
public ActionResult DynamicView(int? roomNumber)
{
return View(GroupRoom(roomNumber));
}
public ActionResult GroupRoom(int roomNumber)
{
return View();
}
Does anyone know how I should change my controllers and views so that I'm able to insert a number in my DynamicGroup view, and get a view back based on the inserted number and the lastly mentioned view?
You could pass the number from the model to the new action just how #Matt Bodily did. But if you want to use a different model on your new view, you can use the below code instead:
public ActionResult GroupRoom(int roomNumber)
{
ViewBag.RoomNumber = roomNumber;
return View();
}
This way, you can use a different model for this page, if you want to. To display this ViewBag on the page, use this code anywhere you want:
#ViewBag.RoomNumber
I hope that helps you out.
How you have it set up the Model.GroupID will be set on the first view so change your controller like this
[HttpPost]
public ActionResult DynamicView(GroupNumber model)
{
//model.GroupId here will be what was selected on the first view
return RedirectToAction("GroupRoom", "Controller", new { GroupId = model.GroupId });
}
public ActionResult GroupRoom(int GroupId)
{
var model = //build your model based on the selected GroupId
return View(model);
}

Two Partial Views, posting data to actions causing issue

I am using MVC 4 for a project. one of my view has 2 partial views in it as:
<div id="fPassword">
#Html.Partial("_ForgotPassword",new UserForgotPasswordModel())
</div>
<div id="aLink">
#Html.Partial("_ActivationLink", new UserActivationLinkModel())
</div>
The Partial Views are as:
#model Me2Everyone.Web.UI.Models.User.UserForgotPasswordModel
#using (Html.BeginForm("ForgotPassword", "Home", FormMethod.Post, new { id = "personal-form" }))
{
<table width="100%" cellspacing="0" cellpadding="0" border="0" style="padding: 0px 0 20px 0;">
<tbody>
<tr>
<td width="375">
<div class="text-problem">
#Html.EditorFor(model => model.Email)
</div>
</td>
<td width="80" valign="bottom">
<input type="submit" value="Send" />
</td>
</tr>
</tbody>
</table>
}
and the second one is almost same to above the only difference is model type and action method of Form.
In Home Controller the ForgotPassword action is as:
[HttpPost]
public ActionResult ForgotPassword(UserForgotPasswordModel model)
{
if (ModelState.IsValid)
{
// Search email in database.
if(emailNotFound)
{
model.ErrorMessage = "Email not found.";
}
}
else
{
model.ErrorMessage = "Email address not found.";
}
return PartialView("_ForgotPassword", model);
}
Now when I was posting data to server, it was returning the partial view as independent not in the main View, so I looked around on net and found that I need to send ajax call for it, so I did it as in the parent view as:
<script type="text/javascript">
$(function () {
$("form").submit(function () {
if ($(this).valid()) {
var dataToSend = { model: { Email: $("#Email").val() } };
var serializedForm = $(this).serialize();
var isForgotPassword = true;
if ($(this).attr('action').indexOf("ForgotPassword") < 0)
isForgotPassword = false;
$.ajax({
url: $(this).attr('action'),
data: serializedForm,
type: 'POST',
success: function (data, textStatus, request) {
if (isForgotPassword == true)
$("#fPassword").html(data);
else
$("#aLink").html(data);
},
error: function (xhr, ajaxOptions, thrownError)
{
alert('error')
}
});
return false;
}
});
});
and also in my parent view I have:
#if(Model.ErrorMessage != "")
{
<script type="text/javascript">
alert(#Model.ErrorMessage);
</script>
}
The problem is when I give a valid email address, it works fine but when I provide an email address that doesnot exist in database, I get the alert that Email not found but when I click again on the submit button, the partial view is created independently in browser instead of being in parent view.
I tried by changing my parent view as:
<div id="fPassword">
#{Html.RenderAction("ForgotPassword");}
</div>
<div id="aLink">
#{Html.RenderAction("ActivationLink");}
</div>
but still its not working, any ideas or help on it?
Replace:
$("form").submit(function () {
with:
$(document).on('submit', 'form', function () {
This will ensure that your submit handler is registered in a lively manner. This means that after you refresh your DOM with contents coming from the AJAX call, the submit handler that you registered will continue to be associated to this new form. You could read more about the .on() method in the documentation. The .on() replaces the deprecated (and even removed in jQuery 1.9) .live() method which allowed you to achieve the same task. After the .live() method was deprecated, they introduced the .delegate() method with the same semantics. So depending on the jQuery version you are using, you should pick the right method.