Passing strongly type form model data in asp.net mvc through jquery - asp.net-mvc-3

It is easy to submit form to an action method in the controller which has strongly typed textboxes for example, with a submit button, but what if I want to send the exact same form with the strongly typed textboxes through jquery perhaps the $.ajax call after something else has been clicked.
code like this:
#Html.TextBoxFor(m => m.topTenFav.YoutubeLink,new { id="youTubeLinkTxt"})
does all the work for us and it's very simple to map the properties of our object in the controller
[HttpPost]
public ActionResult AddTopTenFav(HomeViewModel topTen)
{
topTen.topTenFav.Date = DateTime.Now;
topTen.topTenFav.UserName = User.Identity.Name;
repository.AddTopTen(topTen);
repository.Save();
return RedirectToAction("Index");
}
How would I send this form to the controller, map the textboxes in the form to object's properties on a click event such as
$("#btnAddGenre").click(function () {}
#using (Html.BeginForm(
"AddTopTenFav", "Home", FormMethod.Post, new { id = "AddTopTenFavForm" }))
{
<span id="youTubeLinkSpan">Youtube Link</span>
<div>
#Html.TextBoxFor(m => m.topTenFav.YoutubeLink,new { id="youTubeLinkTxt"})
</div>
<span id="youTubeNameSpan">Song Title</span>
<div>
#Html.TextBoxFor(m => m.topTenFav.Title,new { id="youTubeNameTxt"})
</div>
<button type="submit" name="btnSubmit" value="">submit</button>
}

You can do the following post:
$(document).ready(function(){
$('#btnAddGenre').click(function () {
$.post(
$('#AddTopTenFavForm').attr('action'),
$('#AddTopTenFavForm').serialize,
function (data) {
window.location = #Url.Action("Index");
},
'html' // returned data type
);
});
});
I use the html data type so you can return whatever you want and the redirect occurs on the window.location using the #Url.Action to give the location.
Please if it work mark as accepted answer

yes you can post the data of strongly typed textboxex using jquery.
First you have to do
take the values of all the textboxex in jquery using the below code.
var xx= $("#xx").val();
this will give the val in xx from your mvc text box.
Then by using jquery ajax call you can call the action method.
the code is below.
$.get("/XXXX/YY/1", { xxName: xx }, function (data) {
var status = data;
alert(status);
if (status) {
return true;
}
else {
alert("The book with this name is already present. TRY DIFFERENT NAME!")
return false;
}
});
here xxxx is controller amd yy is action method name.the next parameter is the value of all the textboxes which you want to send as an parameter.
This will perform the ajax call and return the value.
Please tell me if you find any problem the i will give the whole code.

Related

MVC Core ajax and return result is a view

