I'm working on an asp-mvc application and facing the following issue:
I have a model with simple properties plus one property which is a list of my custom object, and I render the Ienumerable property as mentioned here:
Passing IEnumerable property of model to controller post action- ASP MVC
In my view, I have a button that is supposed to add items to the ienumerable property of my model. Of Course, I don't want to lose already inserted data, so I need to pass the model to the corresponding action.
I've noticed that the model os transferred entirely only upon post. So, I did something like:
$(".addButton").click(function (event) {
event.preventDefault();
$("#FilterForm").submit();
#{ Session["fromAddFullItem"] = "true";}
return false;
});
And then in my controller, I do something like:
public ActionResult Index(FilterModel model)
{
if (Session["fromAddFullItem"].ToString() == "true")
{
Session["fromAddFullItem"] = "false";
return AddBlankItemTemplate(model);
}
I've read that assigning session in js is not recommended, but also tried TempData, and there the data was always null.
My problem is that Session["fromAddFullItem"] is always true, even when I come from another button. If I put breakpoint in addbtn click in line- Session["fromAddFullItem"] = "false";, and press the other button, I see that for some odd reason the mentioned breakpoint is hit, even though I haven't pressed the add button.
Any help? Maybe there is another way to achieve what I want. Currently, no matter which button I press (which posts the form), it comes as Session["fromAddFullItem"] = "false" and goes to action AddBlankItemTemplate. Thanks.
EDIT - AJAX POST
$(".addButton").click(function(event) {
event.preventDefault();
var modelData = JSON.stringify(window.Model);
$.ajax({
url: '#Url.Action("AddBlankItemTemplate")',
type: 'POST',
dataType: 'json',
data: modelData,
contentType: 'application/json; charset=utf-8',
});
return false;
});
and controller
public ActionResult AddBlankItemTemplate(string modelData)
EDIT 2:
$(".addButton").click(function (event) {
event.preventDefault();
$.ajax({
url: '#Url.Action("AddBlankItemTemplate")',
data: $("#FilterForm").serialize()
}).success(function(partialView) {
$('DetailsTemplates').append(partialView);
});
});
and Controller:
public ActionResult AddBlankItemTemplate(FilterModel model)
The line #{ Session["fromAddFullItem"] = "true";} is Razor code and will be run on page rendering and load regardless of where you put it in the page.
It's not client side code so won't wait for your js code to run. If you're trying to synchronise state between js and MVC have you looked into angularjs which could simplify these actions.
Related
My project contains a view. which contain few text boxes and a button.
Button click will send the all text box value to controller method.
$('#btnNext').click(function () {
var dataToPost =
{
FirstName: $('#txt_FirstName').val(), LastName: $('#txt_lastName').val(), Email: $('#txt_emailID').val(),
MobileNumber: $('#txt_mobileNumber').val(), AddressLine1: $('#txt_addressLine1').val(), AddressLine2: $('#txt_addressLine2').val(),
Nationality: $('#ddlCountry').val(), State: $('#ddlCountry').prop('selectedIndex'), City: $('#ddlCountry').val(),
GenderTypeID: $('#ddlGender').val(), Pincode: $('#txtPincode').val()
}
$.ajax({
url: "/Main/getCompetitorDetails",
data: dataToPost,
type: "POST",
success: function (e) {
debugger;
alert(e);
Which is perfectly calling the method "getCompetitorDetails" in Main Controller.
Then the method call is calling the Payment view
public ActionResult getCompetitorDetails(CompetitorInformation cI) => View("Payment");
public ActionResult Payment() => View();
But the view is not showing.
When debugging , After the method call, The next step it is going to the Payment.cshtml page. Here:
#{
Layout = null;
}
Then nothing happens.
I didn't set any layout page as of now.
So i made it to null.
AM i missing anything here?
From razor view I'm sending js object using jquery to the mvc controller. Reason why I do it using jquery post method and not razors form is that I need to manage dynamic input of certain fields. On the view certain fields (inputtextbox) are dynamically added to the view (from 0 - 10) and I manage that solution using js on the page.
var myJsObj = ...
$.ajax({
type: 'POST',
traditional: true,
contentType: 'application/json',
url: '/MyController/SomeAction',
data: JSON.stringify({ model: myJsObj}),
success: function () {
}
});
On the server side, mvc receives that model and in case of some error I want to return this object back to the view.
[HttpPost]
public ActionResult SomeAction(MyModel model)
{
if(!ModelState.IsValid)
{
ModelState.AddModelError("", "Error occured!");
return View(model);
}
return RedirectToAction("Index");
}
I have inside razor view
Html.ValidationSummary
but since I'm using jquery post I dont know how to receive back to the view and display error like I would use regular razor form. Or if you know better approach to manage dynamically added input boxes on the razor view please post. Any help please.
I think you've got a couple of options here:
If you prefer to continue to use an Ajax POST as you've shown above, then you need to take the response from the POST and inject it back into your current HTML document. For example,
$.ajax({
type: 'POST',
traditional: true,
contentType: 'application/json',
url: '/MyController/SomeAction',
data: JSON.stringify({ model: myJsObj}),
success: function (data) {
// This is the case where the MVC action found model validation
// errors, and so it is responding with an HTML document that
// shows the errors.
var returnedBodyHtml = $(data).find('body').html();
$('body').html(returnedBodyHtml);
}
});
(That's untested code up there, so you may have to debug a little bit.) But this code doesn't handle the case where the server responded with a redirect (in the case of successful validation). So, check out this post for some options there.
Your other option is to use the standard Form submit. I know you said you had some dynamically generated input controls on your page, but that doesn't mean that you can't do a Form submit. You just need to make sure that these dynamically generated elements have the correct "name" attribute on them, so that their values get mapped appropriately to the Model on the server side action that is accepting the POST. For example, if your Javascript is dynamically generating an HTML element like this, and inserting it into the form:
<input type="text" name="myDynamicallyGeneratedInput[0]" />
<input type="text" name="myDynamicallyGeneratedInput[1]" />
<input type="text" name="myDynamicallyGeneratedInput[2]" />
<input type="text" name="myDynamicallyGeneratedInput[3]" />
then your Form submit will still work, as long as on the server side, your MyModel class has that corresponding property:
class MyModel
{
public List<string> MyDynamicallyGeneratedInput {get; set;}
}
This is what I have done to display errors for dynamic inputs. First off, take a look at this post to give you a better understanding. I have modified my code to better suit my needs, but you can check if it works for your application.
use-asp-net-mvc-validation-with-jquery-ajax.
I would then consume the return result in the ajax post error callback. It returns a code 400 'Bad Request' on validation errors. The validator variable is the form validator object.
error: function (xhr, textStatus, errorThrown) {
var statusCode = parseInt(xhr.status);
if (statusCode == 400) {
var data = $.parseJSON(xhr.responseText);
var message = "";
$.each(data, function (i, item) {
var propertyName = "" + item.key + "";
if ($("input[name='" + item.key + "']").length > 0) {
var errorObj = {};
errorObj[item.key] = item.error;
validator.showErrors(errorObj);
}
else {
message += "<div>" + item.key + ": " + item.error + "</div>";
}
});
if (message != "") {
//display message
}
}
}
I hope this helps. Good luck.
I have this Kendo UI dropdownlist with a select event that is handled by a JavaScript function.
I need to call an action result from a controller that runs a LINQ query to populate a Kendo UI grid on my page. My problem is the only way I can find to handle this even is with JavaScript and I have been unable to figure out how to call my action result from my controller from the JavaScript event function.
This is what the DropDownList looks like...
#(Html.Kendo().DropDownList()
.Name("Options")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new List<SelectListItem>() {
new SelectListItem() {
Text = "Policies Not Archived",
Value = "1"
},
new SelectListItem() {
Text = "View All Policies",
Value = "2"
},
new SelectListItem() {
Text = "Filter Policies",
Value = "3"
}
})
.Events(e =>
{
e.Select("select");
})
)
and my JavaScript event handler that needs to call the action result
function select(e) {
}
and depending on the selection an ActionResult like this,
public ActionResult ViewAllPolicies()
{
//mycode
}
see this post
var url = '#Url.Action("ViewAllPolicies","YourController")';
$.ajax({ url: url, success: DataRetrieved, type: 'POST', dataType: 'json' });
in controller
public ActionResult ViewAllPolicies()
{
//Should return json format
}
url – this is the URL where request is sent. In my case there is
controller called contacts and it has action calles
ListPartiesByNameStart(). This action method takes parameter
nameStart (first letter of person or company). success – this is the
JavaScript function that handles retrieved data. You can write there
also anonymous function but I suggest you to use functions with names
because otherwise your code may get messy when functions grow. type –
this is the type of request. It is either GET or POST. I suggest you
to use POST because GET requests in JSON format are forbidden by
ASP.NET MVC by default (I will show you later how to turn on GET
requests to JSON returning actions). dataType – this is the data
format that is expected to be returned by server. If you don’t assign
it to value then returned result is handled as string. If you set it
to json then jQuery constructs you JavaScript object tree that
corresponds to JSON retrieved from server.
Instead of returning json, you can also return a PartialView and in the .done function grab an element and replace it with the results from the partial view. PartialView actions basically return a fragment of HTML, and so you can just stuff that anywhere you want on the page:
$.ajax({
url: urlToPartialViewAction,
type: 'POST',
dataType: 'JSON',
data: '123'
})
.done(function (result) {
$('#someDivPlaceholder').replaceWith(result);
});
You could have something like a link or grey div and wire up to it's click event and then call this, the link might say "View Receipt" and when you click it you call an action that returns a partial view with the receipt, and so when they click it the div/link is replaced with the result. Kind of like the "View More Comments" links you see on social sites.
Note that you can't have a partial view by itself, it must be called through an action
public PartialViewResult _GetReceipt(string id)
{
ReceiptViewModel vm = //query for receipt data
return PartialView(vm);//render partial view and return html fragment
}
Once the select function executes, you need to make an AJAX call back to your Controller. You can use jQuery.ajax() (a wrapper for the most common AJAX operations) in the select function,
function select(e) {
var url = '#Url.Action("ViewAllPolicies", "PolicyController")';
var selectedPolicy = $('#Options').val(); // The option selected
$.ajax({
url: url,
type: 'POST',
dataType: 'JSON',
data: selectedPolicy
})
.done(function (data) {
// Display the data back from you Controller
});
}
You can look at the Kendo site for more info on how the DropDownList works.
I have a jquery click method that looks like this
<script type="text/javascript">
function clickView(e) {
e.preventDefault();
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
$.ajax({
url: "/Jac/ViewCustomDetails",
data: { productId: dataItem.Id },
success: function (response) {
$("#details").html(response);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
document.write(xhr.responseText);
}
});
}
</script>
Basically this makes an AJAX call to my controller to render an action.
The action ViewCustomDetails, which is within JacController, and within an area looks like this:
public ActionResult ViewCustomDetails(int productId)
{
Detail model;
model = new Detail
{
Price = productId.ToString(),
Origin = productId.ToString()
};
return View(model);
}
When I click on my button that fires off the AJAX call, I am able to break into my action. however I get this error in my view
The view 'ViewCustomDetails' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Jac/ViewCustomDetails.aspx
~/Views/Jac/ViewCustomDetails.ascx
~/Views/Shared/ViewCustomDetails.aspx
~/Views/Shared/ViewCustomDetails.ascx
~/Views/Jac/ViewCustomDetails.cshtml
~/Views/Jac/ViewCustomDetails.vbhtml
~/Views/Shared/ViewCustomDetails.cshtml
~/Views/Shared/ViewCustomDetails.vbhtml
Obviously there's no such controller/action in my views folder as my controller is within an area.
How do I get it to reference the controller in my area?
It IS referencing your controller but the line return View(model); in your ViewCustomDetails method requires the existence of a View file, which would normally be called ViewCustomDetails.cshtml
This View file must take a model type of Detail
You might also need to JSONify the returning view.
I just had to add the area name into my URL in the jquery code as such
url: "/Dan/Jac/ViewCustomDetails",
instead of just
url: "/Jac/ViewCustomDetails",
I have a view model which has a list of objects.
Binding the list to the WebGrid is fine and displays all data correctly.
The objects have a boolean value I'd like the user to change by selecting a checkbox displayed with each record in a WebGrid. When the user checks/unchecks various checkboxes then clicks an update button, I want to be able to save the changes on the objects.
The updating is proving a right "pain in the A..." with MVC and Razor.
How can I send these changes back to the controller?
Can someone please advise?
Any samples?
Update:
I have been able to pass the whole form back to the Controller after giving each checkbox a name and ID. The value is set to the item.Id and is only found in the form's attributes if the checkbox was actually checked.
So in the Controller, I do this:
public ActionResult Assign(FormCollection form)
{
var ch = form.GetValues("itemChk");
foreach (var id in ch)
{
//...Get object using id
//...Call your method
}
return View();
}
ch is a string array of the item.Ids of only those checkboxes that were checked.
By returning the item.Id as a value of a checked checkbox, I can assign/set that value on the item itself and save/update it.
Next to try get ALL checkboxes returned with values. Also to at text box to each row in the WebGrid and try something similar to handle the return values.
Here's the link that got me going down this track.
You can use Json ajax call to pass the FormCollection to the controller
$('#btnClick').click(function (event) {
var form = $("#fmSearch").serializeArray();
$.ajax({
url: '/Home/Assign',
dataType: "json",
type: "post",
data: form,
cache: false,
success: function (data) {
},
error: OnError
});
});
Please note: do not add "contentType: "application/json; charset=utf-8" because no any json content.
fmSearch is the id of Html.BeginForm(), you can write them as:
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "fmSearch" }))
{
#Html.ValidationSummary(true)
}
Last, add [HttpPost] above your action and replace ActionResult with JsonResult.