Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with ngChange for <select> after 1.3.0 rc0

Tags:

angularjs

I have been using a beta version of 1.3 and now after moving to 1.3.1 I notice a problem which by checking all earlier versions I see it appears to have started in 1.3.0 rc1.

I have code like this:

<select ng-model="home.modal.topicId"
        ng-change="ctrl.modalTopicChanged()"
        ng-options="item.id as item.name for item in home.modal.option.topics.data"
        ng-required="true">
        <option style="display: none;" value="">Select Topic</option>
</select>

Prior to rc1 the ng-change was not being fired when the form was first displayed. Now it is being fired with a home.modal.topicId of undefined. This is a breaking change for me but it's not mentioned in the breaking change section and I wonder if it's a bug that has yet to be noticed.

Here is the stack trace produced:

TypeError: Cannot read property 'dataMap' of undefined
    at AdminProblemController.modalTopicChanged (http://127.0.0.1:17315/Content/app/admin/controllers/ProblemController.js:109:114)
    at $parseFunctionCall (http://127.0.0.1:17315/Scripts/angular.js:11387:18)
    at Scope.$get.Scope.$eval (http://127.0.0.1:17315/Scripts/angular.js:13276:28)
    at http://127.0.0.1:17315/Scripts/angular.js:19888:13
    at http://127.0.0.1:17315/Scripts/angular.js:19499:9
    at forEach (http://127.0.0.1:17315/Scripts/angular.js:331:20)
    at $$writeModelToScope (http://127.0.0.1:17315/Scripts/angular.js:19497:5)
    at writeToModelIfNeeded (http://127.0.0.1:17315/Scripts/angular.js:19490:14)
    at http://127.0.0.1:17315/Scripts/angular.js:19484:9
    at validationDone (http://127.0.0.1:17315/Scripts/angular.js:19420:9) 

What I notice here is a new function: writeToModelIfNeeded

When I look at the change log differences I cannot find any mention of this function being introduced when I check all the changes and the line numbers.

I would like to get some advice on this. Firstly is it possible to find the change that caused the addition of the writeToModelIfNeeded then secondly is this the correct functionality for the select box. I thought the whole idea was that the ng-change would only fire if the model value was defined.

For reference here's the area of new code that seems to have been added with 1.3.0 rc.1

**
   * @ngdoc method
   * @name ngModel.NgModelController#$commitViewValue
   *
   * @description
   * Commit a pending update to the `$modelValue`.
   *
   * Updates may be pending by a debounced event or because the input is waiting for a some future
   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
   * usually handles calling this in response to input events.
   */
  this.$commitViewValue = function() {
    var viewValue = ctrl.$viewValue;

    $timeout.cancel(pendingDebounce);

    // If the view value has not changed then we should just exit, except in the case where there is
    // a native validator on the element. In this case the validation state may have changed even though
    // the viewValue has stayed empty.
    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
      return;
    }
    ctrl.$$lastCommittedViewValue = viewValue;

    // change to dirty
    if (ctrl.$pristine) {
      ctrl.$dirty = true;
      ctrl.$pristine = false;
      $animate.removeClass($element, PRISTINE_CLASS);
      $animate.addClass($element, DIRTY_CLASS);
      parentForm.$setDirty();
    }
    this.$$parseAndValidate();
  };

  this.$$parseAndValidate = function() {
    var parserValid = true,
        viewValue = ctrl.$$lastCommittedViewValue,
        modelValue = viewValue;
    for(var i = 0; i < ctrl.$parsers.length; i++) {
      modelValue = ctrl.$parsers[i](modelValue);
      if (isUndefined(modelValue)) {
        parserValid = false;
        break;
      }
    }
    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
      // ctrl.$modelValue has not been touched yet...
      ctrl.$modelValue = ngModelGet();
    }
    var prevModelValue = ctrl.$modelValue;
    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
    if (allowInvalid) {
      ctrl.$modelValue = modelValue;
      writeToModelIfNeeded();
    }
    ctrl.$$runValidators(parserValid, modelValue, viewValue, function() {
      if (!allowInvalid) {
        ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
        writeToModelIfNeeded();
      }
    });

    function writeToModelIfNeeded() {
      if (ctrl.$modelValue !== prevModelValue) {
        ctrl.$$writeModelToScope();
      }
    }
  };

  this.$$writeModelToScope = function() {
    ngModelSet(ctrl.$modelValue);
    forEach(ctrl.$viewChangeListeners, function(listener) {
      try {
        listener();
      } catch(e) {
        $exceptionHandler(e);
      }
    });
  };
like image 642
Samantha J T Star Avatar asked Nov 04 '14 09:11

Samantha J T Star


1 Answers

I was able to reproduce your issue by doing this. Without seeing your controller though not sure if same thing:

 this.modal = {
      topicId:null,
      option:{
        topics:{
          data:[{id:1,name:'item1'},{id:2,name:'item2'}]
        }
      }
    };

What is happening here is that angular says null is an invalid value so by default sets it to undefined. You can fix this by setting it to 'undefined' or adding this to your html:

ng-model-options="{allowInvalid:true}"

also tested Josep plunker and changing that value to null also caused ngChange to fire

like image 68
Jason Avatar answered Dec 09 '22 00:12

Jason