MVC Core, NET 5, not razor pages.
On a view I have three select components (bootstrap-select). I populate them via ViewModel.
"Get request -> Controller -> return View(viewModel);"
What I want...
When I changed value in any select component I do a post request (ajax) to the same controller (other method) and return view with repopulated data.
"'Post request -> Controller -> return View(changedModel);"
As I understood when I did ajax request I should handle it result in success and other cases.
What I should to do to reload page with new data?
Is it possible to achive this with this approach?
Yes, this is possible and you do not need to reload the page, just append the returned html to wherever you want it.
$.ajax({
type: "POST",
url: {your_url},
dataType: "html",
success: function (html) {
$("#someDiv").html(html);
}
});
What I should to do to reload page with new data?
If the post action return the same view as the get action and you want to reload the whole page, I think there is no need to use ajax. You can just redirect to post action with a form submission. If the view returned by the post action is a partialview you want render to the current view, you can use it like that in #cwalvoort answer.
Based on advices of cwalvoort and mj1313
I did:
Render main page with partials. ViewModel transfered to a partial as a parameter
On main page I added eventListners to controls with JS.
When control changes - ajax request to backend happens Controller/GetPartialView
Result from ajax replace html in partial section
Programmatically show needed components, re-add eventListners
PS Really need to learn Blazor or UI Framework :)
Code samples:
// JS
document.addEventListener("DOMContentLoaded", function (event) {
BindSelectActions();
});
function BindSelectActions() {
$('#selectGroups').on('hidden.bs.select', DoPartialUpdate);
$('#selectCompanies').on('hidden.bs.select', DoPartialUpdate);
$('#selectPeriods').on('hidden.bs.select', DoPartialUpdate);
}
function DoPartialUpdate(e, clickedIndex, isSelected, previousValue) {
// ToDo: Implement common script with "CallBackend" function
$.ajax({
type: "POST",
url: 'https://localhost:44352/TestController/TestGetPartial',
// no data its a stub at the moment
// data: $('#form').serialize(),
success: function (data, textStatus) {
$("#testControls").html(data);
$('#selectGroups').selectpicker('show');
$('#selectCompanies').selectpicker('show');
$('#selectPeriods').selectpicker('show');
BindSelectActions();
}
});
}
// Controllers
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<IActionResult> Main()
{
// ViewModel = _helper -> _mediator -> query -> context
return await Task.Run(() => View(new TestViewModel()));
}
[HttpPost]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult TestGetPartial(TestViewModel model)
{
// ViewModel = _helper -> _mediator -> query -> context
var result = new TestViewModel();
result.IsPageReload = "yes";
result.TestCollection = new string[] { "A", "B", "C" };
result.Companies = new List<SelectListItem> { new SelectListItem { Value = "999",
Text = "Test" } };
// ModelState.Clear();
return PartialView("_TestPartial", result);
}
// Main and partial views
#model TestViewModel
#{
ViewData["Title"] = "Test";
}
<div id="testControls">
#await Html.PartialAsync("_TestPartial", Model)
</div>
#section Scripts {
<script type="text/javascript" src="~/js/test.js" asp-append-version="true">
</script>
}
#model TestViewModel
<form>
<div class="d-flex flex-row justify-content-between mt-4">
<div><select id="selectGroups" asp-for="Groups" asp-items="Model.Groups"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Group"></select></div>
<div><select id="selectCompanies" asp-for="Companies" asp-items="Model.Companies"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Company"></select></div>
<div><select id="selectPeriods" asp-for="Periods" asp-items="Model.Periods"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Period"></select></div>
<div><button type="button" class="btn btn-outline-dark">Import</button></div>
</div>
</form>
<div>
#{
if (null != Model.TestCollection)
{
foreach (var item in Model.TestCollection)
{
<p>#item</p>
<br>
}
}
}
</div>

Calling multiple action methods (using ajax) and showing the result of last in a new tab

