Javascript POST and button submit - asp.net-mvc

So, here is a weird behaviour I have noticed today.
I have a controller which inherits from SurfaceController.
I have a [Post] Action method which returns back to the same partial view and that's fine. The reason is due to paging/filtering that happens on that page.
Now, on the view itself, if I use a button submit, I see everything being submitted just fine.
However, if I use a hyperlink with an onclick event to set hidden field values and then do a form.submit(), initially the model has values which are null but then it re-executes the submit with the all of the values put in place again!
That doesn't make sense. What is the difference with a button submit and a javascript function doing a form.submit() ?
There really isn't much code:
// Controller
[HttpPost]
public PartialViewResult FilterResultsForTransaction(MyModel model)
{
.....
}
// View
<script language="javascript">
function ApplySort(fieldname, sortDir)
{
$('#Filter_FieldName').val(fieldname);
$('#Filter_SortDir').val(sortDir);
//var form = $('#form');
//form.submit();
}
</script>
<snip>
<input type="submit" onclick="javascript:ApplySort('OrderDate'........)" value="ASC" />
ASC
Now, if I don't use the submit button but just the hyperlink and comment in the form.submit() in the JS function - it does the post but the model values are null/default and then it recalls itself with the values populated again.
thoughts?!

When you click on the submit button, the page will submit its data BEFORE the script in ApplySort() is complete. So you will have to stop the submitting, then set your hidden field values, and then submit the form. Like this:
<input type="submit" data-field="bla" data-sort="ASC" value="Sort ASC" />
<script>
$("input").on("click", ApplySort)
function ApplySort(e)
{
e.preventDefault(); //stop postback
var btn = $(this);
var form = $('#form');
$('#Filter_FieldName').val(btn.attr("data-field"));
$('#Filter_SortDir').val(btn.attr("data-sort"));
console.log("submit")
form.submit();
}
</script>
Its generally bad to have script code in a onclick, so I bind the click event in my js code with jQuery.
Test it out here: http://jsfiddle.net/03gj1r02/2/

Related

Route to a POST method with model and id

I want to pass in two Ids to my method. The method is called DeleteAttendee and is on my SessionController in the Training area.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteAttendee(int sessId, int attId)
I have created a link to get there that takes you to https://localhost:<port>/Training/Session/DeleteAttendee?sessId=1&attId=3.
<a asp-action="DeleteAttendee" asp-route-attId="#item.Attendee.Id" asp-route-sessId="#Model.Id">Delete</a>
Using the default routing, this page can't be found. What do I need to change or set up to route to this method?
Edit: Apparently the problem is that the link is performing a GET, but I need it to POST. How do I change it?
I think you can accomplish what I want to do with a button control. It will actually work better for me now if I can pass in the model and a specific id. I tried the button below. It looks correct in the markup, but the id keeps getting replaced with the sessionId when the button is clicked.
<button formaction="/Training/Session/DeleteAttendee/#item.Id" formmethod="post">Edit</button>
Can you use a ActionLink instead?
#Html.ActionLink("Delete", "DeleteAttendee", "Session", new { sessId = Model.Id, attId = item.Attendee.Id })
I ended up using a submit button that calls javascript and added the value to the viewmodel to get this done.
On the page:
<input type="hidden" asp-for="SelectedAttendeeId" />
<input type="button" onclick="DeleteAttendee(#item.Id)" value="E" />
In javascript:
function DeleteAttendee(attendeeId) {
var selectedAtt = $('#SelectedAttendeeId');
selectedAtt.val(attendeeId);
var model = $('#frmSession').serialize();
$.post('/Training/Session/DeleteAttendee', model, function (data) {
// success logic here
});
}

MVC - unable to pass value back to controller using hidden field

I'm trying to assign value to the hidden field in java script using the
JavaScript variable and trying to pass it back to the controller. The value every time I go in the post method for my model property DBErrorID is 0.
Razor View:
<body>
#Html.HiddenFor(model => model.DBErrorID, new { id = "DBErrorId" })
<input type="submit" value="Update" class="btn btn-success" onclick="GetValue()" />
</body>
JavaScript:
<script language="javascript" type="text/javascript">
function GetValue() {
$("#DBErrorId").val(totalValues);
// alert(totalValues);
}
<script>
Controller:
[HttpPost]
public ActionResult ErrorStatusView(Models.ErrorStatusModel obj)
{
Models.ErrorStatusModel objreg = new Models.ErrorStatusModel();
objreg.DBErrorID = obj.DBErrorID;
}
Your current server side code is creating an unnecessary new object of ErrorStatusModel as the first line, which will create a new object(objreg variable) with default values(unless you are setting it in a constructor), for an int type property it will be 0. If you are inspecting the values of objreg, that is the reason you see 0 as the property value.
You do not need to create this additional object. The model binder framework will create one for you and map the posted values, when you use ErrorStatusModel your method parameter type. That means your obj property is properly populated by the form data (assuming the DBErrorID property is settable)
[HttpPost]
public ActionResult ErrorStatusView(Models.ErrorStatusModel obj)
{
// obj is populated with form values.
// use obj
// return something.
}
Also, your client side code is trying to set the value of hidden input inside the GetValue method which is called on the onclick event of the submit button. If you are using a normal form submit and your button is inside a form tag, when user clicks on the submit button it will immediately submit the form (with the current value of that input)
If that is the case, you should prevent the default behavior (submitting the form) when the button is clicked, set the value as needded and fire the form submit via JavaScript.
There are multiple ways to do it. Here is one approach- the unobtrusive JavaScript approach- which assumes you have jQuery loaded to your page. Give an Id to the button, which we can use to wireup the click event.
<input type="button" value="Update" class="btn btn-success"
id="btn-save" />
Now in JavaScript, listen to the click event on this button, prevent the normal behavior(submitting the form), update the hidden input value and trigger the form submit.
$(function () {
$('#btn-save').click(function (e) {
e.preventDefault();
$("#DBErrorId").val(34);
$(this).closest("form").submit();
});
})
Also you should not create a new object in server

