Reputation: 6607
I've seen many many questions here on SO during my research regarding to the same problem but none of the answers on any of them have helped me yet.
I've tried each of the answer solutions but none of them worked.
So I'll lay out the code piece by piece and what I've tried and where.
First, my view.. consists of 2 forms and a partial view, the main view here:
@using MyApplication.Select.Web.Helpers;
@model SearchUsersViewModel
@*Scripts*@
<script src="@Url.Content("~/Scripts/SearchEntity.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ManageUsers.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/FormValidation.js")" type="text/javascript"></script>
<script type="text/javascript">
var theme = "admin"; //(lender, school, admin, or public)
var cssArray = ["ManageUsers.css", "ActionIcons.css"];
//Invoke loadCSS Method from CSSLoader.js
loadCSS(theme, cssArray);
//Set active tab from NavigationAdmin.js
setActiveNavTab("tab01"); //(tab01, tab02, tab03, tab04, tab05, tab06)
document.getElementById("AdminPageHeaderIMG").className = "HeaderImageManageUsers";
</script>
<div>
<table id="" cellpadding="0" cellspacing="0" border="0">
<tr style="height: 60px;">
<td>
<div class="PageHeaderDescriptionDiv">
Welcome to My Application
</div>
</td>
</tr>
<tr style="height: 1px;">
<td class="ContentDividerHoriz"></td>
</tr>
</table>
<table class="SearchUsersMainTable" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td>
<div class="SearchByUserDataTable">
@using (Html.BeginForm()) {
@Html.HiddenFor(model => model.SearchFilterType)
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 30px;">
<td class="Header01">
User Search
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 20px;">
<td class="Header02">
Search By User Information
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 1px;">
<td class="ContentDividerHoriz_425"></td>
</tr>
</table>
<table id="searchByUserDataTable" cellpadding="0" cellspacing="0" border="0">
<tr style="height: 26px;">
<td class="leftColumn">
@Html.LabelFor(model => model.LastName)
</td>
<td class="rightColumn">
@Html.TextBoxFor(model => model.LastName, new { @class = "TextField_220" })
</td>
</tr>
<tr style="height: 26px;">
<td class="leftColumn">
@Html.LabelFor(model => model.Username)
</td>
<td class="rightColumn">
@Html.TextBoxFor(model => model.Username, new { @class = "TextField_220" })
</td>
</tr>
<tr style="height: 26px;">
<td class="leftColumn">
@Html.LabelFor(model => model.EmailAddress)
</td>
<td class="rightColumn">
@Html.TextBoxFor(model => model.EmailAddress, new { @class = "TextField_220" })
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td id="filterByUserError" style="width: 300px; color: #ff0000;"></td>
<td align="right" style="width: 50px;">
<div>
<input id="filterByUserButton" type="submit" value="Search" />
</div>
</td>
<td style="width: 75px;"></td>
</tr>
</table>
}
</div>
</td>
<td style="width: 20px;"></td>
<td>
<div class="SearchByEntityDataTable">
@using (Html.BeginForm()) {
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 28px;">
<td style="width: 425px;"></td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 20px;">
<td class="Header02">
Search By Entity Information
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 1px;">
<td class="ContentDividerHoriz_425"></td>
</tr>
</table>
<table id="searchByEntityDataTable" cellpadding="0" cellspacing="0" border="0">
<tr style="height: 26px;">
<td class="leftColumn">
@Html.LabelFor(model => model.EntityTypeID)
</td>
<td class="rightColumn">
@Html.DropDownListFor(model => model.EntityTypeID, new SelectList(Model.EntityTypes, "ID", "Name"), new { id = "entityTypeDropDown", @class = "DropDown_220" })
</td>
</tr>
<tr style="height: 26px;">
<td class="leftColumn">
@Html.LabelFor(model => model.SearchField, new { id = "entityTypeSearchLabel"})
</td>
<td class="rightColumn">
@Html.TextBoxFor(model => model.SearchField, new { id = "entityTypeSearchField", @class = "ui-widget TextField_220" })
</td>
</tr>
<tr style="height: 26px;">
<td class="leftColumn"></td>
<td class="rightColumn"></td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td id="filterByEntityError" style="width: 300px; color: #ff0000;"></td>
<td align="right" style="width: 50px;">
<div>
<input id="filterByEntityButton" type="submit" value="Search" />
</div>
</td>
<td style="width: 75px;"></td>
</tr>
</table>
}
</div>
</td>
</tr>
</tbody>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 1px;">
<td class="ContentDividerHoriz"></td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 33px;">
<td style="width: 870px;">
<div class="TelerikGridHeaderBkgd">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="Header01" style="width: 150px; padding: 0px 0px 4px 5px;">
User Search Results
</td>
<td style="width: 10px;"></td>
<td style="width: 710px;">
<table cellpadding="0" cellspacing="0" border="0">
<tr style="height: 4px;">
<td style="width: 710px;"></td>
</tr>
</table>
<table>
<tr style="height: 20px;">
<td style="width: 188px;">
@*Resend Invitation*@
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ActionIcon_ResendInvitationOn"></td>
<td style="padding-left: 5px; padding-right: 10px;">
<span class="SearchUsersLegendText">= Resend Invitation</span>
</td>
</tr>
</table>
</td>
<td style="width: 140px;">
@*Account Approved Status*@
<table width="140" cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ActionIcon_AccountStatusLegend"></td>
<td style="padding-left: 5px; padding-right: 10px;">
<div class="SearchUsersLegendText">
<span>= </span>
<span style="color: #839f1b;">Active</span>
<span> / </span>
<span style="color: #d6161f;">Inactive</span>
</div>
</td>
</tr>
</table>
</td>
<td style="width: 162px;">
@*Account Lock Status*@
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ActionIcon_UnlockAccountOn"></td>
<td style="padding-left: 5px; padding-right: 10px;">
<span class="SearchUsersLegendText">= Unlock Account</span>
</td>
</tr>
</table>
</td>
<td style="width: 170px;">
@*Reset Password*@
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ActionIcon_ResetPasswordOn"></td>
<td style="padding-left: 5px; padding-right: 10px;">
<span class="SearchUsersLegendText">= Reset Password</span>
</td>
</tr>
</table>
</td>
<td style="width: 145px;">
@*Edit Account*@
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ActionIcon_EditOn"></td>
<td style="padding-left: 5px; padding-right: 10px;">
<span class="SearchUsersLegendText">= Edit Account</span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table>
<tr style="height: 6px;">
<td style="width: 710px;"></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</td>
</tr>
<tr>
<td style="width: 870px;">
<div id="searchResults">
@Html.Partial("SearchResultsPartial", Model)
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
SearchResultsPartial is here:
@model SearchUsersViewModel
@*Scripts*@
<link href="@Url.Content("~/Content/styles/TelerikCustom.css")" rel="stylesheet" type="text/css" />
@(Html.Telerik().Grid(Model.Users)
.Name("Users").TableHtmlAttributes(new { style = "width: 870px;"})
.Columns(columns => {
columns.Bound(o => o.EntityTypeName).Title("Entity Type");
columns.Bound(o => o.FirstName).Title("First Name");
columns.Bound(o => o.LastName).Title("Last Name");
columns.Bound(o => o.Username).Title("Username");
columns.Template(
@<text>
<a href="mailto:@item.EmailAddress" target="blank">@item.EmailAddress</a>
</text>).Title("Email").HtmlAttributes(new { style = "text-align: center" }).HeaderHtmlAttributes(new { style = "text-align: center" });
columns.Template(
@<text>
@{ if (@item.MembershipID == 0) {
<div class="ActionIcon_ResendInvitationOn" title="Resend Invitation" onclick="resendInvitation(@item.EntityID, @item.EntityTypeID, '@item.EmailAddress')"></div>
}
else {
if ((bool) item.IsApproved) {
<div class="ActionIcon_AccountStatusOn" title="Disable Account" onclick="setApprovalStatus('@item.Username', false)"></div>
}
else {
<div class="ActionIcon_AccountStatusOff" title="Enable Account" onclick="setApprovalStatus('@item.Username', true)"></div>
}
if ((bool) item.IsLockedOut) {
<div class="ActionIcon_UnlockAccountOn" title="Unlock Account" onclick="unlockAccount('@item.Username')"></div>
}
else {
<div class="ActionIcon_ResetPasswordOn" title="Reset Password" onclick="resetPassword('@item.Username')"></div>
}
<div class="ActionIcon_EditOn" title="Edit User" onclick="location.href='@Url.Action("Edit", "Admin", new { id = item.MembershipID, username = item.Username })'"></div>
}
}
</text>).Title("Actions");
columns.Bound(o => o.RowNumber).Hidden(true);
columns.Bound(o => o.MembershipID).Hidden(true);
columns.Bound(o => o.EntityID).Hidden(true);
columns.Bound(o => o.EntityTypeID).Hidden(true);
})
)
<div>
Total Rows:
@{
if ([email protected]()) {
@Html.Label("0")
}
else {
@Model.Users.First().TotalRows
}
}
</div>
Either form can do a search, it's a mutually exclusive search. Search by user data or search by entity data, but not both. The submit button on each form fire off their own javascript which runs an ajax call:
function filterByUserSearch() {
var lastName = document.getElementById("LastName");
var username = document.getElementById("Username");
var emailAddress = document.getElementById("EmailAddress");
var entityTypeID = document.getElementById("entityTypeDropDown");
var entityName = document.getElementById("entityTypeSearchField");
var searchFilterType = document.getElementById("SearchFilterType");
//alert("User Search");
entityTypeID.value = 0;
entityName.value = "";
searchFilterType.value = 0;
$.ajax({
url: "/Admin/Search/",
dataType: "json",
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: { LastName: lastName.value, Username: username.value, EmailAddress: emailAddress.value, SearchFilterType: searchFilterType.value },
success: function (result) {
$('#resultSpan').html('');
if(result.Success) {
$('#searchResults').html(result.Data);
if (result.ResultMessage != '<li></li>') {
$('#resultSpan').append($("<ul id='successMsg' style='list-style: none;' />").append(result.ResultMessage)).addClass("AjaxSuccessText");
$('#successMsg').css("padding-left", "0");
showResultPopUpDiv("ajaxResultWrapperDiv", "Action was Successful!");
}
}
else {
$('#resultSpan').append($("<ul id='errorMsg' style='list-style: none;' />").append(result.ResultMessage)).addClass("AjaxErrorText");
$('#errorMsg').css("padding-left", "0");
showResultPopUpDiv("ajaxResultWrapperDiv", "Ooops! There was an Error");
}
}
});
return false;
}
The controller action that gets called is here:
[HttpPost]
public JsonResult Search(SearchUsersViewModel model) {
try {
if (model.SearchFilterType == SearchFilterType.ByUserData)
return SearchForUsersByUserData(model, string.Empty);
if (model.SearchFilterType == SearchFilterType.ByEntityData)
return SearchForUsersByEntityData(model, string.Empty);
}
catch (Exception ex) {
ModelState.AddModelError("", ModelStateErrorUtility.WrapResultMessageForList(ex.Message));
}
return Json(new { Success = false, ResultMessage = ModelStateErrorUtility.GetModelStateErrors(ModelState) }, "application/json", JsonRequestBehavior.AllowGet);
}
private JsonResult SearchForUsersByUserData(SearchUsersViewModel model, string resultMessage) {
if (model.LastName != null || model.Username != null || model.EmailAddress != null) {
var listOfMatchingUsers = SearchUserService.SearchByUserData(model.LastName, model.Username, model.EmailAddress);
return PrepareSearchResultsForPartialView(model, listOfMatchingUsers, resultMessage);
}
throw new ArgumentNullException("Last Name, Username or Email Address must be entered for search");
}
private JsonResult SearchForUsersByEntityData(SearchUsersViewModel model, string resultMessage) {
if ((model.EntityTypeID == 1) || (model.EntityTypeID > 0 && model.SearchField != null)) {
var listOfMatchingUsers = SearchUserService.SearchByEntityData(model.EntityTypeID, model.SearchField);
return PrepareSearchResultsForPartialView(model, listOfMatchingUsers, resultMessage);
}
throw new ArgumentNullException("Entity Type must be entered for search");
}
private JsonResult PrepareSearchResultsForPartialView(SearchUsersViewModel model, ICollection<SearchUserResultsDTO> list, string resultMessage) {
return Json(new { Success = true, ResultMessage = ModelStateErrorUtility.WrapResultMessageForList(resultMessage), Data = RenderRazorViewToString("SearchResultsPartial", PrepareSearchResultsForModel(list, model)) }, "application/json", JsonRequestBehavior.AllowGet);
}
private static SearchUsersViewModel PrepareSearchResultsForModel(ICollection<SearchUserResultsDTO> listOfMatchingUsers, SearchUsersViewModel model) {
if (listOfMatchingUsers.Count != 0) {
model.Users = listOfMatchingUsers.Select(item => new UserEditViewModel(item)).ToList();
}
return model;
}
private string RenderRazorViewToString(string viewName, object model) {
ViewData.Model = model;
using (var sw = new StringWriter()) {
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
What I've tried:
Return on action method set to JsonResult
No content type specified in response, ajax call contentType: application/json; charset=utf-8
application/json; charset=utf-8 specified in response, no content type on ajax call
At this point I have 3 out of 4 ways working great, IE 8 sucks. So now I change the action method result.
Return on action method set to ActionResult
No content type specified in response, ajax call contentType: application/json; charset=utf-8
At this time I believe having a contentType specified in the ajax call itself is BAD.
application/json; charset=utf-8 specified in response, no content type on ajax call
I've even added the script file JSON2.js to the project and referenced the script in the view, this didn't do anything at all.
Here' the crazy part, I have another controller set up the same way, it builds teh json exactly the same, the only difference is the view that calls the ajax calls it slightly differently. Here's that Ajax method:
function setApprovalStatus(username, isApproved) {
$.ajax({
url: "/ELMAdmin/SetApprovalStatus/",
dataType: "json",
cache: false,
type: 'POST',
data: { username: username, isApproved: isApproved },
success: function (result) {
showManageUsersSuccessError(result);
}
});
}
It calls the same sub routine to render the grid, the only difference is the data that is returned. So I stripped all the text out of the partial view and IE still attempts to download the file. I feel like I've tried everything I know and now need some extra eyes to help point out what I am hoping is something obvious.
Upvotes: 1
Views: 1721
Reputation: 1352
Even though this question is a old, I thought I'll add one more suggestion, just in case anyone else is using ASP.NET MVC 3 or 4
and runs into this problem.
In my experience, when IE attempts to download the Json response as a file all you have to do to correct the problem is to add a reference to jquery.unobtrusive
to your view.
for example:
@Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
Once this is in place IE will no longer try to download the json response from a JsonResult controller action. No need to change the response type etc..
Upvotes: 0
Reputation: 26689
Ok, after looking at the sample and testing it out you need to add this content type to your ajax calls.
$.ajax({
url: "/ELMAdmin/Search",
cache: false,
type: 'POST',
contentType: "application/x-www-form-urlencoded;charset=utf-8",
data: { LastName: lastName.value, Username: username.value, EmailAddress: emailAddress.value, SearchFilterType: searchFilterType.value },
success: function (result) {
showManageUsersSuccessError(result);
}
});
notice the contentType
is application/x-www-form-urlencoded;charset=utf-8
. This should fix any issues with IE8.
In addition, though it could be just the sample, but you'll want to return false
in the onclick methods on your search buttons otherwise it will return true and then do a second post.
//Search By User Info
searchByUserBtn.onclick = function (event) {
entityName.className = "FieldOk";
entityName.value = "";
entityName.setAttribute("disabled", "disabled");
entityTypeID.value = 0;
entityTypeID.className = "FieldOk";
entityErrorMsgField.innerHTML = "";
filterByUserValidation();
return false;
}
Upvotes: 1