I don't know if this is a real question or I am just wrong with my idea on how to do things but how do you post a json object (from knockout) together with a form submit to an MVC controller?
First, this is my controller:
[HttpPost]
public ActionResult CreateLoanApp(PeopleViewModel MyViewModel)
{
//Do something on MyViewModel;
return RedirectToAction("Index");
}
[HttpPost]
public JsonResult CreateLoanApp(string deductions)
{
//Do something on string deductions;
}
And this is my view:
<h2>My Form </h2>
@using (Html.BeginForm(new { @class = "submitForm" }))
{
<label>Loan Amount</label>
@Html.DropDownListFor(model => model.Loan.LoanAmount, Model.DropDownOfLoanAmount, new { @class = "LoanAmount", @data_bind = "value: selectedLoanAmount" })
@Html.ValidationMessageFor(model => model.Loan.LoanAmount)
<label>Loan Receivable</label>
@Html.TextBoxFor(model => model.Loan.LoanReceivable, "{0:0,0.00}", new { @class = "LoanReceivable", @readonly = true, dir = "rtl", @data_bind = "value: loanReceivable" })
@Html.ValidationMessageFor(model => model.Loan.LoanReceivable)
<label>Interest</label>
@Html.TextBoxFor(model => model.Loan.Interest, "{0:0,0.00}", new { @readonly = true, @class = "Interest", dir = "rtl", @data_bind = "value: interest" })
<table class="input-group">
<tbody data-bind="foreach: loanDeductions">
<tr>
<td><strong data-bind='text: deductionName'></strong></td>
<td>
<input class="deductionCode form-control" data-bind='value: amount, valueUpdate: "afterkeydown"' /></td>
<td><a href='#' data-bind='click: $parent.removeLine'>Delete</a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-danger" data-bind="click: save">Save Deduction</button>
<button type="submit" class="btn btn-primary">Save changes</button>
}
As you can see I have 2 different save buttons:
1. "Save Deduction" button calls an ajax function that posts a json string called "deductions" to an action in my controller.
2. "Save Changes" button on the other hand, is a submit button that submits the form to my controller and passes "MyViewModel".
Now what I am trying to do is to combine the 2 buttons into one and pass the 2 objects together in a single controller.
What I mean is I want to create an action that accepts 2 parameters like this:
[HttpPost]
public ActionResult CreateLoanApp(PeopleViewModel MyViewModel, string deductions)
{
// Do something on MyViewModel
//Do something on string deductions;
}
Is this possible and if it is can you show me how it is done.
Any help will be greatly appreciated. IF you need more details just comment. Thanks!
Fortunately, I have been in this exact same situation. The way it was handled was by updating the data property in the beforeSend object.
Here is the controller action (mainView is what the form will bind too and is your page view model, the tabView param will be the one from the knockout model):
[HttpPost]
public ActionResult Save(MainViewModel mainView, TabViewModel tabView)
{
//do some work here
}
And the Html of the view, indicating the before send setter (MainScript is the name of the javascript object used to manage the client side work of this page):
@using(Ajax.BeginForm("Save",new AjaxOptions {HttpMethod="POST", OnBegin = "MainScript.beforeSend", OnSuccess="MainScript.onSuccess(data)"}})
{
//some html form elements
<input type="submit" value="Send" id="btnSave" />
}
@section Scripts{
//a bunch of scripts are loaded here
<script type="text/javascript">
$(document).ready(function(){
MainScript.initialize(@Html.Raw(Json.Encode(Model)));
});
</script>
}
The initialize takes in the ViewModel so that we can store it in the client and setup the knockout bindings, not important here, but a nice thing to know if you haven't learned how to do that yet.
Finally, when the user hits submit, the MainScript.beforeSend(), function will be called:
beforeSend: function() {
var tabViewModel = tabKnockoutVM.GetModel();
this.data = this.data + "&" + convertToFormData(tabViewModel);
}
Two things to note here:
First, you need to create or get a model from your knockout model that matches your .Net object in the parameter. If you Knockout model is identical, you can use the Knockout Mapping library. If not, I would suggest putting a function on your Knockout model that builds the object for you.
Second, there needed to be a function that converted the object to a format that could be parsed by .Net. That is where the 'convertToFormData' function call comes into play, which, is this (mind you, this code was found elsewhere):
function convertToFormData(obj,prefix) {
var dataArray=[];
for (var op in obj) {
if(op in obj) {
var k = prefix ?
(isNaN(op ) ?
prefix + "." + op :
prefix + "[" + op + "]") :
op;
var v = obj[op];
dataArray.push(typeof(v==="object"?
convertToFormData(v,k) :
encodeURIComponent(k)+"=" + encodeURIComponent(v));
}
}
return dataArray.join("&");
}