C# MVC5 JsonResult - javascript

I am trying to check for whether or not a CustomUrl is taken within my application. The problem is that when I click the submit button, nothing happens. I having a very difficult time understanding what I have done wrong.
To clarify: There is no javascript errors, the page does not get submitted. There are no errors anywhere of any sort for me to go on. Also, if I remove [Remote] section from AccountViewModel, so it does not attempt to make the check, the page will submit and it will also record the value in the database. So I'm fairly certain it has something to do with the validation I tried to put in place.
Here is the code:
MembersController.cs
public JsonResult IsCustomUrlInUse(string customUrl)
{
return Json(!UserManager.Users.Any(x => x.CustomUrl == customUrl), JsonRequestBehavior.AllowGet);
}
AccountViewModel.cs
[Required]
[StringLength(20, MinimumLength = 3)]
[Display(Name = "Custom URL")]
[Remote("IsCustomUrlInUse", "Members", ErrorMessage="Custom Url is already in use. Please choose another.")]
public string CustomUrl { get; set; }
Register.cshtml
#model Azularis.System.Events.Models.RegisterViewModel
#{
ViewBag.Title = "Register";
}
<h2>#ViewBag.Title.</h2>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
#using (Html.BeginForm("Register", "Members", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.CustomUrl, new { #class = "col-md-2 control-label"})
<div class="col-md-10">
#Html.TextBoxFor(m => m.CustomUrl, new { #class = "form-control"})
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
EDIT:
Answering questions in comments:
Network Tab in Developer options is capturing the entry of Custom URL field.
When I remove Remote, the validation for minimum characters kicks in and displays an error. Also, even while Remote is there, the minimum character limit still kicks in.
Example of the network tab: http://postimg.org/image/8e2e1hesx/
I have also removed the bundle in the view, to make sure this is not happening due to duplication, but still the same thing happens.
EDIT 2:
I added a Logging line in the IsCustomUrlInUse method, but it never gets triggered. Can it be that somehow I need to enable json call to the server? As in the MVC is blocking json calls until I enable it in settings somewhere?
EDIT 3:
I managed to produce this error, I'm not sure how as I am not able to replicate it, but maybe this helps:
2015-11-29 13:50:19.4659||System.Web.HttpException (0x80004005): The controller for path '/__browserLink/requestData/c244808430ad49a5afee6a0ecb685cf7' was not found or does not implement IController.
at System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType)
at System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName)
at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
2015-11-29 13:50:47.0584||System.Web.HttpException (0x80004005): The controller for path '/__browserLink/requestData/c244808430ad49a5afee6a0ecb685cf7' was not found or does not implement IController.
at System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType)
at System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName)
at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Related

How can I generate a PartialView for each click of a button? [duplicate]

