如何让 ng-model-options 使用动态变量?

How do I make ng-model-options use dynamic variables?

我遇到一个问题,ng-model-options 没有反映我需要的更改。 例如,在下面的代码片段中,如果您在两个时间输入中都输入 4:00 pm,您会看到 UTC 输出不同 - 第一个是 6,第二个是 8。这是意料之中的。但是,当我 select +0800 使用下拉菜单时会出现问题。由于这会更新 timezone 变量,所以当我输入 4:00 pm 时,两个 time 输入现在都应该显示 8 因为第一个输入现在应该使用 timezone 变量(因为在其 ng-model-options 中指定)。然而,这并没有发生。即使在我清除输入并手动重新输入时间后,它仍然显示不正确的时间。如何使 ng-model-options 中的 timezone 选项使用动态变化的变量,例如 timezone

请参阅以下问题:

angular.module('myApp', [])
  .controller('myController', function($scope) {
    $scope.timezones = ['+1000', '+0800'];
    $scope.timezone = $scope.timezones[0];

    $scope.time = '';
    $scope.eightTime = '';
  });

angular.element(document).ready(() => {
  angular.bootstrap(document, ['myApp']);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
<div ng-controller="myController">
  <select ng-model="timezone">
    <option ng-repeat="timezone in timezones" ng-value="timezone">{{timezone}}</option>
  </select>
  <p>Selected Timezone: {{timezone}}</p>

  <input type="time" ng-model="time" ng-model-options='{timezone: timezone}' />
  <p>Using dropdown T.Z of '{{timezone}}': {{time.getUTCHours()}}</p>

  <input type="time" ng-model="eightTime" ng-model-options="{timezone: '+0800'}">
  <p>Hardcoded '+0800': {{eightTime.getUTCHours()}}</p>
  <!-- ^^^ This should be the output when '+0800' is selected in the dropdown -->
</div>

根据 documentation -

The ngModelOptions expression is only evaluated once when the directive is linked; it is not watched for changes. However, it is possible to override the options on a single ngModel.NgModelController instance with NgModelController#$overrideModelOptions()

我已经更改了一些行以使其适合您:)

angular.module('myApp', [])
  .controller('myController', function($scope) {
    $scope.timezones = ['+1000', '+0800'];
    $scope.timezone = $scope.timezones[0];
    $scope.time = '';
    $scope.eightTime = '';
    
    $scope.$watch('timezone',function(v){
        $scope.time = '';
        $scope.myForm.time.$overrideModelOptions({'timezone': $scope.timezone}); 
        //this will update the options whenever the timezone will be changed.
    })
    
  });

angular.element(document).ready(() => {
  angular.bootstrap(document, ['myApp']);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-controller="myController">
  <form name="myForm">
    <select ng-model="timezone" ng-options="timezone for timezone in timezones">
    </select> <!-- ng-options is the correct way to provide options to the select dropdown -->
    <p>Selected Timezone: {{timezone}}</p>

    <input type="time" name="time" ng-model="time" ng-model-options='{timezone: timezone}' />
    <p>Using dropdown T.Z of '{{timezone}}': {{time.getUTCHours()}}</p>
    <input type="time" ng-model="eightTime" ng-model-options="{timezone: '+0800'}">
    <p>Hardcoded '+0800': {{eightTime.getUTCHours()}}</p>
    <!-- ^^^ This should be the output when '+0800' is selected in the dropdown -->
  </form>
</div>

您可以在此处找到有关 $overrideModelOptions 的更多详细信息 - https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$overrideModelOptions

已编辑:

您可以通过创建一个单独的指令来实现它。

** 对于版本 >1.6.2 **

angular.module('myApp', ['kcd.directives'])
  .controller('myController', function($scope) {
    $scope.timezones = ['+1000', '+0800'];
    $scope.timezone = $scope.timezones[0];
    $scope.time = '';
    $scope.eightTime = '';
    
});
angular.module('kcd.directives', []).directive('kcdRecompile', ['$parse', function($parse) {
  'use strict';
  return {
    transclude: true,
    link: function link(scope, $el, attrs, ctrls, transclude) {
      var previousElements;

      compile();

      function compile() {
        transclude(scope, function(clone, clonedScope) {
          // transclude creates a clone containing all children elements;
          // as we assign the current scope as first parameter, the clonedScope is the same
          previousElements = clone;
          $el.append(clone);
        });
      }

      function recompile() {
        if (previousElements) {
          previousElements.remove();
          previousElements = null;
          $el.empty();
        }

        compile();
      }

      scope.$watch(attrs.kcdRecompile, function(_new, _old) {
        var useBoolean = attrs.hasOwnProperty('useBoolean');
        if ((useBoolean && (!_new || _new === 'false')) || (!useBoolean && (!_new || _new === _old))) {
          return;
        }
        // reset kcdRecompile to false if we're using a boolean
        if (useBoolean) {
          $parse(attrs.kcdRecompile).assign(scope, false);
        }
        recompile();
      }, typeof $parse(attrs.kcdRecompile)(scope) === 'object');
    }
  };
}]);

angular.element(document).ready(() => {
  angular.bootstrap(document, ['myApp']);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.0/angular.min.js"></script>
<div ng-controller="myController">
  <div kcd-recompile="timezone">
    <select ng-model="timezone" ng-options="timezone for timezone in timezones">
    </select> <!-- ng-options is the correct way to provide options to the select dropdown -->
    <p>Selected Timezone: {{timezone}}</p>

    <input type="time" name="time" ng-model="time" ng-model-options="{timezone: timezone}"/>
    <p>Using dropdown T.Z of '{{timezone}}': {{time.getUTCHours()}}</p>
    <input type="time" ng-model="eightTime" ng-model-options="{timezone: '+0800'}">
    <p>Hardcoded '+0800': {{eightTime.getUTCHours()}}</p>
    <!-- ^^^ This should be the output when '+0800' is selected in the dropdown -->
  </div>
</div>

参考 Post -