I have a form in which I need to call two action methods, one after the other. This is how the flow goes.
First I check if the prerequisite data is entered by the user. If not then I show a message that user needs to enter the data first.
If all the prerequisite data is entered, I call an action method which return data. If there is no data returned then I show a message "No data found" on the same page.
If data is returned then I call another action method present in a different controller, which returns a view with all the data, in a new tab.
The View:
#using (Ajax.BeginForm("Index", "OrderListItems", null, new AjaxOptions { OnBegin = "verifyRequiredData"}, new { #id = "formCreateOrderListReport", #target = "_blank" }))
{
//Contains controls and a button
}
The Script in this View:
function verifyRequiredData() {
if ($("#dtScheduledDate").val() == "") {
$('#dvValidationSummary').html("");
var errorMessage = "";
errorMessage = "<span>Please correct the following errors:</span><ul>";
errorMessage += "<li>Please enter Scheduled date</li>";
$('#dvValidationSummary').append(errorMessage);
$('#dvValidationSummary').removeClass('validation-summary-valid').addClass('validation-summary-errors');
return false;
}
else {
$('#dvValidationSummary').addClass('validation-summary-valid').removeClass('validation-summary-errors');
$('#dvValidationSummary').html("");
$.ajax({
type: "GET",
url: '#Url.Action("GetOrderListReport", "OrderList")',
data: {
ScheduledDate: $("#dtScheduledDate").val(),
Crews: $('#selAddCrewMembers').val(),
Priorities: $('#selPriority').val(),
ServiceTypes: $('#selServiceTypes').val(),
IsMeterInfoRequired: $('#chkPrintMeterInfo').val()
},
cache: false,
success: function (data) {
debugger;
if (data !== "No data found") {
//var newUrl = '#Url.Action("Index", "OrderListItems")';
//window.open(newUrl, '_blank');
return true;
} else {
//Show message "No data found"
return false;
}
}
});
return false;
}
}
The "GetOrderListReport" Action method in "OrderList" Controller:
public ActionResult GetOrderListReport(OrderListModel model)
{
var contract = new OrderReportDrilldownParamDataContract
{
ScheduledDate = model.ScheduledDate
//Setting other properties as well
};
var result = OrderDataModel.GetOrderList(contract);
if (string.IsNullOrWhiteSpace(result) || string.IsNullOrEmpty(result))
{
return Json("No data found", JsonRequestBehavior.AllowGet);
}
var deserializedData = SO.Core.ExtensionMethods.DeserializeObjectFromJson<OrderReportDrilldownDataContract>(result);
// send it to index method for list
TempData["DataContract"] = deserializedData;
return Json(deserializedData, JsonRequestBehavior.AllowGet);
}
The last action method present in OrderListItems Controller, the result of which needs to be shown in a new tab:
public ActionResult Index()
{
var deserializedData = TempData["DataContract"] as OrderReportDrilldownDataContract;
var model = new OrderListItemViewModel(deserializedData);
return View(model);
}
The problem is that I am not seeing this data in a new tab, although I have used #target = "_blank" in the Ajax.BeginForm. I have also tried to use window.open(newUrl, '_blank') as can be seen above. But still the result is not shown in a new tab.
Please assist as to where I am going wrong?
If you are using the Ajax.BeginForm you shouldn't also be doing an ajax post, as the unobtrusive ajax library will automatically perform an ajax post when submitting the form.
Also, if you use a view model with data annotation validations and client unobtrusive validations, then there would be no need for you to manually validate the data in the begin ajax callback as the form won't be submitted if any validation errors are found.
The only javascript code you need to add in this scenario is a piece of code for the ajax success callback. That will look as the one you currently have, but you need to take into account that opening in new tabs depends on the browser and user settings. It may even be considered as a pop-up by the browser and blocked, requiring the user intervention to allow them as in IE8. You can give it a try on this fiddle.
So this would be your model:
public class OrderListModel
{
[Required]
public DateTime ScheduledDate { get; set; }
//the other properties of the OrderListModel
}
The form will be posted using unobtrusive Ajax to the GetOrderListReport of the OrderList controller. On the sucess callback you will check for the response and when it is different from "No data found", you will then manually open the OrderListItems page on a new tab.
This would be your view:
#model someNamespace.OrderListModel
<script type="text/javascript">
function ViewOrderListItems(data){
debugger;
if (data !== "No data found") {
var newUrl = '#Url.Action("Index", "OrderListItems")';
//this will work or not depending on browser and user settings.
//passing _newtab may work in Firefox too.
window.open(newUrl, '_blank');
} else {
//Show message "No data found" somewhere in the current page
}
}
</script>
#using (Ajax.BeginForm("GetOrderListReport", "OrderList", null,
new AjaxOptions { OnSucces= "ViewOrderListItems"},
new { #id = "formCreateOrderListReport" }))
{
#Html.ValidationSummary(false)
//input and submit buttons
//for inputs, make sure to use the helpers like #Html.TextBoxFor(), #Html.CheckBoxFor(), etc
//so the unobtrusive validation attributes are added to your input elements.
//You may consider using #Html.ValidationMessageFor() so error messages are displayed next to the inputs instead in the validation summary
//Example:
<div>
#Html.LabelFor(m => m.ScheduledDate)
</div>
<div>
#Html.TextBoxFor(m => m.ScheduledDate, new {id = "dtScheduledDate"})
#Html.ValidationMessageFor(m => m.ScheduledDate)
</div>
<input type="submit" value="Get Report" />
}
With this in place, you should be able to post the data in the initial page using ajax. Then based on the response received you will open another window\tab (as mentioned, depending on browser and user settings this may be opened in a new window or even be blocked) with the second page content (OrderListItems).
Here's a skeleton of what I think you are trying to do. Note that window.open is a popup though and most user will have popups blocked.
<form id="formCreateOrderListReport">
<input type="text" vaule="testing" name="id" id="id"/>
<input type="submit" value="submit" />
</form>
<script type="text/javascript">
$('#formCreateOrderListReport').on('submit', function (event) {
$.ajax({
type: "POST",
url: '/home/test',
data: { id: $('#id').val()},
cache: false
}).done(function () {
debugger;
alert("success");
var newUrl = '/home/contact';
window.open(newUrl, '_blank');
}).fail(function () {
debugger;
alert("error");
});
return false;
});
</script>
Scale down the app to get the UI flow that you want then work with data.

ASP MVC-3 : Problems updating the data of an AJAX form after making a post

I have the following problem when updating a for via AJAX after it is submitted. For some reason some hidden fields that are on the HTML that is returned are not being updated, which is weird because when I run the debugger they appeared to have the correct value.
This is the relevant part of my form
<div id="itemPopUpForm">
#{Html.EnableClientValidation();}
#Html.ValidationSummary()
<div id="formDiv">
#{ Html.RenderPartial("ItemData", Model, new ViewDataDictionary() { { "Machines", ViewBag.Machines }, { "WarehouseList", ViewBag.WarehouseList }, { WebConstants.FORM_ID_KEY, #ViewData[WebConstants.FORM_ID_KEY] } }); }
</div>
</div>
Then the partial view contains hidden fields like these which are the ones not being updated
#using (Html.BeginForm("Index", "Item", FormMethod.Post, new { id = "frmItem", name = "frmItem" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Item.SodID)
#Html.HiddenFor(model => Model.Item.ItemID) //The itemID needs updating when an item is copied
#Html.HiddenFor(model => model.Item.Delivery.DeliveryAddressID, new { #id = "delAddressID" })
And this is the javascript method that updates the form
function ajaxSave() {
if (!itemValid()) return;
popup('ajaxSplash');
$.ajax({
type: "POST",
url: '#Url.Action("Index")',
data: $("#frmItem").serialize(),
success: function (html) {
console.log(html);
$("#formDiv").html(html);
initItemPage();
alert("Item was saved successfully");
},
error: function () { popup('ajaxSplash'); onFailure(); }
});
}
The action Index returns the Partial View "ItemData" and when I check the Item Model it does have the correct value, but when I see the html returned it is still set to 0.
If you intend to modify a model property in your POST action don't forget to remove it from ModelState first, otherwise HTML helpers will use the originally posted value when rendering:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// remove the value from modelstate
ModelState.Remove("Item.ItemID");
// update the value
model.Item.ItemID = 2;
return PartialView(model);
}
I'm having the same problem and it seems like the helper HiddenFor evaluates with required unobtrusive validation even if in the model one does not annotate the property with [Required].
The HTML rendered by #Html.HiddenFor(m=>m.Step) is :
<input data-val=​"true" data-val-number=​"The field Step must be a number." data-val-required=​"The Step field is required." id=​"Step" name=​"Step" type=​"hidden" value=​"2">​
Hence, it is why it works if we remove it from the ModelState.
Removing the property from the ModelState seems to me like a hack. I would prefer to use
<input type="hidden" id="Step" name="Step" value="#Model.Step" />
instead of the Html.HiddenFor helper.
You can also implement you own HiddenFor helper.