Get ID from modal window

I have a block of code running 3 times that will display some books and have a button to add a review.
Review button fires a modal popup with form to fill to create a review.
I am feeling dumb but I cant find a way to get a ID of a bookto use to create a review.
Was thinkink to display it within add reiew button params but dont know how to pass it after form submit (with other button pressed withtin form itself)
Template view for each book
<div class="book-#{book.id}">
... other stuff
<button class="review-create"> Write Review </button>
</div>
Javascript to display modal
$('.review-create').click(function(e) {
e.preventDefault();
// might not be parent() here but you can call parent() as many times as you want
var bookId = $(e.currentTarget).parent().attr('class').split('-')[1];
// other stuff for ajax or pass bookId into modal method
// Modal.displayForBook(bookId);
});
Now that your modal has the bookId, you can submit it through ajax by just referencing the variable. Hope this helps.
EDIT:
Because you can't edit the current script, you can write a new script in the view.
_book.html.erb
<script type='javascript/text'>
$('modal-button').click(function(e) {
$('review-create').attr('id', "#{book.id}");
});
</script
modal.html.erb
Your modal will submit ajax, dont make it do the form_for helpers
<script type='javascript/text'>
$('form.create').on('submit', function(e) {
var bookId = $(e.currentTarget).parent().attr('id');
// ajax call here
// on success, $(e.currentTarget).parent().attr('id', null)
});
</script>

Posting form to different MVC post action depending on the clicked submit button

I am using ASP.Net MVC 4. I have multiple buttons on a view.. At present I am calling the same action method; and I am distinguishing the clicked button using a name attribute.
#using (Html.BeginForm("Submit", "SearchDisplay", new { id = Model == null ? Guid.NewGuid().ToString() : Model.SavedSearch }, FormMethod.Post))
{
<div class="leftSideDiv">
<input type="submit" id="btnExport" class="exporttoexcelButton"
name="Command" value="Export to Excel" />
</div>
<div class="pageWrapperForSearchSubmit">
<input type="submit" class="submitButton"
value="Submit" id="btnSubmitChange" />
</div>
}
//ACTION
[HttpPost]
public ActionResult Submit(SearchCostPage searchModel, string Command)
{
SessionHelper.ProjectCase = searchModel.ProjectCaseNumber;
if (string.Equals(Command, Constants.SearchPage.ExportToExcel))
{
}
}
QUESTIONS
Is there a way to direct to different POST action methods on different button clicks (without custom routing)?
If there is no way without custom routing, how can we do it with custom routing?
References:
Jimmy Bogard - Cleaning up POSTs in ASP.NET MVC
You can choose the url where the form must be posted (and thus, the invoked action) in different ways, depending on the browser support:
for newer browsers that support HTML5, you can use formaction attribute of a submit button
for older browsers that don't support this, you need to use some JavaScript that changes the form's action attribute, when the button is clicked, and before submitting
In this way you don't need to do anything special on the server side.
Of course, you can use Url extensions methods in your Razor to specify the form action.
For browsers supporting HMTL5: simply define your submit buttons like this:
<input type='submit' value='...' formaction='#Url.Action(...)' />
For older browsers I recommend using an unobtrusive script like this (include it in your "master layout"):
$(document).on('click', '[type="submit"][data-form-action]', function (event) {
var $this = $(this);
var formAction = $this.attr('data-form-action');
$this.closest('form').attr('action', formAction);
});
NOTE: This script will handle the click for any element in the page that has type=submit and data-form-action attributes. When this happens, it takes the value of data-form-action attribute and set the containing form's action to the value of this attribute. As it's a delegated event, it will work even for HTML loaded using AJAX, without taking extra steps.
Then you simply have to add a data-form-action attribute with the desired action URL to your button, like this:
<input type='submit' data-form-action='#Url.Action(...)' value='...'/>
Note that clicking the button changes the form's action, and, right after that, the browser posts the form to the desired action.
As you can see, this requires no custom routing, you can use the standard Url extension methods, and you have nothing special to do in modern browsers.
BEST ANSWER 1:
ActionNameSelectorAttribute mentioned in
How do you handle multiple submit buttons in ASP.NET MVC Framework?
ASP.Net MVC 4 Form with 2 submit buttons/actions
http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx
ANSWER 2
Reference: dotnet-tricks - Handling multiple submit buttons on the same form - MVC Razor
Second Approach
Adding a new Form for handling Cancel button click. Now, on Cancel button click we will post the second form and will redirect to the home page.
Third Approach: Client Script
<button name="ClientCancel" type="button"
onclick=" document.location.href = $('#cancelUrl').attr('href');">Cancel (Client Side)
</button>
<a id="cancelUrl" href="#Html.AttributeEncode(Url.Action("Index", "Home"))"
style="display:none;"></a>
This sounds to me like what you have is one command with 2 outputs, I would opt for making the change in both client and server for this.
At the client, use JS to build up the URL you want to post to (use JQuery for simplicity) i.e.
<script type="text/javascript">
$(function() {
// this code detects a button click and sets an `option` attribute
// in the form to be the `name` attribute of whichever button was clicked
$('form input[type=submit]').click(function() {
var $form = $('form');
form.removeAttr('option');
form.attr('option', $(this).attr('name'));
});
// this code updates the URL before the form is submitted
$("form").submit(function(e) {
var option = $(this).attr("option");
if (option) {
e.preventDefault();
var currentUrl = $(this).attr("action");
$(this).attr('action', currentUrl + "/" + option).submit();
}
});
});
</script>
...
<input type="submit" ... />
<input type="submit" name="excel" ... />
Now at the server side we can add a new route to handle the excel request
routes.MapRoute(
name: "ExcelExport",
url: "SearchDisplay/Submit/excel",
defaults: new
{
controller = "SearchDisplay",
action = "SubmitExcel",
});
You can setup 2 distinct actions
public ActionResult SubmitExcel(SearchCostPage model)
{
...
}
public ActionResult Submit(SearchCostPage model)
{
...
}
Or you can use the ActionName attribute as an alias
public ActionResult Submit(SearchCostPage model)
{
...
}
[ActionName("SubmitExcel")]
public ActionResult Submit(SearchCostPage model)
{
...
}
you can use ajax calls to call different methods without a postback
$.ajax({
type: "POST",
url: "#(Url.Action("Action", "Controller"))",
data: {id: 'id', id1: 'id1' },
contentType: "application/json; charset=utf-8",
cache: false,
async: true,
success: function (result) {
//do something
}
});

