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.
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 am submitting some info using an Ajax post which is supposed to refresh a partial view. The info gets sent to the database however on return of the partial view, the Fees.Feetype data can't be rendered as it is null.
On manual refresh, the Fees.FeeType is rendered with no problem.
How do I get the FeeType to be included in the partial view after the ajax post?
Models
public class Members
{
public virtual ICollection<Fees> Fees { get; set; }
}
public class Fees
{
public int FeeTypesId { get; set; }
public virtual FeeTypes FeeType { get; set; }
public virtual Members Members { get; set; }
}
public class FeeTypes
{
public int Id { get; set; }
public string FeeTypeName { get; set; }
}
Ajax Post
var token = $('[name=__RequestVerificationToken]').val();
var postData = {
__RequestVerificationToken: token,
FeeOccurence: feeOccField.val(),
FeeAmount: amountField.val(),
FeeStatus: statusField.val(),
FeeTypesId: typeField.val(),
FeeDate: dateField.val(),
MemberNo: memberNo
};
$.ajax({
url: '/Members/AddFees',
type: 'POST',
data: postData,
success: function (members) {
alert(result);
$("#details").html(members);
},
error: function (result) {
alert(result);
},
traditional: true
Controller
public ActionResult AddFees (Fees fees)
{
if (ModelState.IsValid)
{
db.Fees.Add(fees);
db.SaveChanges();
Members members = new Members();
members = db.Members.Find(fees.MemberNo);
return PartialView("~/Views/Members/_MemberDetails.cshtml", members);
}
}
Views
#model AccPortal.Models.Members
<div class="col-md-7 md-fees" id ="details">
#Html.Partial("_MemberDetails", Model)
</div>
//This is the partial view.
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
//The error happens on this line but all other Fees properties are rendered without issue.
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
}
These are the things you need to change in your code,
First, return a partial view with model from your Action method like this,
return PartialView("_MemberDetails", members);
Datatype should be html in your ajax method. So add this bellow line inside ajax method. You can add this line after this line data: postData,
dataType: "html",
Now in your ajax success method, change like this,
success: function (members) {
//alert(result); what is result here, that you have written
$("#details").html(''); //first make it blank then render
$("#details").html(members);
},
In your partial view, you need to check whether FeeType is there or not before render the value,
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
if(item.FeeType != null)
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
else
<td class="md-fee-value"data-type="status"></td> //You can write NA here
}
The issue with the null type was lazy loading in the end.
I had to requery the db after I had saved the changes and include Fees.FeeTypes to return the correctly populated model.
members = db.Members.Find(fees.MemberNo);
members.Fees = db.Fees.Include(x => x.FeeType).Where(x => x.Members.MemberNo.Equals(x.MemberNo)).ToList();
you need to check for null values before populating in the list to avoid null exception
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
if(item.FeeType != null)
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
}
because some data might be missing which is causing problem so with if condition it will populated only that data which is not null
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 a strongly typed view and I'm trying to pass the input from a textbox upon a button click to an action using BeginForm. My code keeps passing a null object to the action method in the controller. How do I pass the object to the controller via the form ?
#using (#Html.BeginForm("GetQueueInfoWorkorder","Home", FormMethod.Post, new { id = Model}))
{
#Html.TextBoxFor(x=> x.ID);
<input type="Submit" value ="Search" class="ui-button-icon-secondary"/>
}
Here is the actionmethod :
[HttpPost]
public ActionResult GetQueueInfoWorkorder(UserResponse id)
{
//Check queue complete
int woNumber = Convert.ToInt32(id);
tb_QueueCompleted completed = db.tb_QueueCompleted.SingleOrDefault(x => x.WorkOrderNumber == woNumber);
if (completed != null)
{
var u = new UserResponse { ID = completed.QueueId.ToString() };
GetLogInfoCompleted(u);
return View("GetLogInfo");
}
//check queue pending
return View();
}
I think you're fairly close, but make these changes & it should work as expected:
Model:
public class UserResponse
{
public int ID { get; set; }
}
View:
#model UserResponse
#using (Html.BeginForm("GetQueueInfoWorkorder", "Home"))
{
#Html.TextBoxFor(x => x.ID);
<input type="Submit" value ="Search" class="ui-button-icon-secondary"/>
}
Action method:
public ActionResult GetQueueInfoWorkorder(UserResponse model)
{
int woNumber = model.ID;
//...
}
if the #model of your view is UserResponse , then on submission of this page the model (UserResponse) will automatically get submitted to the controller. Where have you declared the #model for the view.
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>