I am having a strange issue when serializing a form to post back to a controller method. Some of the fields being passed are null (in the case of strings or nullable values) or zero (in the case of numeric values). For instance, with this simple configuration:
ViewModel:
public class HomeViewModel
{
public int FakeId { get; set; }
public string StringDataValue { get; set; }
public int IntegerDataValue { get; set; }
public HomeViewModel() { }
public HomeViewModel(int fakeId)
{
FakeId = fakeId;
StringDataValue = "This is some string data";
IntegerDataValue = 42;
}
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
HomeViewModel viewModel = new HomeViewModel(15);
return View(viewModel);
}
[HttpPost]
public JsonResult PostData(HomeViewModel model)
{
return JsonResult
{
Data = new
{
FakeId = model.FakeId,
StringData = model.StringDataValue,
IntegerData = model.IntegerDataValue
}
};
}
}
View:
#model WebApplication1.Models.HomeViewModel
#{
ViewBag.Title = "Home Page";
}
#using(Html.BeginForm())
{
#Html.HiddenFor(m => m.FakeId)
<div>
Fake ID: #Html.DisplayFor(m => m.FakeId)
</div>
<div>
String Data: #Html.TextBoxFor(m => m.StringDataValue)
</div>
<div>
Integer Data: #Html.TextBoxFor(m => m.IntegerDataValue)
</div>
<button id="btnPost">Post</button>
}
#section scripts
{
<script type="text/javascript">
$(function () {
$("#btnPost").on("click", function (e) {
e.preventDefault();
var model = $("form").serialize();
console.log(model);
$.post("PostData", JSON.stringify(model))
.success(function (d) {
console.log(d);
})
.error(function () {
console.log("error");
})
})
})
</script>
}
If I click the Post button I get this output for the two console.log() lines:
console.log(model): FakeId=15&StringDataValue=This+is+some+string+data&IntegerDataValue=42
console.log(d):
Object {FakeId: 0, StringData: "This is some string data", IntegerData: 0}
As you can see only the StringDataValue actually made it to the controller. However, if I add #Html.Hidden("dummy") in the view just above the hidden field for Model.FakeId then I get the following output:
console.log(model):
dummy=&FakeId=15&StringDataValue=This+is+some+string+data&IntegerDataValue=42
console.log(d):
Object {FakeId: 15, StringData: "This is some string data", IntegerData: 0}
That's a little better, but the IntegerDataValue still didn't make it to the controller. However, if I add #Html.Hidden("dummy2") somewhere after where the Model.IntegerDataValue is shown in the view I get the following output:
console.log(model):
dummy=&FakeId=15&StringDataValue=This+is+some+string+data&IntegerDataValue=42&dummy2=
console.log(d):
Object {FakeId: 15, StringData: "This is some string data", IntegerData: 42}
Now all of my view model values are being passed to the controller properly. I just don't understand why I have to put the dummy fields in to make it happen. It took me a long time to figure out why I was getting incomplete data in the controller methods until I realized what was happening.
Is it just me? Can anyone else duplicate this behavior? Am I missing something?
Take out JSON.stringify and try that. So something like
<script type="text/javascript">
$(function () {
$("#btnPost").click(function(e) {
e.preventDefault();
var model = $("form").serialize();
console.log(model);
$.post("Home/PostData", model)
.success(function(d) {
console.log(d);
})
.error(function() {
console.log("error");
});
});
});
</script>
Related
I have this AjaxForm in my partial view:
#using (Ajax.BeginForm("CreateStarter", "Player", new AjaxOptions { HttpMethod = "POST"}))
{
#Html.HiddenFor(m => m.OwnerID)
#Html.HiddenFor(m => m.Species)
#Html.HiddenFor(m => m.Gender)
#Html.ValidationSummary(true)
<div class="editor-label">#Html.LabelFor(m => m.Nickname)</div>
<div class="editor-field">
#Html.EditorFor(m => m.Nickname)
#Html.ValidationMessageFor(m => m.Nickname,"", new { #class = "text-danger" })
</div>
<input type="submit" value="Choose my pokemon">
}
In my controller post action i verify whether or not the model is valid. If it is not i return the partial view. If the model is not valid, the partial view is returned, but the validation message is not shown. Am I missing something?
This is my action:
[HttpPost]
public ActionResult CreateStarter(PokemonViewModel pokemonViewModel)
{
if (ModelState.IsValid)
{
Pokemon pokemonEntity = PokemonMapper.GetPokemonEntity(pokemonViewModel);
_userService.AddStarterPokemonToPlayer(pokemonViewModel.OwnerID, pokemonEntity);
return RedirectToAction("PlayerProfile");
}
else
{
return PartialView("_CreateStarter", pokemonViewModel);
}
}
And this is my model:
public class PokemonViewModel
{
public int ID { get; set; }
public int Species { get; set; }
public string Gender { get; set; }
[Required]
public string Nickname { get; set; }
public int OwnerID { get; set; }
}
Dealing with partial views and ajax is not straight forward but it is not hard either. You need to do a few things:
Create a container (<div>) in your main page wherein you house you
partial view.
In Ajax.BeginForm, specify what to do in:
InsertionMode
UpdateTargetId
OnFailure
OnBegin
In your controller you cannot simply return the view if the model is not valid, because that will send an HTTP 200 OK status indicating the request succeeded. You need to inform the client that something is not right.
Step 1 and Step 2
Imagine you have a main page and within that you will need to create a container for your partial and put your partial there. Also note the OnXxx function handlers.
<html>
<!-- Your main page -->
<div id="your-partial-form-id-here>
#using (Ajax.BeginForm( /* other arguments */
new AjaxOptions
{
HttpMethod = "POST",
OnBegin = "DoBeforeUpdatingPage",
OnFailure = "DoWhenThereIsAnIssue",
OnSuccess = "DoOnSuccess",
UpdateTargetId = "id-of-html-element-to-update-on-success",
}))
{
//...code
}
</div>
</html>
All the OnXxx handlers are javascript method names which will handle each scenario. Here is what you may do in each:
<script type="text/javascript">
function DoBeforeUpdatingPage() {
// Maybe nothing or maybe form validation
}
function DoWhenThereIsAnIssue(responseFromServer) {
// In your case the responseFromServer is the partial view
// so you just need to inject the html into the container
// you have created earlier.
$('your-partial-form-id-here').html(responseFromServer);
// Also since there was an issue, you may want to clear
// the thing which is updated when it is success
$('#id-of-html-element-to-update-on-success').empty();
}
function DoOnSuccess(responseFromServer) { // whatever... }
</script>
Step 3
Return BadRequest to the client so the javascript OnFailure handler is invoked; in our case the DoWhenThereIsAnIssue will be invoked.
public ActionResult SomeAction(SomeModel model)
{
if (!ModelState.IsValid)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return PartialView("_NameOfPartial", model);
}
}
I'm a beginner & I'm trying to develop a autocomplete search box with ASP.Net MVC 5. I use Northwind Database and Entity Framework 6.
Here is my index.cshtml code
#model IEnumerable<AutoComplete3.Models.Customers>
<link href="~/Content/jquery-ui.css" rel="stylesheet" />
<script type="text/javascript" src="~/Scripts/jquery-1.9.1.js"></script>
<script type="text/javascript" src="~/Scripts/jquery-ui.js"></script>
<script type="text/javascript">
$(function () {
$("#txtSearch").autocomplete({
source: '#Url.Action("GetCustomers")'
});
});
</script>
#using (#Html.BeginForm())
{
<b>Name : </b>
#Html.TextBox("searchTerm", null, new { #id = "txtSearch" })
<input type="submit" value="Search" />
}
Here is my CustomerController class
public class CustomersController : Controller
{
northwindEntities db = new northwindEntities();
public ActionResult Index()
{
return View(db.Customers);
}
[HttpPost]
public ActionResult Index(string SearchTerm)
{
List<Customers> customers;
if (string.IsNullOrEmpty(SearchTerm))
{
customers = db.Customers.ToList();
}
else
{
customers = db.Customers.Where(c => c.CompanyName.StartsWith(SearchTerm)).ToList();
}
return View(customers);
}
public JsonResult GetCustomers(string term)
{
List<string> customers;
customers = db.Customers.Where(c => c.CompanyName.StartsWith(term)).Select(y => y.CompanyName).ToList();
return Json(customers,JsonRequestBehavior.AllowGet);
}
}
This code is working when i am searching records, by entering keyword & clicking submit button. But the GetCustomer method cannot be called by the jquery script. Inspect Elements shows following error.
Uncaught TypeError: $(...).autocomplete is not a function
The text box should be suggest Company Names to the textbox itself. How to correct this.
Thanks.
Javascript
$(document).ready(function () {
$("#txtSearch").autocomplete({
source: function (request, response) {
$.ajax({
url: '#Url.Action("GetCustomers","Home")',
type: "POST",
dataType: "json",
data: { searchTerm: request.term },
success: function (data) {
response($.map(data, function (item) {
return { label: item.CompanyName, value: item.CompanyName };
}))
}
})
},
messages: {
noResults: "", results: ""
},
});
})
View
#using (#Html.BeginForm())
{
<b>Name : </b>
#Html.TextBox("searchTerm", null, new { #id = "txtSearch" })
<input type="submit" value="Search" />
}
Controller
Please update your controller with [HttpPost]
[HttpPost]
public JsonResult GetCustomers(string searchTerm)
{
List<string> customers;
customers = db.Customers.Where(c => c.CompanyName.StartsWith(term)).Select(y => y.CompanyName).ToList();
return Json(customers,JsonRequestBehavior.AllowGet);
}
I am trying to make an online bank website(for learning ASP.NET MVC).I have a class Account
class Account
{
int account_id;
String account_number;
decimal balance;
}
and I have a model for transaction.
public class MakeTransactionModel
{
[Required]
public String AccountFrom { get; set; }
[Required]
public String AccountTo { get; set; }
public Decimal OrignalBalance { get; set; }
[Required]
public Decimal Amount { get; set; }
[Required]
public String TransactionFor { get; set; }
}
Then in controller, i am putting accounts in ViewBag.
ViewBag.account_from = new SelectList(db.Accounts, "account_id", "account_number");
In View, I created a drop down for showing all accounts
#Html.DropDownListFor(u => u.AccountFrom, (SelectList)ViewBag.account_from, htmlAttributes: new { #class = "form-control", #id = "AccountFrom", onchange=#"
#Model.OrignalBalance = 1000; // I tried this but did not work
" })
Now , I am trying to show balance of selected account in an EditorFor
#Html.EditorFor(model => model.OrignalBalance, new { htmlAttributes = new { #id="OrignalBalance", #class = "form-control", disabled = "disabled", #readonly = "readonly" } })
I have all accountsin ViewBag and I am showing that accounts number in drop down (those accounts also have balance in it). I am trying to change value of EditorFor on DropDownFor value change but still unable to do that. I tried to do that using jquery, but i don't know can I use LINQ in jquery
My jquery code is
<script type="text/javascript">
$(document).ready(function () {
$(function () {
$('#AccountFrom').change(function () {
var selectedValue = $('#AccountFrom').text();
$('#OrignalBalance').val(#{new BankEntities().Accounts.SingleOrDefault(acc => acc.account_number == $('#AccountFrom').text())}); // I am trying to do this
});
});
}
)
</script>
It will be good if i find a good solution to do that, so I can update EditorFor on change event.
Thank you.
You should make an ajax call and pass the account number and get the amount from the server.
$(function()
{
$('#AccountFrom').change(function() {
var accountId= $('#AccountFrom').val();
var url="#Url.Action("Balance","Account")";
$.post(url+"?accountNumber="+accountId,function(response){
if(response.Status==="success")
{
$("#OrignalBalance").val(response.Balance);
}
else
{
alert("Invalid Account");
}
});
});
});
Assuming you have an action method to return the balance
[HttpPost]
public ActionResult Balance(string accountNumber)
{
//Of course you want to authorize the call
var db=new BankEntities();
var a= db.Accounts.FirstOrDefault(x=> x.account_number ==accountNumber);
if(a!=null)
{
return Json( new { Status="success", Balance=a.Balance });
}
else
{
return Json( new { Status="error"});
}
}
If you do not wish to make an action method and the ajax way, What you can do is, Create a dictionary of your account number and the balance and pass that as part of your view model and in your razor view, set that to a js object and in the change event you can query the js dictionary to get the value.
Also, I recommend to NOT use ViewBag to transfer data between your action method and your view for rendering the dropdown. You should add a strongly typed property to handle that.
So let's add some new properties to your view model.
public class MakeTransactionModel
{
// Your other existing properties here
public Dictionary<string,decimal> AccountBalances { set; get; }
// These 2 properties are for rendering the dropdown.
public int FromAccountId { set; get; }
public List<SelectListItem> FromAccounts { set; get; }
}
And in your GET action, fill this property with account number and corresponding balance value.
public ActionResult Transfer()
{
var vm = new MakeTransactionModel();
vm.AccountBalances = new Dictionary<string, decimal>();
// Hard coded for demo. You may read it from your db tables.
vm.AccountBalances.Add("CHECKING0001", 3450.50M);
vm.AccountBalances.Add("SAVINGS0001", 4450.50M);
//load the data for accounts.pls change to get from db
vm.FromAccounts = new List<SelectListItem>
{
new SelectListItem { Value="CHECKING0001", Text="Checking" },
new SelectListItem { Value="SAVINGS0001", Text="Saving" }
};
// to do : Load other properties also
return View(vm);
}
And in your razor view, serialize this property and set to a js object.
#model MakeTransactionModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(s=>s.FromAccountId,Model.FromAccounts,"Select")
#Html.EditorFor(model => model.OrignalBalance,
new { #id="OrignalBalance", #class = "form-control",
disabled = "disabled", #readonly = "readonly" } )
<input type="submit" />
}
#section Scripts
{
<script>
var balanceDict = #Html.Raw(Newtonsoft.Json.JsonConvert
.SerializeObject(Model.AccountBalances));
$(function () {
$('#FromAccountId').change(function() {
var accountId= $('#AccountFrom').val();
var v = balanceDict[accountId];
$("#OrignalBalance").val(v);
});
});
</script>
}
It may not seem like it, but this is pretty broad. Basic rundown, you'll either have to:
Serialize all accounts and balances into JSON and store them client-side:
This is more code than is appropriate here, but you could use JSON.net to get JSON for new BankEntities().Accounts.ToList() (something you should be getting from your controller code, by the way, not in your view), then set a window variable to that in your JavaScript, and call upon that whenever the value changes.
Untested, but something like:
var balances = #Html.Raw(JsonConvert.SerializeObject(new BankEntities()
.Accounts
// Filter by logged in user
.ToDictionary(c => c.account_number, c.balance));
$(document).ready(function () {
$(function () {
$('#AccountFrom').change(function () {
var selectedValue = $('#AccountFrom').text();
$('#OrignalBalance').val(balances[selectedValue]);
});
});
}
Introduce an API call performed through AJAX to get balances whenever the value changes.
Shyju beat me to this one, but it's probably a better way to do it, as long as you're comfortable introducing an API element. It's kind of advanced for first learning MVC, but it's not too complicated.
This is what I'd do in a production application (although I'd do it with Web API), but for just playing, the first option is a little quicker, and probably easier to understand and debug fully if you're just getting started.
The confusion here comes from where code is executed. Your script can't refer to the BankEntities because it's running client-side, as compared to server side.
JQuery knows nothing about LINQ, since it is client based. So, I suggest making an ajax request when the account from gets changed.
for example, in the view, make the ajax call
<script type="text/javascript">
$(document).ready(function() {
$('#AccountFrom').change(function() {
var selectedAccountNumber = $('#AccountFrom option:selected').text();
$.ajax({
url: "/Accounts/GetAccountBalance",
type: "POST",
dataType: "json",
data: { accountNumber: selectedAccountNumber },
success: function (
$('#OrignalBalance').val(data.Balance);
}
});
});
});
</script>
and have the following in the controller (let's say that you have a controller called Accounts)
public ActionResult GetAccountBalance(string accountNumber)
{
var account = db.Accounts.SingleOrDefault(a => a.account_number == accountNumber);
// add validation logic for account not exits
return Json(new { AccountNumber = accountNumber, Balance = account.balance });
}
I have the following Model
public class PersonViewModel {
public Guid Id { get; set; }
public string Firstname{ get; set; }
public string Lastname { get; set; }
}
My view looks something like this
#model Web.UI.PersonViewModel
<form class="form-horizontal" id="fuelux-wizard" method="post">
#Html.Bootstrap().TextBoxFor(p => p.Firstname).Placeholder("First name")
#Html.Bootstrap().TextBoxFor(p => p.Lastname).Placeholder("Last name")
#Html.Bootstrap().Button().Class("btn btn-next").Text(Model.Id == Guid.Empty ? "Lets Start" : "Continue").HtmlAttributes(new { onclick = "CreatePerson();" }).AppendIcon("fa fa-arrow-right")
</form>
Here is the js function which gets called when button is clicked
function CreateProposal() {
// DO Ajax call to create the person
$.ajax({ url: "CreatePerson", type: "POST", data: $('form').serialize() })
.done(function () {
$('.wizard').wizard('next');
})
.fail(function () {
alert("Unable to save.");
$("#personModal").modal("hide");
});
}
In my controller I have a CreatePerson method too
[HttpPost]
public JsonResult CreatePerson(PersonViewModel personViewModel )
{
if(personViewModel .Id == Guid.Empty)
personViewModel .Id = Guid.NewGuid();
return Json(personViewModel );
}
How do I update the ViewModel of the view? The idea is to create a person and this will assign a new Guid to the model. The text in the button will differ when there is a Guid assigned as to when there is not.
Is there a way I can update the viewmodel with the returned ajax result?
Can someone point me into the right direction?
Sounds like you need something like this:
<form class="form-horizontal" id="fuelux-wizard" method="post">
#Html.HiddenFor(m => m.Id)
#Html.Bootstrap().TextBoxFor(p => p.Firstname).Placeholder("First name")
#Html.Bootstrap().TextBoxFor(p => p.Lastname).Placeholder("Last name")
#Html.Bootstrap().Button().Class("btn btn-next").Text(Model.Id == Guid.Empty ? "Lets Start" : "Continue").HtmlAttributes(new { onclick = "CreatePerson();" }).AppendIcon("fa fa-arrow-right")
</form>
.done(function (data) {
$('#Id').val(data.Id);
$('.wizard').wizard('next');
})
You need to set the Id in the DOM. Think of the DOM as a data repository to store data from the server.
I have the following details in my project
MODEL
public class AnimalModels
{
public string AnimalId { get; set; }
public List<SelectListItem> AnimalList { get; set; }
}
VIEW
#model DropDownList.Models.AnimalModels
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<script src="#Url.Content("~/Scripts/Animal/Index.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div>
<fieldset>
<div>#Html.LabelFor(model => model.AnimalId)</div>
<div>#Html.DropDownListFor(model => model.AnimalId, Model.AnimalList)</div>
<div>#Html.ValidationMessageFor(model => model.AnimalId)</div>
</fieldset>
<input type="submit" name="Submit" value="Submit" />
</div>
}
CONTROLLER
public class AnimalController : Controller
{
//
// GET: /Animal/
public ActionResult Index()
{
AnimalModels model = new AnimalModels();
model.AnimalList = new List<SelectListItem>();
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(AnimalModels model)
{
//TODO: get animal list
model.AnimalList = new List<SelectListItem>();
return View(model);
}
}
Index.js
//[B]Document ready
$(document).ready(function () {
//Populate ddl using ajax (jquery)
$.ajax({
url: 'Resources/Animals.xml', // name of file with our data - link has been renamed
dataType: 'xml', // type of file we will be reading
success: parse, // name of function to call when done reading file
error: showError // name of function to call when failed to read
});
});
//[E]Document ready
function parse(xmlDoc) {
var options = new Array();
$(xmlDoc).find("ITEMS").each(function () {
$(this).find("ITEM").each(function () {
var optionValue = $(this).find('VALUE').text();
var optionLabel = $(this).find('TEXT').text();
options.push('<option value="' + optionValue + '">' + optionLabel + '</option>');
});
});
options = options.join('');
$('#AnimalId').append(options);
}
var showError = function (xhr, status, err) {
alert("Error loading file Animals.xml in Resources folder.\n" + xhr.status + " " + err + ".");
};
Animals.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<ITEMS>
<ITEM>
<VALUE>-1</VALUE>
<TEXT></TEXT>
</ITEM>
<ITEM>
<VALUE>0</VALUE>
<TEXT>Tiger</TEXT>
</ITEM>
<ITEM>
<VALUE>1</VALUE>
<TEXT>Lion</TEXT>
</ITEM>
</ITEMS>
my question is there any way to get all drop down list items when i hit submit button?
because the value is always null when i tried to debug it
add into the form -
#for (int i = 0; i < Model.AnimalList.Count; i++)
{
#Html.HiddenFor(model=>model.AnimalList[i].Value)
#Html.HiddenFor(model=>model.AnimalList[i].Text)
}
Code solution suggested by https://stackoverflow.com/users/2984635/user2984635
Model, index.js (unused), Animal.xml (unused) is remaining the same.
View and controller code are updated. bellow are the updates:
VIEW
#model DropDownList.Models.AnimalModels
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#*<script src="#Url.Content("~/Scripts/Animal/Index.js")" type="text/javascript"></script>*#
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div>
<fieldset>
<div>#Html.LabelFor(model => model.AnimalId)</div>
<div>#Html.DropDownListFor(model => model.AnimalId, Model.AnimalList)</div>
<div>#Html.ValidationMessageFor(model => model.AnimalId)</div>
//[B]the update
#for (int i = 0; i < Model.AnimalList.Count; i++)
{
#Html.HiddenFor(model=>model.AnimalList[i].Value)
#Html.HiddenFor(model=>model.AnimalList[i].Text)
}
//[E]the update
</fieldset>
<input type="submit" name="Submit" value="Submit" />
</div>
}
CONTROLLER
public class AnimalController : Controller
{
//
// GET: /Animal/
public ActionResult Index()
{
AnimalModels model = new AnimalModels();
model = new AnimalModels();
//[B]the update
model.AnimalList = new List<SelectListItem>();
model.AnimalList.Add(new SelectListItem { Value = "0", Text = "Tiger" });
model.AnimalList.Add(new SelectListItem { Value = "1", Text = "Lion" });
//[E]the update
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(AnimalModels model)
{
//[B]the update
model.AnimalList[int.Parse(model.AnimalId)].Selected = true;
//[E]the update
return View(model);
}
}