Partial view in a dialog

I have managed to get the JQuery Modal dialog to show and within it, I load a partial view:
var url = '#Url.Action("ShowCarDetail", "Car")?id=' + id;
$('#dialog-modal').dialog(
{
title: "Car Detail",
width: 600,
height: 500,
draggable: false,
close: function (event, ui) {
$(this).dialog('close');
}
});
$('#dialog-modal').load(url, function()
{
$(this).dialog('open');
});
So that works fine. The problem is that when the dialog is closed, and I re-open it, the data is not refreshed. I have a DateTime on that partial view that tells me this so leaving it for a few seconds still shows me the old values.
how can I force the modal dialog to load correctly (without it using the old html that may have been rendered from the previous request)?
also - if the partial view has some actions like a submit or something, will the dialog still remain open or will this refresh the page fully? I want to be able to have that modal dialog similar to an iframe style where any actions that happen within the page in the modal will still be there and be updated without the page having a full refresh and the dialog closing.
thanks
Regarding your question:
also - if the partial view has some actions like a submit or
something, will the dialog still remain open or will this refresh the
page fully? I want to be able to have that modal dialog similar to an
iframe style where any actions that happen within the page in the
modal will still be there and be updated without the page having a
full refresh and the dialog closing.
The page will be refreshed fully with a normal form. To achieve what you describe, use an ajax form which does a post to a controller method and returns a partial view. Then have a success callback for the ajax form, which would replace the contents of a div (within the open dialog) with the response content (which would be the partial view returned from the post).
Simplified example...
View:
<div id="dialog-modal">
<p>Some optional static content here (within the dialog)
that should not change when the form is submitted.</p>
<div id="dialog-content">
#using (Html.BeginForm("MyAction", "MyController", null, FormMethod.Post, new { #id="MyForm" }))
{
#Html.EditorFor(x => x.Foo)
<input type="submit" value="OK" />
}
</div>
</div>
Controller:
[HttpPost]
public ActionResult MyAction(MyModel model)
{
// Do some stuff here with the model if you want
MyNewModel newModel = new MyNewModel();
return PartialView("_MyPartialView", newModel);
}
JavaScript:
$(function () {
$('#MyForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (xhr) {
$('#dialog-content').html(xhr);
}
});
return false;
});
});
Note that this implementation will replace the form, so you could put the form outside the div that gets replaced if needed, or have a different form in the partial view that gets returned if you want different forms submitted within the dialog in series. It's flexible to tweak to your needs. It also will not close the dialog until you explicitly call close on it, or affect any content outside of the replaced div's content. Hope this helps!

Resources