0

I am new to MVC and very new to jQuery. I am trying to populate text boxes in a partial view using jQuery that resides in the parent view. The relevant sections of the parent view are as follows:

   @{
        ViewBag.Title = "Edit";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    @Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

    <script type="text/javascript">
        function RemoveRow() {
            var $tr = $(this).closest('#row');
            $tr.remove();
        }
    </script>


    <h2>Edit</h2>

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

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


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

 <div class="form-group">

            </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.Company, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Company, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Company, "", new { @class = "text-danger" })
            </div>
        </div>

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

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

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


            <div class="form-group">
                @Html.LabelFor(model => model.QuoteDetail, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10" id="QuoteDetails">
                    @for (int i = 0; i < Model.QuoteDetail.Count; i++)
                    {
                        <div id="row">
                            @Html.EditorFor(model => model.QuoteDetail[i].QuoteId, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].QuoteDetailId, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.HiddenFor(model => model.QuoteDetail[i].ProductId, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].ProductName, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].Amount, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].ListPrice, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].Discount, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.EditorFor(model => model.QuoteDetail[i].Price, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.ValidationMessageFor(model => model.QuoteDetail, "", new { @class = "text-danger" })

                            @Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteDetail[i].QuoteId, quoteDetailId = (Model.QuoteDetail[i].QuoteDetailId) },
                              new AjaxOptions
                              {
                                  HttpMethod = "POST",
                                  Confirm = "Are you Sure You Want to Delete " + Model.QuoteDetail[i].ProductName,
                                  //OnSuccess = "function() { window.location.reload(); }"
                                  OnSuccess = "RemoveRow"
                              },
                              new { @class = "btn btn-danger glyphicon glyphicon-trash" })
                        </div>
                    }

                    @Ajax.ActionLink("Add product", "AddProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetail.Count + 1) }, new AjaxOptions
               {
                   UpdateTargetId = "QuoteDetails",
                   InsertionMode = InsertionMode.InsertAfter
               })

                    <p>&nbsp;</p>
                </div>


                <script type="text/javascript">
                $(document).ready(function () {
                    alert("Step 1");
                    $('[id*="ProductList"]').change(function () {
                        alert("We got this far");
                        $.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
                            alert("Did we get this far?")
                            $("[id*='ProductId']").val(data.ProductId);
                            $("[id*='Price']").val(data.Price);
                        });
                    });
                });
                </script>
                <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>

    }

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

The partial view is as follows:

@model CMSUsersAndRoles.Models.QuoteDetail

@{
    ViewBag.Title = "EditQuoteDetail";
    Layout = null;
}

@{
    var quoteId = (int)ViewData["QuoteId"];
    var quoteDetailId = (int)ViewData["QuoteDetailId"];
}
<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
</head>
<body>

<div id="row">
        <table>

            @using (Html.BeginCollectionItem("quoteDetail"))

            {

                <tr>
                    @Html.HiddenFor(model => model.QuoteId, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.HiddenFor(model => model.QuoteDetailId, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.TextBoxFor(model => model.ProductId, new { htmlAttributes = new { @id = "ProductId", @class = "form-control" } })
      @Html.DropDownList("ProductList", new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { htmlAttributes = new { @id = "ProductList", @class = "form-control" } });
                   @Html.EditorFor(model => model.Amount, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.TextBoxFor(model => model.Price, new { htmlAttributes = new { @id = "Price", @class = "form-control" } })
                    @Html.EditorFor(model => model.Discount, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.ListPrice, new { htmlAttributes = new { @class = "form-control" } })
                    @Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetailId) },
                          new AjaxOptions
                          {
                              HttpMethod = "POST",
                              Confirm = "Are you Sure You Want to Delete " + Model.ProductName,
                              OnSuccess = "RemoveRow"
                          },
                          new { @class = "btn btn-danger glyphicon glyphicon-trash" })
                </tr>
            }

        </table>
    </div>
</body>
</html>

The jQuery at the bottom of the Edit view is what I'm concerned about, namely:

<script type="text/javascript">
            $(document).ready(function () {
                alert("Step 1");
                $('[id*="ProductList"]').change(function () {
                    alert("We got this far");
                    $.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
                        alert("Did we get this far?")
                        $("[id*='ProductId']").val(data.ProductId);
                        $("[id*='Price']").val(data.Price);
                    });
                });
            });
            </script>

