ASP.NET MVC Submitting Form Using ActionLink - asp.net

I am trying to use link to submit a form using the following code:
function deleteItem(formId) {
// submit the form
$("#" + formId).submit();
}
Basically I have a grid and it displays a number of items. Each row has a delete button which deletes the item. I then fire the following function to remove the item from the grid.
function onItemDeleted(name) {
$("#" + name).remove();
}
It works fine when I use a submit button but when I use action link the JavaScript from the controller action is returned as string and not executed.
public JavaScriptResult DeleteItem(string name)
{
var isAjaxRequest = Request.IsAjaxRequest();
_stockService.Delete(name);
var script = String.Format("onItemDeleted('{0}')", name);
return JavaScript(script);
}
And here is the HTML code:
<td>
<% using (Ajax.BeginForm("DeleteItem",null, new AjaxOptions() { LoadingElementId = "divLoading", UpdateTargetId = "divDisplay" },new { id="form_"+stock.Name }))
{ %>
<%=Html.Hidden("name", stock.Name)%>
<a id="link_delete" href="#" onclick="deleteItem('form_ABC')">Delete</a>
<% } %>
</td>
My theory is that submit button does alter the response while the action link simply returns whatever is returned from the controller's action. This means when using submit the JavaScript is added to the response and then executed while in case of action link it is simply returned as string.
If that is the case how can someone use action links instead of submit buttons.
UPDATE:
Seems like I need to perform something extra to make the action link to work since it does not fire the onsubmit event.
http://www.devproconnections.com/article/aspnet22/posting-forms-the-ajax-way-in-asp-net-mvc.aspx

My guess is the MS Ajax form knows how to handle a JavaScriptResponse and execute the code whereas your plain old Action link, with no relationship to the AjaxForm, does not. I'm pretty sure the MS ajax library essentially eval()s the response when it sees the content type of javascript being sent back.
Since you have no callback in your deleteItem() method there is no place for the script to go. To fix you'll have to manually eval() the string sent back which is considered a bad practice.
Now I'm not familiar with the MS Ajax library to be certain of any of this but what your doing is possible. I'd do things differently but don't want to answer with a "my way is better" approach ( especially because your blog has helped me before ) but I'd like to show this can be easier.
I'd ditch the form entirely and use unobtrusive javascript to get the behavior you want. IN psuedo jqueryish ( don't know ms ajax ) code:
function bindMyGrid() {
$('.myDeleteLink').onclicksyntax( function() {
//find the td in the row which contains the name and get the text
var nameTdCell = this.findThisCellSibling();
//do an ajax request to your controller
ajax('myUrl/' + nameTdCell.text(), function onSuccessCallback() {
//get the tr row of the name cell and remove it
nameTdCell.parent().remove();
});
});
}
This also gains the benefit of not returning javascript from your controller which some consider breaking the MVC pattern and seperation of concerns. Hope my psuedo code helps.

