1

So I'm trying to create a Invoice form. In this form I want to fill in some customer details and add products, all in the same form.
I created a static example of how I want it to look: mockup of this form The idea is to manually fill in a product, and if I want another product, I'd press the green plus glyphicon and add another product.
By clicking on the create button I want to submit this entire form with the customer details and the array/list of added products.

This is the code I have so far:

This is the view model that I use inside the view:

public class InvoiceViewModel
{
    public Enums.Gender Gender { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string StreetName { get; set; }
    public string HouseNumber { get; set; }
    public string PostCode { get; set; }
    public string City { get; set; }
    public string PhoneNumber { get; set; }
    public string EmailAddress { get; set; }
    public List<InvoiceItemViewModel> InvoiceItems { get; set; }
}

This is the InvoiceItemViewModel that I use inside the partial view:

public class InvoiceItemViewModel
{
    public string Name { get; set; }
    public Enums.UnitType UnitType { get; set; }
    public int Ammount { get; set; }
    public decimal PriceWithoutVAT { get; set; }
}

This is the Create view:

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

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

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

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

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

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

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

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

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

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

        <hr />
        <h4>Products</h4>
        <div class="container">
            <div class="row">
                <div class="col-md-6">Name</div>
                <div class="col-md-2">Unit(s)</div>
                <div class="col-md-2">Ammount</div>
                <div class="col-md-2">Price</div>
            </div>
            <div class="form-group">
                <div class="row">
                    <div class="col-md-11">
                        @Html.Partial("~/Views/Invoice/InvoiceItem.cshtml")
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-11">
                        @Html.Partial("~/Views/Invoice/InvoiceItem.cshtml")
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-11">
                        @Html.Partial("~/Views/Invoice/InvoiceItem.cshtml")
                    </div>
                    <div class="col-md-1">
                        <span class="glyphicon glyphicon-plus" style="color: green;"></span>
                    </div>
                </div>
            </div>
        </div>

        <br />
        <br />
        <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>
}

And this is the partial view for an invoice item:

<div class="col-md-6">
    @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
</div>
<div class="col-md-2">
    @Html.EnumDropDownListFor(model => model.UnitType)
</div>
<div class="col-md-2">
    @Html.EditorFor(model => model.Ammount, new { htmlAttributes = new { @class = "form-control" } })
</div>
<div class="col-md-2">
    @Html.EditorFor(model => model.PriceWithoutVAT, new { htmlAttributes = new { @class = "form-control" } })
</div>

So my question is, how do I add multiple invoice items to my form that I can use in the controller when I submit? I'm afraid that it's going to be a lot of javascript DOM manipulations.

kawa
  • 65
  • 1
  • 3
  • 11
  • Refer also [this answer](http://stackoverflow.com/questions/40539321/partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) for a more complete example using `BeginCollectionItem()` –  Aug 05 '17 at 22:09

1 Answers1

0

Have a hidden template of the row on your page, then on the click of plus button, you can read the innerhtml of the template and append to list container.

Sample code

var templatehtml = $('#template')[0].innerHTML;
var newhtml = templatehtml
        .replace(new RegExp('{{counter}}', 'g'), counter.toString())
        .replace('{{counter++}}', (Number(counter) + Number(1)).toString());
$('#container').append(newhtml);

This will get you started, however it's not the most optimal code and the recommended way is to use a template library like JSRender

Abhishek Siddhu
  • 577
  • 6
  • 22