I put the alerts in the javascript because I wanted to see how far I was getting in the code. The "Step 1" alert fires, but nothing beyond that. The BeginCollectionItem helper in the partial view adds what appear to be guids to the ID and Class ProductList and ProductId, e.g., quoteDetail_2b71523b-3714-4e0d-bb83-e8fbb3b3a7fb__ProductLis‌​t. For this reason I altered the jQuery to select ids which contain, e.g., "ProductList". Still, the jQuery does not fire on the change event. Any help would be much appreciated.

Per request below, here is part of the rendered HTML:

<select htmlattributes="{ id = ProductList, class = form-control }" id="quoteDetail_f13ff5a1-6705-44f0-957a-0b739c5cfa53__ProductList" name="quoteDetail[f13ff5a1-6705-44f0-957a-0b739c5cfa53].ProductList"><option value="1">Albi Polymer</option>
<option value="2">Fireproofing Paint</option>
</select>

Per suggestion below, I changed the jQuery as follows:

<script type="text/javascript">
            $(document).ready(function () {
                alert("Step 1");
                $('#QuoteDetails').on('change', 'ProductList', function() {
                    alert("We got this far");
                    $.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
                        alert("Did we get this far?")
                        $("[id*='ProductId']").val(data.ProductId);
                        $("[id*='Price']").val(data.Price);
                    });
                });
            });
            </script>

I added the class "ProductList" to the dropdownlist as follows:

 @Html.DropDownList("ProductList", new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { htmlAttributes = new { @id = "ProductList", @class = "ProductList" } });
Pismotality
  • 2,149
  • 7
  • 24
  • 30
  • Are there elements having duplicate `id` within `html`? Can you include rendered `html` at Question? – guest271314 Mar 02 '17 at 17:53
  • I added it above. – Pismotality Mar 02 '17 at 20:02
  • Still not sure what issue is? Is the `html` the actual rendered `html`? – guest271314 Mar 02 '17 at 20:07
  • I captured the HTML above using Chrome Developer tools. Is there another tool I should use? – Pismotality Mar 02 '17 at 22:35
  • Can you reproduce issue at jsfiddle? – guest271314 Mar 02 '17 at 22:37
  • There are multiple problems with your code (too long for an answer) and I suggest you start by looking at the code in [this answer](http://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) –  Mar 02 '17 at 22:45
  • Your immediate issue is that you need to use a class name for the dropdownlist, and the script will be `$(document).on('change', 'yourClassName', function() { $.post(....` but replace `document` with the closest ancestor which exists when the page is first rendered (which looks like it should be `$('#QuoteDetails')` –  Mar 02 '17 at 22:48
  • I changed the jQuery to use the .on method, as suggested above, and also added the class 'ProductList' to the dropdownlist. Still, the jQuery does not fire. – Pismotality Mar 03 '17 at 03:12
  • @DwightMendoza. Then you did not do it correctly - but there are so many other issues with your code that is a wonder anything works –  Mar 03 '17 at 06:48
  • The changed jQuery is posted above, @StephenMuecke. Does this not follow your suggestion? – Pismotality Mar 03 '17 at 09:31
  • Its `'.ProductList'` (the dot signifies a class selector) - an I assume you have add `class="ProductList"` in both the view and the partial –  Mar 03 '17 at 10:30
  • I added the class "ProductList" to the dropdownlist as shown above (partial view). I don't see where to add it in the view as there is no dropdownlist there. Even with the change to '.ProductList' the jQuery still doesn't fire. – Pismotality Mar 03 '17 at 22:40

1 Answers1

0

After much agony, I found an answer. The jQuery (on the parent view) is as follows:

<script type="text/javascript">
            $(document).ready(function () {
                $(document).on("change", '[id*="ProductList"]', function () {
                    $.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
                        $("[id*='ProductId']").val(data.ProductId);
                        $("[id*='Price']").val(data.Price);
                    });
                });
              });
            </script>

And the controller action is as follows:

 [HttpPost]
        public ActionResult GetProduct(int pId)
        {
            var data = db.Products.Find(pId);
            return Json(data);
        }
Pismotality
  • 2,149
  • 7
  • 24
  • 30