Dynamically load Partial Views

How can i dynamically load a Partial View?
I mean I have this view, lets say ListProducts, there I select some dropdownlists with products, etc, and with the selected values from those I wanna fill a partial view, which would be in a div that was invisible but after onchange() event would become visible and with the data from the specific selected items.
Use jQuery's $.load() with a controller action that returns a partial view.
For example:
HTML
<script type="text/javascript">
$(document).ready(function()
{
$("#yourselect").onchange(function()
{
// Home is your controller, Index is your action name
$("#yourdiv").load("#Url.Action("Index","Home")", { 'id' : '123' },
function (response, status, xhr)
{
if (status == "error")
{
alert("An error occurred while loading the results.");
}
});
});
});
</script>
<div id="yourdiv">
</div>
Controller
public virtual ActionResult Index(string id)
{
var myModel = GetSomeData();
return Partial(myModel);
}
View
#model IEnumerable<YourObjects>
#if (Model == null || Model.Count() == 0)
{
<p>No results found</p>
}
else
{
<ul>
#foreach (YourObjects myobject in Model)
{
<li>#myObject.Name</li>
}
</ul>
}
You can do this by following these steps. In your controller, you return a partial view.
[HttpGet]
public virtual ActionResult LoadPartialViewDynamically()
{
var query = _repository.GetQuery();
return PartialView("_PartialViewName", query);
}
then in the view you have an empty div
<div id="partialgoeshere"></div>
and then load the partial view using jQuery:
function LoadPartialView() {
$.get("#Url.Action(MVC.ControllerName.LoadPartialViewDynamically())", { null }, function (data) {
$("#partialgoeshere").empty();
$("#partialgoeshere").html(data);
});
}
Hope this helps
I believe you can do something like this example, just using the change event on your dropdown instead. It's a simple jQuery call, you can find more on the jQuery website.
$("#dropdown").change(function() {
$("#destination").load("/Products/GetProduct", $(this).val(),
function(result) {
// do what you need to do
});
});
The first parameter is the view you need to call for the details.
The second parameter is the selected value.
The third parameter of the $.load function is the callback function, where you can parse the result and do what you need to do.
If you have a multiple select $(this).val() that will give you an array with the selected options.
If you want only return a Json object you may want to follow this example.
Use Ajax :)
http://api.jquery.com/jQuery.ajax/
Example:
$.post(window.gRootPath + "Customer/PartialView", { Question: questionId})
.done(function (data) {
$('#partialDiv').html(data.responceText);
});
You can use ajax to call action an then just insert html string using jQuery to the page where you want it to appear:
Server-side:
Render partial view to string
Renders partial view on server to html string, useful when you need to add partial view to ASP.NET MVC page via AJAX.
Client-side:
$('#yourDdl').change(function()
{
$.get('/InsertPartialViewUsingAjax', function (data)
{
$('#container').html(data);
});
});
The following article tells you how to do it with minimum javascript. Basically you return html instead of JSON to your response object.
https://www.simple-talk.com/content/article.aspx?article=2118

