Search code examples
javascriptangularjstypeerrorng-optionstypechecking

AngularJS ng-options selecting default value after selecting option results in: "TypeError: Cannot read property '[property]' of null"


I have a select tag that uses ng-options and ng-model. When I select an option and then select the default option again, then submit the form, it seems like the selected value disappears. This is an optional select field, so I need to be able to check if nothing is selected when submitting.

I have found several questions similar to this, but none with this exact problem:

Here's a simple fiddle I made illustrating my problem: https://jsfiddle.net/tjadenad/tse4ans1/29/

Instructions for replicating error:

  1. Open console in browser
  2. Select an option from the select list.
  3. Select the default ("Select an Option") option from the select list.
  4. Press the submit button and see the error in the console

Here is the html:

<div ng-app="app">
  <div ng-controller="TestController as ctrl">
        <form data-ng-submit="ctrl.submit()">

                <label for="test-select">
                    Label
                </label>
                <br />
                <select id="test-select" name="optionId"
                        data-ng-model="ctrl.modelObject.selectedOption" 
                        data-ng-options="option as option.description for option in ctrl.options track by option.id">
                    <option value="">Select an Option</option>
                </select>
        <br />
        <button type="submit">Submit</button>
    </form>
  </div>
</div>

Here is the controller:

angular.module('app', []).controller('TestController', function() {
    var vm = this;
    vm.modelObject = { selectedOption: {id: '', description: ''} };
  vm.submit = submit;

  vm.options = [
    {
            id : 1,
      description: 'Option 1'
    },
    {
        id : 2,
      description: 'Option 2'
    }
  ];

  function submit() {
    var objectFromOptions = {
      optionId : vm.modelObject.selectedOption.id
    };
  }

});

I need to create an object after submitting with the option's id as one of the properties. If the id is null or empty, that is fine, but I cannot find a way to check if it is null. I have tried using

angular.isDefined(vm.modelObject.selectedOption.id)

but that throws the same error when trying to access 'id'.

I also tried

vm.modelObject.selectedOption.hasOwnProperty('id')

to set the property of the new object like so:

 function submit() {
    var objectFromOptions = { };

    if (vm.modelObject.hasOwnProperty('selectedOption')) {
        console.log('selectedOption exists');
    } else {
        console.log('selectedOption does not exist');
    }

    if (vm.modelObject.selectedOption.hasOwnProperty('id')) {
      console.log('id exists');
      objectFromOptions.optionId = vm.modelObject.selectedOption.id
    } else {
      console.log('id does not exist');
    }
  }

This results in:

selectedOption exists

followed by the error:

TypeError: Cannot read property 'hasOwnProperty' of null

Please let me know if more information is necessary. Thanks in advance for any help!


Solution

  • vm.modelObject.selectedOption will pass an isDefined check -- because it's defined, but with no value assignment. Also, when the page is fresh, the .id property will pass that isDefined check too. The angular.isObject() function is useful here. Or you could just look at the object/properties and see if they are null or evaluates to falsey.

       if (angular.isObject(vm.modelObject.selectedOption) && angular.isNumber(vm.modelObject.selectedOption.id)) {
          console.log('id exists');
          objectFromOptions.optionId = vm.modelObject.selectedOption.id
        } else {
          console.log('id does not exist');
        }
    

    Here's a fiddle with some console logging: https://jsfiddle.net/f3jym9zg/

    Here's some examples of peeking at variables to determine if they exist. The same holds true for var.property.

    var oops=null;//assign a value of null to the variable oops
    console.log(Boolean(oops));//false
    console.log(oops!=null);//false
    console.log(oops==undefined);//true (== has type conversion baked in)
    console.log(oops===undefined);//false (=== does not type conversion, and the types must be the same to be equal. typeOf(undefined)!==typeOf(null))
    console.log(angular.isDefined(oops));//true (defined, but assigned a value of null)
    console.log(angular.isDefined(oopsy));//false
    
    if (oops){ //evaluates to falsey
    //never hit
    } else {
    //will be hit
    }