1

I have a DropDownListFor that, when a different elementID/value is selected, should populate related elements with Model data returned from a GET ActionResult, or clear the entries from the related TextBoxFor(s) if no value is selected. The string ID from the DropDownListFor passed to the ActionResult is always null, generating an error. I've tried using variables for the AJAX data call, stringify-ing the data, and reading many StackOverflow questions, but the null exception still occurs. I'm new to jQuery, so can anyone see why the data doesn't contain a value?

The View:

<div class="row">
    <div class="col-lg-4">
        @Html.LabelFor(m => m.AccountOwnerID, "Account Owner Name", new { @class = "req" })
        @Html.DropDownListFor(m => m.AccountOwnerID, Model.AccountOwners, "--New Owner--")
    </div>
    <div class="col-lg-4" id="AccountOwnerName">
        @Html.LabelFor(m => m.AccountOwner.AccountOwnerName, new { @class = "req"})
        @Html.TextBoxFor(m => m.AccountOwner.AccountOwnerName)
    </div>
</div>
<div class="row">
    <div class="col-lg-6" id="AccountOwnerPhoneNumber">
        @Html.LabelFor(m => m.AccountOwner.AccountOwnerPhoneNumber, "Owner Phone Number")
        @Html.TextBoxFor(m => m.AccountOwner.AccountOwnerPhoneNumber, new { @class = "form-control", data_parsley_required = "true" })
    </div>
    <div class="col-lg-6" id="AccountOwnerEmail">
        @Html.LabelFor(m => m.AccountOwner.AccountOwnerEmail, "Owner E-Mail Address")
        @Html.TextBoxFor(m => m.AccountOwner.AccountOwnerEmail, new { @class = "form-control", data_parsley_required = "true" })
    </div>
</div>

