I've been running into an issue for a while, and I'm not sure how I can get around it or how to solve it. I'm extremely new to Ajax, so trying to find examples of my issue have been falling short.
My issue is I need to send a model to a controller so I can validate/verify specific parameters server side before the user submits a from to the server. This is basically conditional validation server side, parameter A is only required if parameter B is false, so when the user fails to have a file in parameter A when hitting submit, the Ajax call will be sending server side validation to see if the user is allowed to submit a null file value.
From what I have read, I can't send a different model than what the view model is. Because I am using a partial view, the view model of the main view is still the model that must be sent to the controller via the ajax call, even though I want to send a nested model. (Please tell me if this is wrong, I've been reading so many code examples and questions on this subject)
However, it gets a little more complicated, because the view model consists of nested models with lists, with more nested models and lists of models within the nested models.
Here is the current Ajax call in the main view
<script type="text/javascript">
$(function () {
$("#test").change(function () {
var values =
{
XmlValue: $("#test").val(),
}
$.ajax({
type: 'POST',
url: '/TestSuites/UploadValidation',
data: JSON.stringify(values),
contentType: 'application/json; charset=utf-8',
dataType: "json",
success: function (data) {
alert(data);
}
});
});
});
</script>
Here is the layout of the view model.
AddTestSuiteViewModel (Main viewmodel)
- List<AddTestStepDisplayModel>
- AddTestStepDisplayModel
- AddTestStepParameterModel (PartialView view model)
- List<AddXmlParameterModel>
- AddTestStepXmlParameterModel
Here is the AddTestStepXmlParameterModel in question
public class AddTestStepXmlParameterModel
{
public string ParameterName { get; set; }
public string Description { get; set; }
[AssertThat("BindingExists == false", ErrorMessage = "An Xml file is required to save this test step")]
[FileExtensions(Extensions = "xml", ErrorMessage = "Specify an XML file.")]
public HttpPostedFileBase XmlValue { get; set; }
public bool BindingExists { get; set; }
}
Here are the other models in nested order
AddTestSuiteViewModel
public class AddTestSuiteViewModel
{
public AddTestSuiteViewModel()
{
this.AddTestSuiteModel = new AddTestSuiteModel();
this.IsTestSuiteComplete = false;
}
public AddTestSuiteViewModel(TestSuite testSuite)
{
ParameterValidator.ValidateObjectIsNotNull(testSuite, "testSuite");
this.TestSuiteId = testSuite.TestSuiteId;
this.AddTestSuiteModel = new AddTestSuiteModel();
this.AddTestSuiteModel.TestSuiteName = testSuite.Name;
this.AddTestStepModel = new AddTestStepModel(testSuite);
this.AddTestStepModel.PreStepSequenceNumber = 1;
this.AddTestStepModel.MainStepSequenceNumber = 1;
this.PreTestSteps = new List<AddTestStepDisplayModel>();
this.MainTestSteps = new List<AddTestStepDisplayModel>();
this.DefinedTestStepNames = new List<string>();
this.DefinedTestStepIds = new List<int>();
}
public int TestSuiteId { get; set; }
public AddTestSuiteModel AddTestSuiteModel { get; set; }
public AddTestStepModel AddTestStepModel { get; set; }
public List<AddTestStepDisplayModel> PreTestSteps { get; set; }
public List<AddTestStepDisplayModel> MainTestSteps { get; set; }
public List<string> DefinedTestStepNames { get; set; }
public List<int> DefinedTestStepIds { get; set; }
}
AddTestStepDisplayModel
public class AddTestStepDisplayModel
{
public AddTestStepDisplayModel()
{
this.IfOtherTestStepsAreDependentOnTestStep = false;
this.DependencyViewModel = new DependencySelectViewModel();
}
public int TestStepId { get; set; }
public string TestStepName { get; set; }
public AddTestStepParametersModel AddTestStepParametersModel { get; set; }
}
AddTestStepParametersModel
public class AddTestStepParametersModel
{
public AddTestStepParametersModel()
{
this.TestStepXmlParameterDefinitions = new List<TestStepParameterDefinition>();
this.AddTestStepXmlParameterModels = new List<AddTestStepXmlParameterModel>();
}
public List<TestStepParameterDefinition> TestStepXmlParameterDefinitions { get; set; }
public List<AddTestStepXmlParameterModel> AddTestStepXmlParameterModels { get; set; }
public string SelectedTestStepValue { get; set; }
public int XmlParameterCount()
{
return this.TestStepXmlParameterDefinitions.Count();
}
}
And then it goes to a list of AddTestStepXmlParameterModel.
I need an Ajax call that builds out the complex main view model with a single AddTestStepXmlParameterModel that holds two values. I'm absolutely stumped on how to do this, if anyone has any code they can share, or a solution/guide to a similar problem I'm all ears.
Thanks, this was a long one.
EDIT: Here are the view and controller pages
[HttpPost]
public JsonResult UploadValidation(AddTestStepXmlParameterModel model)
{
return Json(JsonRequestBehavior.AllowGet);
}
Main View
@model Models.AddTestSuiteViewModel
@{
ViewBag.Title = "Manage Test Suite";
}
<h1>Manage Test Suite</h1>
<head>
<script src="~/Scripts/jquery-3.1.0.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/expressive.annotations.validate.js"></script>
</head>
@Html.Partial("MainTestStepTable", Model)
<script type="text/javascript">
$(function () {
$("#test").change(function () {
var values =
{
XmlValue: $("#test").val(),
}
$.ajax({
type: 'POST',
url: '/TestSuites/UploadValidation',
data: JSON.stringify(values),
contentType: 'application/json; charset=utf-8',
dataType: "json",
success: function (data) {
alert(data);
}
});
});
});
</script>
Partial View
@model Models.AddTestSuiteViewModel
@{
int i = 0;
}
@foreach (var testStep in Model.MainTestSteps)
{
@using (Html.BeginForm("EditTestStep", "TestSuites", FormMethod.Post, new {enctype = "multipart/form-data"}))
{
<div>
@Html.Partial("AddParameters", testStep.AddTestStepParametersModel)
</div>
}
Add Parameters Partial View
@model Models.AddTestStepParametersModel
for (int k = 0; k < Model.XmlParameterCount(); k++)
{
<div>
<br />
@Html.HiddenFor(m => m.AddTestStepXmlParameterModels[k].ParameterName, new { @Value = Model.TestStepXmlParameterDefinitions[k].Name })
@Html.HiddenFor(m => m.AddTestStepXmlParameterModels[k].Description, new { @Value = Model.TestStepXmlParameterDefinitions[k].Description })
@Html.HiddenFor(m => m.AddTestStepXmlParameterModels[k].ParameterType, new { @Value = Model.TestStepXmlParameterDefinitions[k].ParameterType })
@Html.LabelFor(m => m.AddTestStepXmlParameterModels[k].ParameterName, "Parameter Name: " + @Model.TestStepXmlParameterDefinitions[k].Name, htmlAttributes: new { @class = "control-label" })
<br />
<div class="col-md-4">
<div class="form-group">
@Html.EditorFor(m => Model.AddTestStepXmlParameterModels[k])
</div>
</div>
<div class="col-md-3">
<div class="form-group">
@Html.LabelFor(m => m.ParameterRepositoryDataCheckViewModelList[k].RepositoryDataExist, "Data Exist in Database?")
@Html.CheckBoxFor(m => m.ParameterRepositoryDataCheckViewModelList[k].RepositoryDataExist, new { disabled = "disabled" })
</div>
</div>
</div>
}
Editor Template of model
@model Models.AddTestStepXmlParameterModel
<div>
@Html.TextBoxFor(m => Model.XmlValue, new { type = "file", @class = "btn btn-default btn-file", style = "color:transparent", onchange = "this.style.color = 'black'", id= "test" })
@Html.ValidationMessageFor(m => Model.XmlValue, "", new {@class = "text-danger"})
</div>