Try without the UpdateTargetId property in the AjaxOptions (don't specify it)
new AjaxOptions() { LoadingElementId = "divLoading" }

What about just change look of a standard or using some css class? It'll look like a link and you'll avoid some problems you get with anchors - user will be able to click on it by a mouse wheel and open that link in a new tab/window.

Related

asp.net mvc - ajax form (Ajax.beginform) in partial view redirects to show json response when the partial view is rendered via ajax

I have a partial view with an ajax form
#using (Ajax.BeginForm("SaveSettings", "Config", new AjaxOptions
{
HttpMethod = "Post",
OnSuccess="settingsUpdateSucces"
}, new { enctype = "multipart/form-data", id = "SaveSettings" }))
{
#Html.HiddenFor(m => m.Id)
//other fields go here
<button id="btnSaveSettings" type="submit" >Save Settings</button>
}
This partial view and the form works in one scenario but not the other.
Let me explain both scenarios
Scenario 1:
The partial page is rendered using "Html.Partial" in an asp.net page
relevant parts of the page
#{
ViewBag.Title = "Edit";
Layout = "~/Layout/V1.cshtml";
}
<!--other non-relevant markup and code here-->
<div>
<h3>Settings</h3>
#Html.Partial("_Settings")
</div>
In this scenario the ajax form works without any problems and the page is not redirected.
This code has been running for over 6 months and no issues whatsoever.
Scenario 2
Now, I am trying to get the same partial to work on another new page.
This is a new page - which works like a wizard.
So, in one of the steps a partial page is added (using Html.Partial). This page has a dropdown, when selected, another partial page is rendered via ajax.
One of the selection loads the above mentioned "_Settings" partial page using this code
function loadPartial(id) {
$.get('/Config/_Settings?sid=' + id, function (data) {
$('#partialSettingsPlaceHolder').html(data);
});
}
The partial page and form is loaded fine, but when I submit a redirect happens and the JSON returned by the ajax form is shown.
I am unable to understand why this is happening in scenario 2.
PS:
I already searched for similar issues and the answers mention that this happens when the required js files - jquery, "jquery.validate.unobtrusive.min.js", "jquery.unobtrusive-ajax.js" - are not referenced and downloaded.
Please note that in both scenarios, jquery, "jquery.validate.unobtrusive.min.js", "jquery.unobtrusive-ajax.js" are referenced and downloaded in the main page - ie the page containing the partial page.
I think your issue could caused by submitting handler function for ajax form only binding at page loaded or document ready event of page which contain ajax form. Since your partial page is adding dynamic via ajax, so the dynamic added ajax form will be full submitted as a normal form.
You could try below work-around solution.
Manually adding submitting event handler function for newly added form, then inside this handler function, we do submitting via ajax instead of full submit.
function loadPartial(id) {
$.get('/Config/_Settings?sid=' + id, function (data) {
var placeholder = $('#partialSettingsPlaceHolder');
placeholder.html(data);
$('form', placeholder).on('submit', submitHandler);
});
}
function submitHandler(event) {
event.preventDefault();
event.stopImmediatePropagation();
// validation code here depend on validation plugin you are using, for example:
// if (!$(this).valid()) return false;
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize()
}).done(function (data) {
// your code in case of success
}).fail(function (jqXHR, textStatus) {
// your code in case of fail
});
return false;
}
Of course, this is just a work-around solution. If you want to do it in more formal way, I suggest you to study auto generated source code for ajax form and aspx page (for example using Developer Tool of browsers).
The reason why your second scenario is not working is that you are loading and adding your form dynamically to our page after the initial page load.
if you will take a look on jquery unobtrusive ajax code you will find that section which is doing few calls like $(document).on(...). That is basically adding listeners directly to html elements like form or input right after page is ready. But because of that those click events are not being attached to elements which will be appended to page later. Unfortunarelly I cannot see in that script any possibility to reinitialise it. So maybe your only option might be to create script which can be called after adding your form and will do the same steps as the original version. That way ajax behaviour should remain the same.
Another option might be to render the form without ajax but hide it with css? That is very dependent on your page styling etc. That way event listeners will get applied and you could then only show the form instead of appending it as a fresh node

ASP.NET MVC Load page with AJAX