@section Scripts{

<script src="~/Scripts/jquery-2.1.3.js"></script>
<script src="/Scripts/jquery-ui-1.11.4.js"></script>

<script>
    $("#AccountOwnerID").change(function () {
        var $ownerName = $("#AccountOwner\\.AccountOwnerName"),
            $ownerEmail = $("#AccountOwner\\.AccountOwnerEmail"),
            $ownerPhone = $("#AccountOwner\\.AccountOwnerPhoneNumber"),
            $ownerValue = $("#AccountOwnerID").val(),
            $aOID = { ownerID : $ownerValue };
        if (this.value >= 1) {
            $.ajax({
                type: "GET",
                url: '/Sales/GetAccountOwnerInfo',
                data: JSON.stringify($aOID),
                success: function (data) {
                    //Fill data
                    $ownerName = data.AccountOwnerName;
                    $ownerEmail = data.AccountOwnerEmail;
                    $ownerPhone = data.AccountOwnerPhoneNumber;
                }
            });
        }
        else {
            //clear fields
            $ownerName.val('');
            $ownerEmail.val('');
            $ownerPhone.val('');
        }
    }).change();
</script>

The Controller, which returns a model with elements to which the AJAX success assigns to the TextBoxFor(s):

public ActionResult GetAccountOwnerInfo(string ownerID)
    {
        Int64 accountOwnerID = 0;
        Int64.TryParse(ownerID, out accountOwnerID);

        ProjectEntities projectDB = new ProjectEntities();
        var owner = projectDB.uspGetAccountOwnerInformation(accountOwnerID).FirstOrDefault();
        AccountOwnersModel accountOwner = new AccountOwnersModel()
        {
            AccountOwnerID = owner.AccountOwnerID,
            AccountOwnerName = owner.AccountOwnerName,
            AccountOwnerEmail = owner.AccountOwnerEmail,
            AccountOwnerPhoneNumber = owner.AccountOwnerPhone,
        };

        return Json(accountOwner, JsonRequestBehavior.AllowGet);
    }
jle
  • 269
  • 8
  • 25
  • I'm not experienced enough to answer with certainty, but I don't think so due to the fact that the null reference exception to the string being passed to the ActionResult, and then to the server, occurs inside the ActionResult when the DropDownListFor is toggled. – jle Aug 19 '15 at 21:32
  • @ Script47 not all URLs must map to a file on the disk –  Aug 19 '15 at 21:37
  • @jle: have you tried to place a breakpoint in the action? is `ownerID` getting the correct value? also what `data` are talking about, the one in the success callback? –  Aug 19 '15 at 21:39
  • @Script47 I changed the URL line to "url: '/Sales/GetAccountOwnerInfo/'+$ownerValue+'.html'" and the null reference exception was still hit inside the ActionResult, but I appreciate the suggestion. – jle Aug 19 '15 at 21:41
  • @StefanBaiu did not know that, thanks for the clarification. – Script47 Aug 19 '15 at 21:42
  • `url: '/Sales/GetAccountOwnerInfo?ownerID=' + $ownerValue` –  Aug 19 '15 at 21:43
  • @Stefan Baiu - I didn't place a breakpoint, but the error details direct me to the ActionResult, where I have to press Continue in order to return to the browser. While that error detail is up, I hovered over the ownerID variable, and it had a null indicator. – jle Aug 19 '15 at 21:43
  • also you can remove this line `data: JSON.stringify($aOID)` –  Aug 19 '15 at 21:52
  • @Stefan Baiu - Thanks for the URL suggestion! The correct data now seems to be grabbed from the database, but the elements' values aren't being assigned to the TextBoxFor(s), nor are the TextBoxFor(s) clearing when a value isn't selected. Also, no errors seem to appear in either Visual Studio or the Chrome console. – jle Aug 19 '15 at 21:52
  • Replace `data: JSON.stringify($aOID),` with `data: $aOID,` and since its an `int`, the paramater should be `int ownerID`not `string ownerID` –  Aug 19 '15 at 21:53
  • And there are no elements with `$("#AccountOwner\\.AccountOwnerName")` etc - look at the html your generating - its `$("#AccountOwner_AccountOwnerName")` –  Aug 19 '15 at 21:54
  • to update your inputs use `$ownerName.val(data.AccountOwnerName);` –  Aug 19 '15 at 21:56
  • Combined with the suggestions to look at the generated html, change the URL, and use .val(), this now works. I was operating under the impression that AJAX name/value pairs were all passed as strings. Thanks to everyone for clearing that up! If one of you will post the URL, string-to-int, and .val(data.element) suggestions as an answer, I'd be glad to mark it as correct. – jle Aug 19 '15 at 22:12
  • @jle glad you solved your problem, I enjoyed our little debugging session via SO comments haha, no need for points, I can't sleep right now, so I have to choose between this and thinking about my past, trust me THIS was a lot more enjoyable –  Aug 19 '15 at 22:31

1 Answers1

1

Firstly $ownerName = $("#AccountOwner\\.AccountOwnerName"), will mean $ownerName is undefined since you don't have any elements with id="AccountOwner\\.AccountOwnerName". The html helper methods generate id attributes by using the replacing any . (and [ and ] characters) in the property name with _ to avoid issues with jquery. It would need to be $ownerName = $("#AccountOwner_AccountOwnerName")

Secondly, you do not need to stringify the data (and if you do, then you need to specify the contentType: 'json', ajax option). It should be data: { ownerID: $(this.val() },. Note the use of $(this) to avoid having to search the DOM again.

Thirdly, $ownerName is a jquery element so to set its value, it needs to $ownerName.val(data.AccountOwnerName);

Side note: your usage of if (this.value >= 1) suggests something wrong. Your helper is generating its first option with <option value="">--New Owner--</option> so you should be checking for null.

Your script should be

// cache the elements since you repeatedly accessing them
var ownerName = $("#AccountOwner_AccountOwnerName"),
    ownerEmail = $("#AccountOwner_AccountOwnerEmail"),
    ownerPhone = $("#AccountOwner_AccountOwnerPhoneNumber");
$("#AccountOwnerID").change(function () {
  if ($(this).val()) {
    $.ajax({
      type: "GET",
      url: '@Url.Action("GetAccountOwnerInfo", "Sales")', // don't hard code your url's
      data: { ownerID: $(this).val() },
      success: function (data) {
        // Fill data
        ownerName.val(dataAccountOwnerName);
        ownerEmail.val(data.AccountOwnerEmail);
        ownerPhone.val(data.AccountOwnerPhoneNumber);
      }
    });
  } else {
    // clear fields
    ownerName.val('');
    ownerEmail.val('');
    ownerPhone.val('');
  }
})

Side note: Since your passing a value which is int to the method, your method parameter should be int ownerID. Also you don't need the value of AccountOwnerID in the response so you could just create an anonymous object in the controller

public ActionResult GetAccountOwnerInfo(int ownerID)
{
  ProjectEntities projectDB = new ProjectEntities();
  var owner = projectDB.uspGetAccountOwnerInformation(ownerID).FirstOrDefault();
  var data = new
  {
    AccountOwnerName = owner.AccountOwnerName,
    AccountOwnerName = owner.AccountOwnerName,
    AccountOwnerPhoneNumber = owner.AccountOwnerPhone
  };
  return Json(data, JsonRequestBehavior.AllowGet);
}