Prevent from ajax call in MVC - javascript

Hi i am making my project in the asp.net,
basic expected behaviour -- fill form name , select master module(dropdown), select sub modules(dropdown), ajax passes id of submodule dropdown, create(submit).. it will submit all values,
now code is behaves---- fill form name, select master and submodule, while selecting submodule from second dropdown is calling the ajax call, and create action is called, so the form name and masterID(that is extracted from first dropdown) gone blank... so i need to prevent the ajax call to call the controller
Myform in razor view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Form</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FormName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormName)
#Html.ValidationMessageFor(model => model.FormName)
</div>
<select id="State" name="state"></select><br />
<p>
<input id="sbmt" type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My ajax call
$('#State').change(function () {
var a = $('#State').val();
var token = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: "/form/create",
type: "POST",
data: { __RequestVerificationToken: token, 'SubID': a }
});
});
My controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Form form, int SubID)
{
if (ModelState.IsValid)
{
form.CreatedBy = 1;
form.SubId = SubID;
form.CreatedDate = DateTime.Now;
form.ModifyBy = 1;
form.ModifyDate = DateTime.Now;
form.IsActive = true;
form.IsDeleted = false;
db.Forms.Add(form);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.MasterID = new SelectList(db.Departments, "MasterId", "ModuleName", form.MasterID);
return View(form);
}

From the comments, you want to be able to post back the value of the selected state when you submit the form. The best approach would be to use a view model that includes the property SubId and bind your dropdownlist to that property (the SubId parameter in you POST method is then not necessary.
#Html.DropDownListFor(m => m.SubId, ....)
The alternative is to rename the control to SubId so it matches the parameter in your POST method
<select name="SubId" ...>
and delete the unnecessary javascript function $('#State').change(function () {...

I guess the easiest way for you would be to use MVC's ajax helpers. Change your form like so:
#using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { OnSuccess = "OnSuccess" }))
{
...
And afterwards add a javascript handler for onsuccess
function OnSuccess(){
alert("success");
}
This is barely functional code just to get you started.
PS. Make sure you add a reference to ~/Scripts/jquery.unobtrusive-ajax.min.js

Related

How to stop redirect from partial page after form submit

Having an issue, I have a partialview named Manage, I load the partial in:
Controller AdminPanel, View AdminProfile like so:
<div id="tab-2" class="tab-pane">
#{Html.RenderPartial("~/Views/Account/Manage.cshtml");
}
</div>
When I click on save changes I get redirected to /Account/Manage, it should be /AdminPanel/AdminProfile?
Not sure if controller is returning the correct redirect or information for the json if I try to use a ajax script:
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: "";
ViewBag.HasLocalPassword = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
public ActionResult Manage(LocalPasswordModel model)
{
bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.HasLocalPassword = hasLocalAccount;
ViewBag.ReturnUrl = Url.Action("AdminProfile", "AdminPanel");
//ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalAccount)
{
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing
// OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
try
{
WebSecurity.CreateAccount(User.Identity.Name, model.NewPassword);
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
catch (Exception)
{
ModelState.AddModelError("", String.Format("Unable to create local account. An account with the name \"{0}\" may already exist.", User.Identity.Name));
}
}
}
// If we got this far, something failed, redisplay form
//return PartialView("Manage", model);
return Json(new { redirectTo = Url.Action("AdminProfile", "AdminPanel") });
}
This is the partial page that is loaded:
#model LocalPasswordModel
#{
ViewBag.Title = "Change Password";
}
<section class="hgroup">
<div class="panel-body">
<h1>#ViewBag.Title</h1>
<ul class="breadcrumb pull-right top-right">
<li>You're logged in as <strong>#User.Identity.Name</strong></li>
</ul>
<ul class="message-success">#ViewBag.StatusMessage</ul>
#using (Html.BeginForm("Manage", "Account", Form.Post, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-group">
<label class="col-sm-2 control-label">Old Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.OldPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">New Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.NewPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Confirm Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-2">
<input type="submit" class="btn btn-primary" value="Change password" />
</div>
</div>
}
</div>
</section>
The script below is placed in the _LayoutPage however as mentioned in the comments it is not doing anything.
<script type="text/javascript">
$.ajax({
type: "POST",
url: '#Url.Action("Manage", "Account")',
data: $('form').serialize(),
success: function (result) {
if (result.redirectTo) {
// The operation was a success on the server as it returned
// a JSON objet with an url property pointing to the location
// you would like to redirect to => now use the window.location.href
// property to redirect the client to this location
window.location.href = result.redirectTo;
} else {
// The server returned a partial view => let's refresh
// the corresponding section of our DOM with it
$(".tab-2").html(result);
}
},
error: function () {
}
});
</script>
All I am trying to do is stop the redirect after I submit, I have opened this to a bounty. It would be nice if you could also include how I could recieve the status messages back aswell after a user hits submit.
For instance user hits save changes > saves to the server > messages are then sent back to the partial page (all without redirecting)
Firstly, you need to decorate your POST action result as a post.
[HttpPost]
public ActionResult Manage(LocalPasswordModel model){
Secondly, you can get rid of the javascript altogether by
testing the validity of the form submit result within your controller
and this will manage whether the user is redirected or not.
[HttpPost]
public ActionResult Manage(LocalPasswordModel model){
if(condition to redirect){
return RedirectToAction("AdminProfile", "AdminPanel");
}
return View(model);
}
Lastly, I cannot see where you've actually put you jquery.
It needs to be put at the end of your form page, in your script section.
You mention it's in your layout page?
I think this link may also help, jQuery.post(), also event.preventDefault() is also useful to prevent form submission client side.
To control specifically where you want to redirect to, add redirects at every place there is a result, example: please note I am only returning to the adminprofile as this is what the op wants
if (changePasswordSucceeded)
{
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
return RedirectToAction("AdminProfile", "AdminPanel");
}
As a sidenote, you need to think through your program flow to decide how you want to manage invalid attempts, etc.
Is it because when you press save its doing a regular http post to the server to the Account/Manage page so you are being redirected. So your javascript is never actually running?
try using Ajax.BeginForm or changing the save button to use your javascript.
If you are posting your form data using AJAX then simply in the submit function
<form onsubmit="mysubmitfunc(event)" ....>
And in the submit function
function mysubmitfunc(e)
{
e.preventDefault();
/*
YOUR AJAX CODE GOES HERE
*/
return false; //last line in the function
}
Use ajax submit in the right way, how? attach to form submission event, catch submit event, stop the post, post it from javscript, and process the response at success.
Replace your _LayoutPage js with next, make sure you are using ID to identify the form, if not this javascript may affect other forms.
$(function() { //shorthand document.ready function
//recommended to use Id on form identification not class... search on google how to
//attach handler on form submit
$('.form-horizontal').submit(catchSubmit);
//custom handler for form submit
function catchSubmit(event) {
event.preventDefault(); //stop html form post
// ajax post with serialized form
$.post($(this).attr("action"),
$(this).serialize(),
postSuccessHandler
);
}
//redirect decizion
function postSuccessHandler(data) {
if (data.redirectTo) {
window.location.href = data.redirectTo;
} else {
$(".tab-2").html(data);
}
}
});
You can also move the html button to outside of the form.

Ajax form: OnSuccess, load partial div

I have this Ajax Form
#using(Ajax.BeginForm("AddAttendeeManual", "Attendee", new AjaxOptions { HttpMethod = "POST", OnSuccess = "doneManualEmail" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-group">
#Html.TextBoxFor(m => m.SelectedManualEmail.Email, new {#class = "form-control",PlaceHolder="Email"})
</div>
<input type="submit" id="btnManual"class="btn btn-default" value="Add>>" />
}
and a div, both are on same view
<div id="invitedPeoples" data-url="#Url.Action("InvitedAttendees", "Attendee", new { appointmentId = Model.AppointmentId })">
</div>
Everytime the form is submitted, I wanted to load the div that should have a partial view.
This is what I have done
function doneManualEmail(isRequestSucceded) {
$(#Html.IdFor(m=>m.SelectedManualEmail.Email)).val('');
var url = $("#invitedPeoples").data('url');
$.get(url, function (data) {
$('#invitedPeoples').html(data);
});
};
everytime the form is submitted, i get forwarded to InvitedAttendees method, but my partial view doesn't show up.
The first line of the jquery works fine but the part to load partial view doesn't work.
Any thing I'm missing?
Error in Console:

What are the possible ways to decouple knockoutjs view-model code from asp.net mvc views and to use the mvc server-side validations with it?

In order to use the view-models written at server-side at client-side, I am using knockoutjs mapping plugin. But I didn't wanted to write any js code in my view. For this reason, as such I don't have access to Model, I am now getting the model via ajax call with no values in the properties in the code below. This code is written in a external js file:
var Person = function () {
var self = this;
self.SetupKOBindings = function (flagkey) {
var source = null;
if (flagkey.val() === "True") {
this.GetViewModelFromServer = function () {
$.ajax(
{
url: "/Person/LoadData",
type: "GET",
async: false
}).
success(function (data) {
if (data !== null && data !== undefined) {
source = data;
flagkey.val("false");
}
});
}();
return ko.mapping.fromJS(source);
}
return null;
};
self.ViewModel = function (flagkey) {
this.model = self.SetupKOBindings(flagkey);
this.model.FullName = ko.computed(function () {
return this.model.FirstName() + " " + this.model.LastName();
});
this.model.ShouldShowFullName = ko.computed(function () {
return (this.model.FirstName() === null || this.model.LastName() === null) ? false : true;
});
this.model.Save = function () {
if ($(form).valid()) {
$.ajax(
{
url: "/Person/Index",
type: "POST",
contentType: "application/json",
data: ko.mapping.toJSON(model),
async: true
}).
success(function (data) {
ko.mapping.fromJS(model, data);
});
}
}
return this.model;
};
self.ApplyKOBindings = function (vm) {
ko.applyBindings(vm);
};
return this;
};
$(function () {
var PersonPage = Person();
var viewModel = PersonPage.ViewModel($('#GetViewModelFromServer'));
if (viewModel !== null) PersonPage.ApplyKOBindings(viewModel);
});
The problem I faced with this approach was everytime I did a post action, when the page loads, the same ajax request was fired to get the viewmodels from server and the same code runs which then binds the form with vm's properties which are empty.
To avoid this, I am using a hidden control's value as a flag to whether convert the server-side viewmodel to js object or not. So, On the first call, I set the value of flag to false.
Now to get the validation messages mentioned using data annotations, I have made the form as partial view and it uses ajax call to replace the content a div with id as Sample. The client-side validation using unobtrusive-validation and server-side validations works very well and also the knockout bindings.
Controller Code:
[HttpPost]
public ActionResult Index(PersonViewModel viewModel)
{
if (viewModel.Age < 10)
{
ModelState.AddModelError("Age", "bal bla bla");
}
if (ModelState.IsValid)
{
return PartialView("_Form", viewModel);
}
else
{
viewModel.GetViewModelFromServer = false;
return PartialView("_Form", viewModel);
}
}
Index View:
<div id="sample">
#Html.Partial("_Form")
</div>
Partial View:
#model MVCKOPractise.Models.PersonViewModel
<fieldset>
<legend>PeopleViewModel</legend>
#using (Ajax.BeginForm("Index", "Person", new AjaxOptions() { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "sample" }))
{
<div>
First Name:
#Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div>
Last Name:
#Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div data-bind="visible: ShouldShowFullName">
Full Name:
<div data-bind="text: FullName"></div>
</div>
<div>
Address:
#Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })<br />
#Html.ValidationMessageFor(model => model.Address)
</div>
<div>
Age:
#Html.TextBoxFor(model => model.Age, new { data_bind = "value: Age" })<br />
#Html.ValidationMessage("Age")
</div>
#Html.HiddenFor(model => model.GetViewModelFromServer)
<input type="submit" value="Save" />
}
</fieldset>
Although the above little sample works for me but I would like to know is this a good way to proceed to
Not to write js code in view and use ajax call to create a copy of viewmodel at client, use data- attributes to access any server-side stuff in javascript like I answered this.
Using the same validation written using data-annotations at server-side for the views. I know there is a knockout-validation and jquery-validation plugin available. But as mvc data-annotations convert validations to data- attributes which is then read by jquery.
I think I understand what you're trying to achieve and have done something similar in the past.
First off, I'm not sure where your model data is coming from - obviously server side but is this always a blank viewmodel? Personally I have implemented something along these lines to retrieve server side data:
In the controller:
[HttpGet]
public ActionResult MyAction(int id)
{
Return View();
}
[HttpGet]
public JsonResult MyActionData(int id)
{
return Json(Something.GetMyData(id) ?? new MyViewModel(), AllowGet);
}
That allows me to retrieve the data for the page as I perhaps normally would with a standard MVC request but allow it to be request in JSON. This way I'd get either the data for whatever I'm querying or I get a blank view model.
You can then add a standard post action with the viewmodel as the parameter without worrying about anything special for the ajax side of things.
Secondly, to handle server side only validation (I'm assuming here that you've got client side stuff working fine but there are more complex rules on the server side which require a post back) I actually started returning the validation data in the ModelState back to the client.
Not sure what the best transit for that would be in your scenario (dedicated post method for validation or a view model wrapper with validation data perhaps) but either way the validation object itself provides the field name that has failed validation and the relevant message to be displayed.
Using that data you could create a custom knockout mapping handler that takes the validation data and calculates it back to validation properties on your model (bearing in mind the field names map directly to the object structure in question) or you could have a jQuery handler that loops through the results looking for the fields with the relevant names and adds the validation classes on the fly.

Filter my #html.dropdownlistfor

Im working with a project and i need to filter my second dropdownlistfor based on my first dropdownlistfor value. Its simple to understand but hard to code it since i dont know jquery or javascript and im working in mvc asp.net, aswell as using a database in sql server where the data is located.
I need to filter my dropdown for project based on my dropdown for customer.
here is some of the code:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>TimeEntry</legend>
<div class="editor-label">
#Html.Label("Customer")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.TimeEntry.CustomerId, #customerSelectList)
#Html.ValidationMessageFor(model => model.TimeEntry.CustomerId)
</div>
<div class="editor-label">
#Html.Label("Project")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.TimeEntry.ProjectId, #projectSelectList, "[ - No project - ]")
#Html.ValidationMessageFor(model => model.TimeEntry.ProjectId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
public IEnumerable<Customer> Customers { get; set; }
public IEnumerable<Project> Projects { get; set; }
here is a code which i think is the code that is calling from the database but not really sure:
var customers = service.GetAllCustomers().ToList();
model.Customers = new SelectList(customers, "CustomerId", "Name");
var projects = service.GetAllProjects().ToList();
model.Projects = new SelectList(projects, "ProjectId", "Name");
Okay so you have a controller with a method that gives you the filtered projects like so:
public class FilterController:Controller {
public ActionResult Projects(int customerId) {
// I expect this call to be filtered
// so I'll leave this to you on how you want this filtered
var projects = service.GetAllProjects().ToList();
// at this point, projects should already be filtered with "customerId"
return Json(new SelectList(projects, "ProjectId", "Name"),
JsonRequestBehavior.AllowGet);
}
}
Then you call that method on the client like this:
// when the customer dropdown changes, you want to use the selected value
// and filter the projects dropdown - more like refresh it
$("#TimeEntry_CustomerId").change(function(){
refreshProjects($(this).val());
});
function refreshProjects(id) {
var projects = $("#TimeEntry_ProjectId");
$.get('#Url.Action("projects","filter")', {customerId:id},
function (result) {
// clear the dropdown
projects.empty();
// rebuild the dropdown
$.each(result, function (i, e) {
projects.append($('<option/>').text(e.Text).val(e.Value));
});
});
}

How to dynamically create checkboxes OR multi select on MVC 4

In this project we have two list, one for the dealer and the second for his products.
So far if you check one dealer we get back all the product for this specific dealer, it implemented in javascript (Json).
Html (5 :)
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>DealerProduct</legend>
<div class="editor-label">
#Html.LabelFor(model => model.DealerID)
</div>
<div class="editor-field">
#Html.DropDownList("DealerID", String.Empty)
#Html.ValidationMessageFor(model => model.DealerID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProductID)
</div>
<div class="editor-field">
#Html.DropDownList("ProductID", String.Empty)
#Html.ValidationMessageFor(model => model.ProductID)
</div>
<p>
<input type="submit" value="#Shared.Add" />
</p>
</fieldset>
}
JavaScript (Json :)
<script type="text/javascript">
$(document).ready(function()
{
$("#DealerID").change(function ()
{
var self = $(this);
var items="";
var url = "";
url = "#Url.Action("GetDealerProducts","DealerProduct")/"+self.val();
$.ajaxSetup({ cache: false });
$.getJSON(url,function(data)
{
$.each(data,function(index,item)
{
items+="<option value='"+item.Value+"'>"+item.Text+"</option>\n";
});
$("#ProductID").html(items);
$.ajaxSetup({ cache: true });
});
});
});
</script>
Controller :
public ActionResult GetDealerProducts(int id)
{
Dealer currentDealer = db.Dealers.Single(p => p.UserName == User.Identity.Name);
Dealer subDealer = db.Dealers.Single(s => s.DealerID == id);
List<Product> productOpenToSale = new List<Product>();
foreach (var item in currentDealer.ProductToSale)
if (!subDealer.ProductToSale.ToList().Exists(e => e.ProductID == item.ProductID))
productOpenToSale.Add(item.Product);
List<SelectListItem> productOpenToSaleList = new List<SelectListItem>();
productOpenToSale.ForEach(item => productOpenToSaleList.Add(new SelectListItem { Value = item.ProductID.ToString(), Text = item.ProductName }));
return Json(productOpenToSaleList, JsonRequestBehavior.AllowGet);
}
What I really need is adding (a pairing of) products dealer, which he can sell in the future.
Current option is to add products one by one, the desire is to give the possibility of multiple selection of all products.
Maybe something like dynamic checkBoxList or an foreach on a List from the ViewModel who add input - checkbox like this, but I don't know how to fill it after the dealer has selected on the first list and receive all the selected product back on submit..
10X for any help!! (&& sorry for my bad English :)
you can change this line of code
#Html.DropDownList("ProductID", String.Empty)
with something like this
<select id="SelectedItemIds" multiple="multiple" name="SelectedItemIds">
and having a viewModel on the server like this
class MyViewModel
{
public int[] SelectedItemIds { get; set; }
public int DealerID {get;set;}
}
and having a controller like this
[HttpPost]
public ActionResult Index(MyViewModel myViewModel)
{
return View();
}
I have similar situation and made it works here:
enter link description here
but I don't know how to pass the actual text back. I can only pass the index of selected items back to the controller. If you figure it out let me know.
Make sure your select name matches your variable name in the model.

Categories

Resources