I have this situation:
A page with an accordion with 2 tabs, one has a list of paymentmethods, the second has a summary of the order with orderlines, amounts and totals (rendered as partialview). Selecting a paymentmethod causes the order totals to be recalculated, extra costs etc might apply.
What is the recommended way to display the new ordersummary after a paymentmethod is selected, using AJAX?
Doing an AJAX call and getting all the new amounts, orderlines, etc and setting these values using JS seems inefficient to me. Ideal situation would be if I could make an AJAX call with the selected payement method and this call would return the HTML which I can use to replace the old summary.
Is it bad to render a partialview to HTML on the server and return it using JSON? What is best practice for this situation?
I have an example here:
Javascript
$("#divForPartialView").load("/HelloWorld/GetAwesomePartialView",
{ param1: "hello", param2: 22}, function () {
//do other cool client side stuff
});
Controller Action
public ActionResult GetAwesomePartialView(string param1, int param2)
{
//do some database magic
CustomDTO dto = DAL.GetData(param1, param2);
return PartialView("AwesomePartialView",dto);
}
In your action method return a PartialView([view name]).
Then you can do this with jquery:
var req = $.ajax({
type:"GET",//or "POST" or whatever
url:"[action method url]"
}).success(function(responseData){
$('targetelement').append($(responseData));});
Where 'targetelement' is a selector for the element into which you want to inject the content.
You might want to do $('targetelement').html(''); first before appending the response to the target element.
Update
Or, better yet, use .load from Rick's answer.

How to check the Condition here