MVC3 & Razor: How to structure forms & actions to allow for postback-like functionality?

I have a view with a drop down list. The default value for this is stored in a session variable. However, the user change change this, in which case new data is entered.
I have a change handler on the drop down:
#using (Html.BeginForm())
{
#Html.DropDownListFor(model => model.SelectedID,
new SelectList(Model.SelectValues, "Key", "Value",
Model.SelectedID), "", new { onchange = "this.form.submit()" });
... more fields ...
<input type="submit" name="Save" />
}
[HttpPost]
public ActionResult Index(ViewModel vm)
{
... decide if I update my data or save the changes ...
}
I tried wrapping the select in a separate form tag, but then the value of my SelectedID not updated in my view model.
How can I determine when the form is posted from a drop down change, and when it is posted from a button click?
If you don't want to reload the entire page when the user changes the selection of the dropdown you could use AJAX to silently trigger a request to a different controller action that will do the necessary updates. For example:
#Html.DropDownListFor(
model => model.SelectedID,
new SelectList(Model.SelectValues, "Key", "Value"),
"",
new {
id = "myddl",
data_url = Url.Action("update")
}
)
and then in a separate javascript file:
$(function() {
$('#myddl').change(function() {
var form = $(this).closest('form');
$.ajax({
url: $(this).data('url'),
type: 'POST',
data: form.serialize(),
success: function() {
alert('update success');
}
});
});
});
and finally you could have a controller action responsible for the update:
[HttpPost]
public ActionResult Update(ViewModel vm)
{
... this will be triggered everytime the user changes some value in the
droipdown list
}
The simplest way would be to simply attach some behavior to those element's events and set a hidden field with the event target (which by now, should sound very familiar to __EVENTTARGET).
Like so:
$('#someButton').click(function()
{
$('#someHiddenField').val('someButton');
});
$('#someDropDown').change(function()
{
$('#someHiddenField').val('someDropDown');
});
And then your action method could inspect this value and act appropriately.
HOWEVER
It sounds like you're thinking in an outmoded concept for MVC. If you really needed some new information, you should consider using some Ajax and then having one of your action methods return a partial view if you want to update part of the page.

Resources