The problem I will be describing is very similar to ones I already found (e.g. this post with nearly identical name) but I hope that I can make it into something that is not a duplicate.
I have created a new ASP.NET MVC 5 application in Visual Studio. Then, I defined two model classes:
public class SearchCriterionModel
{
public string Keyword { get; set; }
}
public class SearchResultModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
Then I created the SearchController as follows:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DisplaySearchResults()
{
var model = new List<SearchResultModel>
{
new SearchResultModel { Id=1, FirstName="Peter", Surname="Pan" },
new SearchResultModel { Id=2, FirstName="Jane", Surname="Doe" }
};
return PartialView("SearchResults", model);
}
}
as well as views Index.cshtml (strongly typed with SearchCriterionModel as model and template Edit) and SearchResults.cshtml as a partial view with model of type IEnumerable<SearchResultModel> (template List).
This is the Index view:
#model WebApplication1.Models.SearchCriterionModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SearchCriterionModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Keyword, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Keyword, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Keyword, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" id="btnDisplaySearchResults" value="Search" onclick="location.href='#Url.Action("DisplaySearchResults", "SearchController")'" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="searchResults">
</div>
As you can see, I added a div with id="searchResults" below the standard template and edited the button. What I want is to display the partial view SearchResults.cshtml in the div on the bottom, but only after the button is clicked. I have succeeded in showing a partial view there by using #Html.Partial("SearchResults", ViewBag.MyData), but it is rendered when the parent view is loaded for the first time and I set ViewBag.MyData in the Index() method already, which is not what I want.
Summary: On clicking the button, I will obtain some List of SearchResultModel instances (via database access) and then the partial view should be rendered, using this newly obtained data as model. How can I accomplish this? I already seem fail at the first step, that is reacting to the button click with the above code. Right now, I navigate to the URL ~/Search/DisplaySearchResults, but of course there's nothing there and no code-behind method is called.
In traditional ASP.NET I'd just have added a server-side OnClick handler, set the DataSource for a grid and show the grid. But in MVC I already fail with this simple task...
Update: Changing the button to #Html.ActionLink I can finally enter the controller method. But naturally since it returns the partial view, it's displayed as the whole page content. So the question is: How do I tell the partial view to be rendered inside a specific div on the client side?
Change the button to
<button id="search">Search</button>
and add the following script
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('#search').click(function() {
var keyWord = $('#Keyword').val();
$('#searchResults').load(url, { searchText: keyWord });
})
and modify the controller method to accept the search text
public ActionResult DisplaySearchResults(string searchText)
{
var model = // build list based on parameter searchText
return PartialView("SearchResults", model);
}
The jQuery .load method calls your controller method, passing the value of the search text and updates the contents of the <div> with the partial view.
Side note: The use of a <form> tag and #Html.ValidationSummary() and #Html.ValidationMessageFor() are probably not necessary here. Your never returning the Index view so ValidationSummary makes no sense and I assume you want a null search text to return all results, and in any case you do not have any validation attributes for property Keyword so there is nothing to validate.
Edit
Based on OP's comments that SearchCriterionModel will contain multiple properties with validation attributes, then the approach would be to include a submit button and handle the forms .submit() event
<input type="submit" value="Search" />
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('form').submit(function() {
if (!$(this).valid()) {
return false; // prevent the ajax call if validation errors
}
var form = $(this).serialize();
$('#searchResults').load(url, form);
return false; // prevent the default submit action
})
and the controller method would be
public ActionResult DisplaySearchResults(SearchCriterionModel criteria)
{
var model = // build list based on the properties of criteria
return PartialView("SearchResults", model);
}
So here is the controller code.
public IActionResult AddURLTest()
{
return ViewComponent("AddURL");
}
You can load it using JQuery load method.
$(document).ready (function(){
$("#LoadSignIn").click(function(){
$('#UserControl').load("/Home/AddURLTest");
});
});
source code link

Use MVC Session to store Client-side values (e.g. filter text) between visits