<script type="text/javascript">
$('#TextEdit').click(function () {
$('#ObnAdd').val('Save');
});
</script>
<% using (Html.BeginForm("Create", "ObnTextComponents",FormMethod.Post,new {#id = "TheForm"}))
{%>
I need to check the condition if my ObnAdd Button text is Add need to go Create ActionResult
if Button text is Save I need to go Save ActionResult..
how to check this Condition in BeginForm?
thanks
From your comments it seems that it's better to check for the value of the button on the Controller side. Because you can't change your aspx code after the page loads.
So, in your controller you should have something like this (make sure your ObnAdd has name=ObnAdd):
public ActionResult SaveCreate(FormCollection form, string ObnAdd)
{
if (ObnAdd == "Save")
{
//Do save
}
else if (ObnAdd == "Create")
{
//Do create
}
//here return RedirectToAction or whatever
return RedirectToAction("Index");
}
And your HTML:
<% using (Html.BeginForm("SaveCreate", "ObnTextComponents",FormMethod.Post,new {#id = "TheForm"}))
{%>
The ASP executes server-side before pageload and has no access to the dom. Whereas the javascript executes client-side during and after pageload.
Since changing the button text is done in javascript (after all the asp runs), the button will always have the same value during pageload, so the branch is unnecessary. Also note that the asp can't access the dom of the page it's creating to test for such things. You would need to either include a library that forms the dom tree for you or use standard text operators to check the value you're looking for (like a regex).
A simple solution to what I think you're doing here would be to maintain a hidden input on the form that is also updated when you update the button. Then you can have the button submit and the page handling the form can make the necessary decisions with all information available.

How do I clear MVC client side validation errors when a cancel button is clicked when a user has invalidated a form?

I have a partial view that is rendered within a main view. The partial view takes advantage of System.ComponentModel.DataAnnotations and Html.EnableClientValidation().
A link is clicked, and div containing the partial view is displayed within a JQuery.Dialog().
I then click the save button without entering any text in my validated input field. This causes the client side validation to fire as expected, and display the '*required' message beside the invalid field.
When the cancel button is clicked, I want to reset the client side MVC validation back to it's default state and remove any messages, ready for when the user opens the dialog again. Is there a recommended way of doing this?
This answer is for MVC3. See comments below for help updating it to MVC 4 and 5
If you just want to clear the validation-messages so that they are not shown to the user you can do it with javascript like so:
function resetValidation() {
//Removes validation from input-fields
$('.input-validation-error').addClass('input-validation-valid');
$('.input-validation-error').removeClass('input-validation-error');
//Removes validation message after input-fields
$('.field-validation-error').addClass('field-validation-valid');
$('.field-validation-error').removeClass('field-validation-error');
//Removes validation summary
$('.validation-summary-errors').addClass('validation-summary-valid');
$('.validation-summary-errors').removeClass('validation-summary-errors');
}
If you need the reset to only work in your popup you can do it like this:
function resetValidation() {
//Removes validation from input-fields
$('#POPUPID .input-validation-error').addClass('input-validation-valid');
$('#POPUPID .input-validation-error').removeClass('input-validation-error');
//Removes validation message after input-fields
$('#POPUPID .field-validation-error').addClass('field-validation-valid');
$('#POPUPID .field-validation-error').removeClass('field-validation-error');
//Removes validation summary
$('#POPUPID .validation-summary-errors').addClass('validation-summary-valid');
$('#POPUPID .validation-summary-errors').removeClass('validation-summary-errors');
}
I hope this is the effect you seek.
If you are using unobtrusive validation that comes with MVC you can simply do:
$.fn.clearErrors = function () {
$(this).each(function() {
$(this).find(".field-validation-error").empty();
$(this).trigger('reset.unobtrusiveValidation');
});
};
------------------------------------------------------------------------
Third Party Edit:
This mostly worked in my case, but I had to remove the $(this).find(".field-validation-error").empty(); line. This appeared to affect the re-showing of the validation messages when resubmitting.
I used the following:
$.fn.clearErrors = function () {
$(this).each(function() {
$(this).trigger('reset.unobtrusiveValidation');
});
};
and then called it like this:
$('#MyFormId input').clearErrors();
function resetValidation() {
$('.field-validation-error').html("");
}
You can simply define a new function in jQuery:
$.fn.resetValidation = function () {
$(this).each(function (i, e) {
$(e).trigger('reset.unobtrusiveValidation');
if ($(e).next().is('span')) {
$(e).next().empty();
}
});
};
and then use it for your input fields:
$('#formId input').resetValidation();
Thank you. I had a similar question for a slightly different scenario. I have a screen where when you click one of the submit buttons it downloads a file. In MVC when you return a file for download, it doesn't switch screens, so any error messages which were already there in the validation summary remain there forever. I certainly don't want the error messages to stay there after the form has been submitted again. But I also don't want to clear the field-level validations which are caught on the client-side when the submit button is clicked. Also, some of my views have more than one form on them.
I added the following code (thanks to you) at the bottom of the Site.Master page so it applies to all of my views.
<!-- This script removes just the summary errors when a submit button is pressed
for any form whose id begins with 'form' -->
<script type="text/javascript">
$('[id^=form]').submit(function resetValidation() {
//Removes validation summary
$('.validation-summary-errors').addClass('validation-summary-valid');
$('.validation-summary-errors').removeClass('validation-summary-errors');
});
</script>
Thanks again.
You can tap into the validation library methods to do this.
There are two objects of interest: FormContext and FieldContext. You can access the FormContext via the form's __MVC_FormValidation property, and one FieldContext per validated property via the FormContext's fields property.
So, to clear the validation errors, you can do something like this to a form:
var fieldContexts = form.__MVC_FormValidation.fields;
for(i = 0; i < fieldContexts.length; i++) {
var fieldContext = fieldContexts[i];
// Clears validation message
fieldContext.clearErrors();
}
// Clears validation summary
form.__MVC_FormValidation.clearErrors();
Then, you can hook that piece of code to whichever event you need.
Sources for this (quite undocumented) insight:
http://bradwilson.typepad.com/presentations/advanced-asp-net-mvc-2.pdf (Mentions FieldContext)
https://stackoverflow.com/a/3868490/525499 (For pointing out this link, which metions how to trigger client-side validation via javascript)
In order to complete clear the validation artifacts including the message, the coloured background of the input field, and the coloured outline around the input field, I needed to use the following code, where this was (in my case) a Bootstrap modal dialog containing an imbedded form.
$(this).each(function () {
$(this).find(".field-validation-error").empty();
$(this).find(".input-validation-error").removeClass("input-validation-error");
$(this).find(".state-error").removeClass("state-error");
$(this).find(".state-success").removeClass("state-success");
$(this).trigger('reset.unobtrusiveValidation');
});
Here you can use simply remove error message
$('.field-validation-valid span').html('')
OR
$('.field-validation-valid span').text('')
I've this issue for "Validation summery" after form ajax submit and done it like this:
$form.find('.validation-summary-errors ul').html('');
and complete code is:
$("#SubmitAjax").on('click', function (evt) {
evt.preventDefault();
var $form = $(this).closest('form');
if ($form.valid()) {
//Do ajax call . . .
//Clear validation summery
$form.find('.validation-summary-errors ul').html('');
}
});

How to click a button on an ASP.NET web page programmatically?

I am trying to figure out how to click a button on a web page programmatically.
Specifically, I have a WinForm with a WebBrowser control. Once it navigates to the target ASP.NET login page I'm trying to work with, in the DocumentCompleted event handler I have the following coded:
HtmlDocument doc = webBrowser1.Document;
HtmlElement userID = doc.GetElementById("userIDTextBox");
userID.InnerText = "user1";
HtmlElement password = doc.GetElementById("userPasswordTextBox");
password.InnerText = "password";
HtmlElement button = doc.GetElementById("logonButton");
button.RaiseEvent("onclick");
This fills the userid and password text boxes fine, but I am not having any success getting that darned button to click; I've also tried "click", "Click", and "onClick" -- what else is there?. A search of msdn of course gives me no clues, nor groups.google.com. I gotta be close. Or maybe not -- somebody told me I should call the POST method of the page, but how this is done was not part of the advice given.
BTW The button is coded:
<input type="submit" name="logonButton" value="Login" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="logonButton" tabindex="4" />
How does this work? Works for me
HtmlDocument doc = webBrowser1.Document;
doc.All["userIDTextBox"].SetAttribute("Value", "user1");
doc.All["userPasswordTextBox"].SetAttribute("Value", "Password!");
doc.All["logonButton"].InvokeMember("Click");
var btn = document.getElementById(btnName);
if (btn) btn.click();
There is an example of how to submit the form using InvokeMember here.
http://msdn.microsoft.com/en-us/library/ms171716.aspx
You can try and invoke the Page_ClientValidate() method directly through the clientscript instead of clicking the button, let me dig up an example.
Using MSHTML
mshtml.IHTMLWindow2 myBroserWindow = (mshtml.IHTMLWindow2)MyWebBrowser.Document.Window.DomWindow;
myBroserWindow.execScript("Page_ClientValidate();", "javascript");
Have you tried fireEvent instead of RaiseEvent?
You could call the method directly and pass in generic object and EventArgs parameters. Of course, this might not work if you were looking at the sender and EventArgs parameters for specific data. How I usually handle this is to refactor the guts of the method to a doSomeAction() method and the event handler for the button click will simply call this function. That way I don't have to figure out how to invoke what is usually just an event handler to do some bit of logic on the page/form.
In the case of javascript clicking a button for a form post, you can invoke form.submit() in the client side script -- which will run any validation scripts you defined in the tag -- and then parse the Form_Load event and grab the text value of the submit button on that form (assuming there is only one) -- at least that's the ASP.NET 1.1 way with which I'm very familiar... anyone know of something more elegant with 2.0+?
Just a possible useful extra where the submit button has not been given an Id - as is frequently the case.
private HtmlElement GetInputElement(string name, HtmlDocument doc) {
HtmlElementCollection elems = doc.GetElementsByTagName("input");
foreach (HtmlElement elem in elems)
{
String nameStr = elem.GetAttribute("value");
if (!String.IsNullOrEmpty (nameStr) && nameStr.Equals (name))
{
return elem;
}
}
return null;
}
So you can call it like so:
GetInputElement("Login", webBrowser1.Document).InvokeMember("Click");
It'll raise an exception if the submit input with the value 'Login', but you can break it up if you want to conditionally check before invoking the click.
You posted a comment along the lines of not wanting to use a client side script on #Phunchak's answer. I think what you are trying to do is impossible. The only way to interact with the form is via a client side script. The C# code can only control what happens before the page is sent out to the browser.
try this
button.focus
System.Windows.Forms.SendKeys.Send("{ENTER}")

Resources