I am having an issue where ng-model-options
isn't reflecting the changes I need it to.
For example, in the snippet below if you enter 4:00 pm
into both time inputs you'll see the UTC output is different - the first is 6
and the second is 8
. This is expected. However, the issue occurs when I select +0800
using the dropdown. Since this updates the timezone
variable, both time
inputs should now display 8
when I enter 4:00 pm
since the first input should now use the timezone
variable (as specified in its ng-model-options
). However this isn't happening. Even after I clear the input and re-enter the time manually it still shows the incorrect time. How can I make the timezone
option in ng-model-options
use a dynamically changing variable such as timezone
?
See issue below:
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>
As per the 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 withNgModelController#$overrideModelOptions()
I have changed some of the lines to make it work for you :)
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>
You will find more details about $overrideModelOptions
here - https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$overrideModelOptions
Edited:
You can achieve it by creating a separate directive.
**For versions >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>
Reference Post - Dynamically Setting ngModelOptions in Angular