In an MVC View, is there an efficient way to store client-side values for use on subsequent page visits?
Typical scenario
An Index page has a table that's getting a bit long so I add a filter (I know paging is another option) and use an input control with some JavaScript to limit the table rows without having to perform another "Get" from the server.
This works fine but, if I navigate off (say) into an edit page then return back to the Index page, the filter is clearly no longer there.
After a bit of searching I never found anything simple so I post my meagre answer below.
The View contains a form at the top of the page into which a user can type filter text (on form "Get", text is set from a session value):-
<form id="frmEdit">
#Html.AntiForgeryToken()
<div class="form-group row">
<div class="col-sm-6">
#Html.ActionLink("Create New", "Create", null, new { #class = "nav-item nav-link" })
</div>
<label for="search" class="col-sm-2 col-form-label text-right">Filter</label>
<div class="col-sm-4">
<input type="text" placeholder="Filter" class="form-control" id="search" value=#Session["SparesSectionFilter"]>
</div>
</div>
</form>
A script section contains the filtering JavaScript but also a postback to the controller
#section Scripts{
<script type="text/javascript">
// on load
PerformFilter();
// hook up events
$(function () {
$("input#search").on("keydown keyup", function () {
PerformFilter();
// post back to session for reuse
$.post('SparesSections/Session_Add', { __RequestVerificationToken: $('[name=__RequestVerificationToken]').val(), itemName: 'SparesSectionFilter', itemValue: $("#search").val() });
});
})
</script>
}
I have a custom base-class for my controller into which I've added the following actions. These are usable from any controller using this class. The Razor view loads the session value but I've included a "Get" in the controller for client-side options.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Session_Add(string itemName, string itemValue)
{
Session.Add(itemName, itemValue);
return Json(new { itemName = itemName, itemValue = itemValue }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Session_Get(string itemName)
{
return Json(new { itemName = itemName, itemValue = Session[itemName] ?? string.Empty }, JsonRequestBehavior.AllowGet);
}

Why does my button click execute multiple times?

I have written this ajax code to send data to web service asmx. It works but with a single click, it inserts data multiple times and sometimes it takes 2,3 click to insert data.
.js
<script type="text/javascript">
function save()
{
$("button").click
(
function()
{
$.post
(
"http://localhost:82/ws/himher.asmx/InsertUsers",
{name: txtUserName.value, pwd: txtUserPwd.value},
//
);
}
);
}
</script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<label>User Name</label>
<input id="txtUserName" type="text" class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<label>Password</label>
<input id="txtUserPwd" type="text" class="form-control" />
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<button type="submit" onclick='save()' class="btn btn-primary pull-right">Register</button>
</div>
</div>
</div>
.cs:
public class himher : System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
//[ScriptMethod(UseHttpGet = false)]
public string InsertUsers(string name, string pwd)
{
try
{
basicoperation bop = new basicoperation();
return bop.insertUsers(name, pwd);
}
catch (Exception ex)
{
throw ex;
}
}
public string insertUsers(string Name, string Password)
{
string status;
String ConStr = ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString;
SqlConnection sqlCon = new SqlConnection(ConStr); // to make a connection with DB
SqlCommand sqlCom = new SqlCommand("InsertUsers", sqlCon); // now in order to perform action such as insert SP, we must create command object which needs command name and conncetion only
sqlCom.CommandType = CommandType.StoredProcedure; // you must tell the system that insertInfo is a storedprocedure
SqlParameter sqlParamName = new SqlParameter("#UserName", Name);
SqlParameter sqlParamPwd= new SqlParameter("#Password", Password);
sqlCom.Parameters.Add(sqlParamName);
sqlCom.Parameters.Add(sqlParamPwd);
try
{
sqlCon.Open();
int i= sqlCom.ExecuteNonQuery(); // executenonquery is used for INSERT, UPDATE, DELETE
//sqlCom.ExecuteScalar(); // used to pick or read a single value from procedure
// Response.Write("Done");
sqlCon.Close();
status= "Success";
}
catch (Exception ex)
{
//response.Write(ex.Message);
status = ex.Message;
}
return status;
}
}
You have two bindings to a saving function, one of them is binded when you click on your button. Rewrite your JS like this:
<script type="text/javascript">
function save()
{
$.post(
"http://localhost:82/ws/himher.asmx/InsertUsers",
{name: txtUserName.value, pwd: txtUserPwd.value}
);
}
</script>
This way your save function will do only saving logic. Binding to call this function is done in HTML by <button type="submit" onclick='save()'>.
If you're going to release this code to users you really need to implement some duplicate action prevention rather than hope they just click it once. Ergo while you may find out why it insets multiples, you cannot rely on user behaviour to keep trash out of the database; they will hit a go slow and hammer that button in frustration. Even if you disable the button, they'll refresh and submit again. Dedupe your data before you insert - this is multi layer information security; even if they disable the script that stops them hammering the button, you don't accept the duplicates
Note, I don't offer this as a solution to a genuine "I click this once and 3 data are inserted" - fix that bug sure, but control the user behaviour with regards to your desire for data purity, within the server (where you're in total control)

