0

I got two model classes like this

    public class JobViewModel
    {
        public int Id { get; set; }
        public float Price { get; set; }
        public int JobSubCategoryId { get; set; }
        public string jobDescription { get; set; }
        public List<Machine> Machines  { get; set; }
        public int SpecialRequirementId { get; set; }
    }

    public class Machine
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public string Brand { get; set; }
    }

Now, When a user creates a new "Job" he must be able to create multiple "Machine" with it. How can I make this possible inside my create view?

Mostafiz
  • 7,243
  • 3
  • 28
  • 42
None
  • 5,582
  • 21
  • 85
  • 170
  • Refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some options –  Aug 21 '16 at 10:41
  • @StephenMuecke Isn't there any way that, I can send all the new machines in a single button click other than using partial views and sending them as each one created.? – None Aug 21 '16 at 10:45
  • I assume you have not yet bothered to read the link (that is exactly what my answer does - save the object and all its dynamically created child objects in one action) –  Aug 21 '16 at 10:47

1 Answers1

0

As well as Stephen's method, you can achieve all this relatively simply, without partial views.

In the first instance, you should revise your model a little. Add to Machine:

    // Foreign key 
    public int JobID { get; set; }

    // Navigation properties 
    public virtual Job Job{ get; set; }

The Job model, you have not shown, but it needs to be:

public class Job
{
    public int Id { get; set; }
    public float Price { get; set; }
    public int JobSubCategoryId { get; set; }
    public string JobDescription { get; set; }
    public int SpecialRequirementId { get; set; }
    public virtual List<Machine> Machines { get; set; }
}

Here is my complete JobViewModel:

public class JobViewModel
{
    public JobViewModel()
    {
        Machines = new List<Machine>();
    }
    public int Id { get; set; }
    public float Price { get; set; }
    public int JobSubCategoryId { get; set; }
    public string JobDescription { get; set; }
    public int SpecialRequirementId { get; set; }
    public List<Machine> Machines { get; set; }
    public string NewMachineBrand { get; set; }
    public string NewMachineType { get; set; }
    public string NewMachineName { get; set; }
    public void AddMachine()
    {
        Machine tmp = new Machine { Brand = NewMachineBrand, Type = NewMachineType, Name = NewMachineName };
        Machines.Add(tmp);
        NewMachineBrand = NewMachineType = NewMachineName = null;
    }
    public Job GetJob()
    {
        Job job = new Job();
        job.JobDescription = JobDescription;
        job.Price = Price;
        job.JobSubCategoryId = JobSubCategoryId;
        job.SpecialRequirementId = SpecialRequirementId;
        job.Machines = new List<Machine>();
        foreach (Machine m in Machines)
        {
            job.Machines.Add(m);
        }
        return job;
    }
}

When creating your create view based on JobViewModel, you will need to add two things that are not defaulted for you, firstly a table to hold the new Machines, and secondly a button to add each machine in turn.

My complete create.cshtml view looks like this:

@model JobMachinesMVC.Models.JobViewModel

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
<div class="form-horizontal">
    <h4>Job</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.JobSubCategoryId, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.JobSubCategoryId, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.JobSubCategoryId, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.JobDescription, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.JobDescription, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.JobDescription, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.SpecialRequirementId, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.SpecialRequirementId, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.SpecialRequirementId, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.NewMachineBrand, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.NewMachineBrand, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.NewMachineBrand, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.NewMachineType, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.NewMachineType, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.NewMachineType, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.NewMachineName, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.NewMachineName, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.NewMachineName, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
            <table>
                <thead>
                    <tr>
                        <th style="text-align:right">
                            @Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Brand)
                        </th>
                        <th style="text-align:right">
                            @Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Name)
                        </th>
                        <th style="text-align:right">
                            @Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Type)
                        </th>
                    </tr>
                </thead>
                <tbody>
                @for (int i = 0; i < Model.Machines.Count; i++)
                {
                    <tr>
                            <td style="text-align:right">@Html.HiddenFor(m => m.Machines[i].Id)@Html.DisplayFor(m => m.Machines[i].Brand)@Html.HiddenFor(m => m.Machines[i].Brand)</td>
                            <td style="text-align:right">@Html.DisplayFor(m => m.Machines[i].Name)@Html.HiddenFor(m => m.Machines[i].Name)</td>
                            <td style="text-align:right">@Html.DisplayFor(m => m.Machines[i].Type)@Html.HiddenFor(m => m.Machines[i].Type)</td>
                        </tr>
                }
                </tbody>
            </table>
    </div>

    <div class="form-group">
            <input type="submit" value="Add Machine" name="addmachine" class="btn btn-default" />
    </div>


    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>
}

<div>

        @Html.ActionLink("Back to List", "Index")
    </div>

    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }

A couple of things to note here. I always include a @Html.HiddenFor in such a sub-table, because @Html.DisplayFor items can be lost when posting back to the controller. Secondly, there are two input type="submit" on the same View. One is given a name attribute. This is so that the Controller can distinguish between the two clicks.

The relevant lines from my controller are these:

    // GET: Jobs/Create
    public ActionResult Create()
    {
        JobViewModel job = new JobViewModel();
        return View(job);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(JobViewModel jobvm)
    {
        if (Request.Form["addmachine"] != null)
        {
            jobvm.AddMachine();
            ModelState.Remove("NewMachineName");
            ModelState.Remove("NewMachineType");
            ModelState.Remove("NewMachineBrand");
            return View(jobvm);
        }
        if (ModelState.IsValid)
        {
            Job job = jobvm.GetJob();
            db.Jobs.Add(job);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(jobvm);
    }

If "addmachine" is clicked, the new machine values get added to the Machines List, get reset and the form is redisplayed. Note you need to set the ModelState even though the ViewModel sets the values to null, otherwise you old values persist in the view. If Create is clicked, the model is checked for ValidState, and the job is saved. What about the Machine table? Because the models are set up as outlined above, internally MVC knows that it has to save the values to Machine as well.

Please note that the above illustration is very crude. I have applied no styling apart from that which you get "out of the box". You will want to tidy this up (a lot!), but I hope I have given you a good start in one way to approach this problem.