Dynamically Setting ngModelOptions in Angular

All we need is an easy explanation of the problem, so here it is.

I’ve got the following snippet:

<input type="date" ng-model="arrival" ng-model-options="{timezone: 'PST'}" />
<input type="time" ng-model="arrival" ng-model-options="{timezone: 'PST'}" />
{{arrival}}

That works properly (the date is displayed in UTC time converted from PST). I’m now trying to make the ‘PST’ option dynamic:

<select ng-model="timezone>
  <option value="PST">PST</option>
  <option value="EST">EST</option>
</select>
<input type="date" ng-model="arrival" ng-model-options="{timezone: timezone}" />
<input type="time" ng-model="arrival" ng-model-options="{timezone: timezone}" />
{{arrival}}

However, changing the timezone never update the arrival (it appears that binding doesn’t work with nd-model-options). Any way I can force the fields to refresh when the timezone is changed?

Edit

Fiddle: https://jsfiddle.net/10nfqow9/

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Create another directive (attribute type) with a high priority (higher than ng-model /ng-model-option’s) that watches the options object for changes and triggers a recompile. My apologies for lack of specifics, I’m on a phone 🙂

EDIT:
Looks like there’s a directive called kcd-recompile that does exactly what I described. Here’s a working plnkr, with some additional goodies for factoring in DST for american timezones.

HTML:

<div kcd-recompile="data.timezone">
  <div>
    <select ng-model="data.timezone" ng-options="x.offset as x.name for x in timezones">
    </select>
  </div>
  <div>
    <input type="date" ng-model="data.arrival" ng-model-options="{timezone: data.timezone}" />
  </div>
  <div>
    <input type="time" ng-model="data.arrival" ng-model-options="{timezone: data.timezone}" />  
  </div>
</div>

And JS:

Date.prototype.stdTimezoneOffset = function() {
    var jan = new Date(this.getFullYear(), 0, 1);
    var jul = new Date(this.getFullYear(), 6, 1);
    return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}

Date.prototype.dst = function() {
    return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

angular.module('DemoApp', ['kcd.directives']);
angular.module('DemoApp')
.controller('DemoCtrl', ['$scope', function($scope) {
    var now = new Date(),
        isDst = now.dst();

    $scope.data ={
      arrival: now,
      timezone: null
    };
    $scope.timezones = [
      {
        name: 'PST', 
        offset: isDst ? '-0700' : '-0800'
      },
      {
        name: 'EST', 
        offset: isDst ? '-0400' : '-0500'
      }
    ];
  }]
);

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply