0
votes

I have a database that contains the following tables: DIM_Invoice, DIM_Carton, DIM_InvoiceItem. It's a hierarchical structure ā€“ An Invoice contains one or more Cartons. A Carton contains one or more InvoiceItems.

Here is my ER Diagram: ER Diagram

Eventually I need to create a hierarchical view, which will display the Invoice information, the Cartons related to the Invoice, and the InvoiceItems related to each Carton. There are a couple fields at each level that should be editable.

For now, however, Iā€™m just trying to get the first two levels to work: An Invoice has multiple Cartons associated with it.

The following are my models which were generated by Entity Framework:

namespace Invoices2.Models
{
    using System;
    using System.Collections.Generic;

    public partial class DIM_Invoice
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public DIM_Invoice()
        {
            this.DIM_Carton = new HashSet<DIM_Carton>();
        }

        public int LPK_Invoice { get; set; }
        public string InvoiceNumber { get; set; }
        public string PONumber { get; set; }
        public Nullable<System.DateTime> InvoiceDate { get; set; }
        public Nullable<System.DateTime> OrderDate { get; set; }
        public Nullable<System.DateTime> ShipDate { get; set; }
        public string Distributor { get; set; }
        public Nullable<bool> AllReceived { get; set; }
        public Nullable<int> RowVersion { get; set; }
        public string Comments { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<DIM_Carton> DIM_Carton { get; set; }
    }
}

namespace Invoices2.Models
{
    using System;
    using System.Collections.Generic;

    public partial class DIM_Carton
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public DIM_Carton()
        {
            this.DIM_InvoiceItem = new HashSet<DIM_InvoiceItem>();
        }

        public int LPK_Carton { get; set; }
        public string CartonNumber { get; set; }
        public Nullable<int> LPK_Invoice { get; set; }
        public string InvoiceNumber { get; set; }
        public Nullable<int> RowVersion { get; set; }
        public string Comments { get; set; }

        public virtual DIM_Invoice DIM_Invoice { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<DIM_InvoiceItem> DIM_InvoiceItem { get; set; }
    }
}

The following are the Edit sections for DIM_InvoiceController. Again they were auto-generated by Visual Studio, except that I've edited the Post section so that it does not redirect to the Index view.

// GET: DIM_Invoice/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    DIM_Invoice dIM_Invoice = db.DIM_Invoice.Find(id);
    if (dIM_Invoice == null)
    {
        return HttpNotFound();
    }
    return View(dIM_Invoice);
}

// POST: DIM_Invoice/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "LPK_Invoice,InvoiceNumber,PONumber,InvoiceDate,OrderDate,ShipDate,Distributor,AllReceived,RowVersion,Comments")] DIM_Invoice dIM_Invoice)
{
    if (ModelState.IsValid)
    {
        db.Entry(dIM_Invoice).State = EntityState.Modified;
        db.SaveChanges();
        return View(dIM_Invoice);
        //return RedirectToAction("Index");
    }
    return View(dIM_Invoice);
}

I've moved a copy of the automatically generated Edit view for DIM_Carton into the Shared/EditorTemplates folder so that it can act as an EditorTemplate, and then simplified it a bit and removed some of the fields because I only need to display the CartonNumber field:

@model Invoices2.Models.DIM_Carton

    <div class="form-horizontal">
        <h4>DIM_Carton</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.LPK_Carton)

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

    </div>

Finally, here is the Edit view for DIM_Invoice, which is trying to utilize the DIM_Carton EditorTemplate by calling @Html.EditorFor(model => model.DIM_Carton)

@model Invoices2.Models.DIM_Invoice

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>


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

    <div class="form-horizontal">
        <h4>DIM_Invoice</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.LPK_Invoice)

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

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

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

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

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

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

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

// ***Here's the call to the EditorTemplate*** 
        <div class="form-group">
            <div class="col-md-10">
                @Html.EditorFor(model => model.DIM_Carton)
            </div>
        </div>

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

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

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

OK, I'm new to MVC so bear with me please... here is the specific problem I'm trying to solve:

At first I tried to do with utilizing a partial view where the DIM_Invoice Edit view would look at the DIM_Carton Edit view as a partial view. However, whenever a edit was done to DIM_Invoice in the browser and posted back, the information displayed in the partial view would not be retained (i.e. it just "disappeared", for lack of a better description, from the page).

After googling I found a post here on stackoverflow which said I should use EditorTemplates. The post gave a resource for EditorTemplates and I also found another resource on simple-talk which was helpful.

However, after trying to use this method, I still have the same problem... the data in the EditorTemplate is not retained after post-back from the main view.

After googling some more, I didn't find any further information that would help.

I did notice that the simple-talk example uses a div class called "editor-field", but IntelliSense is not giving me "editor-field" as an option... so maybe there's a library I'm missing or something?

So... how do I get the EditorTemplate to persist after a post-back from the main view?

Thanks very much in advance to anyone who can help.

1
I'm still trying to solve this problem... if anyone has any helpful information, I'd be grateful. Thanks in advance. ā€“ groovepriest

1 Answers

0
votes

I've come up with at least a temporary solution to this problem, so I'll tell here what I have done for now in order to get it to work. However, I think this is kind of a hack and I should be doing it better somehow. So, if anyone has any information I'm still listening.

So basically I created sort of a loop that will force the page to refresh after the postback. I changed my controller for the Edit action method so that when the HttpPost is successful it redirects to another controller action method called RefreshInvoiceData:

// POST: DIM_Invoice/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "LPK_Invoice,InvoiceNumber,PONumber,InvoiceDate,OrderDate,ShipDate,Distributor,AllReceived,RowVersion,Comments")] DIM_Invoice dIM_Invoice)
        {
            if (ModelState.IsValid)
            {
                db.Entry(dIM_Invoice).State = EntityState.Modified;
                db.SaveChanges();
                //return View(dIM_Invoice);
                //return RedirectToAction("Index");
                return RedirectToAction("RefreshInvoiceData", new { invoiceId = dIM_Invoice.LPK_Invoice.ToString() });
            }
            return View(dIM_Invoice);
        }

The RefreshInvoiceData action method then redirects back to the Edit action method and passes it the ID for the current Invoice. This kinda tricks the entire view into refreshing.

// GET: DIM_Invoice
        public ActionResult RefreshInvoiceData(int? invoiceId)
        {
            if (invoiceId == null)
            {
                return Content("the invoiceId is null");
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            return RedirectToAction("Edit", new { id = invoiceId });
        }

If anyone has a better answer I'd love to hear it.