1

I am new to MVC. I have an Edit view for a Formula which in turn has an Ajax.ActionLink which renders a partial view which enables you to add Ingredients to a Formula. I am having trouble saving the list of ingredients from the partial view to the database. The Edit view is as follows:

@model CMSUsersAndRoles.Models.Formula

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@ViewData["ingredients"]

@Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
<h2>Edit</h2>


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

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

        <div class="form-group">
            @Html.LabelFor(model => model.SKU, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.SKU, new Dictionary<string, object> { { "readonly", true } })
                @*@Html.EditorFor(model => model.SKU, new { htmlAttributes = new { @class = "form-control" } })*@
                @Html.ValidationMessageFor(model => model.SKU, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Name, new Dictionary<string, object> { { "readonly", true } })
                @*@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })*@
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Ingredients, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10" id="Ingredients">
                @for (int i = 0; i < Model.Ingredients.Count; i++)
                {
                    @Html.EditorFor(model => model.Ingredients[i].ProductId, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.Ingredients[i].IngredientId, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.Ingredients[i].Name, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.Ingredients[i].Amount, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.Ingredients[i].UnitOfMeasure, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Ingredients, "", new { @class = "text-danger" })
                }
                @Ajax.ActionLink("Add ingredient", "AddIngredient", "Formulas", new { productId = Model.ProductId, ingredientId = (Model.Ingredients.Count +1), ingredients = @ViewData["ingredients"] }, new AjaxOptions
                {
                   UpdateTargetId = "Ingredients",
                   InsertionMode = InsertionMode.InsertAfter
                })
                @*@Ajax.ActionLink("Add ingredient", "AddIngredient", "Formulas", new AjaxOptions {
                UpdateTargetId = "Ingredients", InsertionMode = InsertionMode.InsertAfter
                })*@
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Instructions, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextAreaFor(model => model.Instructions, 15, 80, htmlAttributes: new { style = "width: 80%; max-width: 100%;" })
                @*@Html.EditorFor(model => model.Instructions, new { htmlAttributes = new { @class = "form-control" } })*@
                @Html.ValidationMessageFor(model => model.Instructions, "", new { @class = "text-danger" })
            </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")
}

The partial view is as follows:

@model CMSUsersAndRoles.Models.Ingredient
@{
    ViewBag.Title = "EditIngredients";


}

@{
    var productId = (int)ViewData["ProductId"];
    var ingredientId = (int)ViewData["IngredientId"];

}
@ViewData["ingredients"] = Model;

<table>

        @foreach (CMSUsersAndRoles.Models.Ingredient item in Model)
        {
            item.ProductId = productId;
            item.IngredientId = ingredientId;
            <tr>                  

                @Html.EditorFor(model => (item.ProductId), new { htmlAttributes = new { @class = "form-control" } })
                @Html.EditorFor(model => (item.IngredientId), new { htmlAttributes = new { @class = "form-control" } })
                @Html.EditorFor(model => item.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.EditorFor(model => item.Amount, new { htmlAttributes = new { @class = "form-control" } })
                @Html.EditorFor(model => item.UnitOfMeasure, new { htmlAttributes = new { @class = "form-control" } })
                @*</td>*@
            </tr>

        }

</table>

I have tried using ViewData to pass the list of Ingredients from the partial View to the parent View, and then using Ajax.ActionLink parameters to pass the list of Ingredients to the controller, but this has been unsuccessful, as it only passes the initial Ingredient from the parent View. The relevant pieces of the Formulas controller are the ActionLink:

public ActionResult AddIngredient(int productId, int ingredientId)

        {

            ViewData["ProductId"] = productId;
            ViewData["IngredientId"] = ingredientId;

            return PartialView("EditIngredients",  new Ingredient { ProductId = productId, IngredientId = ingredientId, Name = " ", Amount = 1, UnitOfMeasure = UnitOfMeasure.ml } );


        }

and the HttpPost:

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "ProductId,SKU,Name,Ingredients,Instructions")] Formula formula, List<Ingredient> ingredients)
        {
            if (ModelState.IsValid)
            {
                foreach (var ingredient in ingredients)
                {
                    formula.Ingredients.Add(ingredient);
                }

                db.Entry(formula).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(formula);
        }

Any help would be greatly appreciated.

Pismotality
  • 2,149
  • 7
  • 24
  • 30
  • 1
    Refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some options to dynamically add items to a collection in a view. And note that you partial is generating duplicate `name` attributes that will not bind to your model - refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) –  Jan 23 '17 at 23:04
  • Thanks for your input. BeginCollectionItem did the trick. – Pismotality Jan 26 '17 at 17:22

0 Answers0