0

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>
TLBB
  • 89
  • 1
  • 10
  • 1
    Please post some controller code and view code. I'm not clear how you are using Ajax. If you are using jquery's ajax method to do some data checking and then looking at the result, there is no reason you can't send a different data structure than is used to get/post your main controller methods. – stephen.vakil Aug 30 '16 at 17:06
  • Added view code as well as script code. I'm unable to get the value of the file for parameter XmlValue, when passed to controller it is null, even when a file is selected. – TLBB Aug 30 '16 at 19:29
  • This appears to be a file upload you are sending via ajax, and you don't seem to be processing it in a manner I'm used to seeing. see: http://stackoverflow.com/questions/20420828/file-upload-using-mvc-4-with-ajax – stephen.vakil Aug 30 '16 at 20:36
  • You need to use `FormData` to post a file using ajax (refer [this answer](http://stackoverflow.com/questions/29293637/how-to-append-whole-set-of-model-to-formdata-and-obtain-it-in-mvc/29293681#29293681)), but nothing in you code makes sense - a file input posts back to `HttpPostedFileBase` –  Aug 30 '16 at 22:25

0 Answers0