HTML form submit a single item from a collection

I have a partial view with a view model that has a collection of sellers. I loop over all of the sellers to render the list. Here is the view model:
public class SellersPartialViewModel
{
public IList<OrderViewModel> Sellers { get; set; }
}
In the partial view I'm using Html.BeginCollectionItem("Sellers") when I loop through the collection and here is my code for the partial (FYI I've stripped away a lot of useless code that doesn't need to be seen):
<div id="sellers-list">
#{
var i = 0;
while (i < Model.Sellers.Count) {
var seller = Model.Sellers[i];
using (Ajax.BeginForm(MVC.Video.PurchaseShares(), purchaseSharesAjaxOptions, new { #class = "seller-form", id = "seller-form-" + i })) {
#using(Html.BeginCollectionItem("Sellers")) {
#Html.TextBoxFor(m => seller.Qty, new { #class = "buyer-qty" })
#Html.ValidationMessageFor(m => seller.Qty)
<input class="buyer-qty-submit" name="Qty" type="hidden" value="" />
<button type="submit">Buy</button>
}
}
}
i++;
}
}
</div>
This works fine for rendering the partial and getting the client-side validation working
however I want each seller to have the inputs named qty and orderId for a controller action called PurchaseShares(int orderId, int qty).
The only problem is the form is being submitted with the odd GUID like Sellers[5b5fd3f2-12e0-4e72-b289-50a69aa06158].seller.Qty which I understand is correct for submitting collections but I don't need to do that.
Right now I have some Javascript that is updating the class="buyer-qty" with whatever they select and it works fine but there has got to be a better way of doing this, no?
Thanks
Why are you using the Html.BeginCollectionItem helper if you don't want to submit collections?
You could have a partial representing your Order collection item (_Order.cshtml):
#model OrderViewModel
#Html.TextBoxFor(m => m.Qty, new { #class = "buyer-qty" })
#Html.ValidationMessageFor(m => m.Qty)
And in your main view simply loop through your collection property and render the partial for each element:
#model SellersPartialViewModel
<div id="sellers-list">
#foreach (var seller in Model.Sellers)
{
using (Ajax.BeginForm(MVC.Video.PurchaseShares(), purchaseSharesAjaxOptions, new { #class = "seller-form" }))
{
#Html.Partial("_Order", seller)
<button type="submit">Buy</button>
}
}
</div>
Now your controller action you are submitting to could directly work with the corresponding view model:
[HttpPost]
public ActionResult PurchaseShares(OrderViewModel order)
{
...
}
because:
[HttpPost]
public ActionResult PurchaseShares(int orderId, int qty)
{
...
}
kinda looks uglier to me but it would also work if you prefer it.
Also please notice that I have deliberately removed the Qty hidden field shown in your code as it would conflict with the input element with the same name. Also don't forget to include an input field for the orderId argument that your controller action is expecting or when you submit it could bomb. Also you could send it as part of the routeValues argument of the Ajax.BeginForm helper if you don't want to include it as an input field.

Client-server searching with jQuery and MVC

I have a view with two drop downlist which is used to search the description. The list of results are displayed in another view for now. I wish to generate the results in the same search view. I assume some AJAX or Jquery can be used to sort this out but don't know how. So, in this case how can the search result be displayed in the same view page?
Moreover, i have some doubt in Search controller. I want at least one drop down list to be selected (Both drop down list shouldn't be allowed null). How can i validate that part?
View
#using (Html.BeginForm("Search","Work",FormMethod.Get))
{
<fieldset>
<legend>Search</legend>
<div class="editor-label">
#Html.LabelFor(model => model.JobTypeID, "Job Type")
</div>
<div class="editor-field">
#Html.DropDownList("JobTypeID", "Select Job Type")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.JobPriorityID, "Job Priority")
</div>
<div class="editor-field">
#Html.DropDownList("JobPriorityID", "Select Job Priority")
</div>
<p>
<input type="submit" value="Search" />
</p>
</fieldset>
}
Controller:
[HttpGet]
public ActionResult Search(int? jobtypeid, int? jobpriorityid)
{
var vJobDescriptions = new List<JobDescription>();
if (jobtypeid != null && jobpriorityid != null )
{
vJobDescriptions = (from description in db.JobDescriptions
where (description.JobTypeID == jobtypeid && description.JobPriorityID == jobpriorityid)
select description).ToList();
}
else if (jobtypeid == null && jobpriorityid != null)
{
vJobDescriptions = (from description in db.JobDescriptions
where (description.JobPriorityID == jobpriorityid)
select description).ToList();
}
else if (jobtypeid != null && jobpriorityid == null)
{
vJobDescriptions = (from description in db.JobDescriptions
where (description.JobTypeID == jobtypeid)
select description).ToList();
}
else
{
vJobDescriptions = (from description in db.JobDescriptions
select description).ToList();
}
return View(vJobDescriptions);
}
One possibility is to use an Ajax.BeginForm instead of a normal form (don't forget to include jquery.js and jquery.unobtrusive-ajax.js scripts to your page):
#using (Ajax.BeginForm("Search", "Work", new AjaxOptions { UpdateTargetId = "results" }))
then you could have a placeholder for the results that we specified in the UpdateTargetId:
<div id="results"></div>
Now all that's left is to have your Search controller action return a PartialView and pass it the model containing the results of the search:
public ActionResult Search(int? jobtypeid, int? jobpriorityid)
{
var model = ...
return PartialView("_Result", model);
}
and of course the corresponding _Result.cshtml partial:
#model IEnumerable<MyViewModel>
...
Moreover, i have some doubt in Search controller. I want at least one
drop down list to be selected (Both drop down list shouldn't be
allowed null). How can i validate that part?
I would recommend you FluentValidation.NET but if you don't want to use third party libraries you could write a custom validation attribute that will perform this validation and then decorate one of the 2 view model properties that are bound to your dropdown lists with it.
Unfortunately if you decide to go the AJAX route, you will have to be able to display validation errors coming from the server in case there was something wrong. So it is the entire form that has to be put inside the partial.
Another approach that you could use is to simply reload the entire page using a standard form without AJAX. The results will be part of your initial view model as a collection property which will initially be null and after performing the search you will populate it with the results. Then inside the view you will test if the property is not null and if it isn't include the Partial that will take care of rendering the results:
#using (Html.BeginForm("Search", "Work", FormMethod.Get))
{
...
}
<div id="results">
#if (Model.Results != null)
{
#Html.Partial("_Results", Model.Results)
}
</div>
A basic approach to this would be to place the markup for your search results into a partial view, and return that from your Search ActionMethod. This would require you to change the last line of your search method to
return Partial(vJobDescriptions)
In your client-side script, you would do something along the lines of this:
var data = $("form").serialize();
$.get("/Search", data)
.complete(function(results) {
$("form").replace(results) };
With regards to the validation aspect you're looking for, I would consider separating your read model from the search command parameters.
public ActionResult Search(SearchModel search)
{
if (!ModelState.IsValid) // return view w/ invalid model
}
where your search params model would be along these lines:
[CustomValidation(typeof(SearchModel),
"OneNotNullValidator",
"One option must be selected"]
public class SearchModel
{
public int? JobTypeID { get; set;}
public int? JobPriorityID { get; set;}
public bool OneNotNullValidator()
{
return JobTypeID.HasValue || JobPriorityID.HasValue;
}
}
The CustomValidation attribute I've applied to the class may not be 100% correct on the specific syntax and name(s), but I hope the gist of it comes across.

Categories