Imagine I am dealing with a directive that receives a date in the form of a Unix timestamp through two-way binding, while also providing a calendar widget for selection.
The calendar widget operates with a date object, and changing the input data format or adjusting the calendar to support Unix timestamps is not an option. This scenario serves as an illustration, with the main question focusing on effective handling of circular watchers.
The scope setup would be:
scope.selectedUnixTimestamp; // provided externally
scope.selectedDate;
scope.$watch('selectedUnixTimestamp', function(newV, oldV) {
$scope.selectedDate = new Date(newV*1000);
});
scope.$watch('selectedDate', function(newV, oldV) {
$scope.selectedUnixTimestamp = Math.floor(newV.getTime()/1000 + 0.000001);
});
The query is: How can I prevent unnecessary calls to $watch callbacks? When selecting a new date, the sequence typically involves:
- Watcher #2 triggers - updates selectedUnixTimestamp
- Watcher #1 triggers - updates selectedDate
- Watcher #2 triggers again (due to new object reference) - updates selectedUnixTimestamp
I aim to avoid these additional calls except for the initial one. Is there a way to achieve this?
One approach could involve:
scope.selectedUnixTimestamp;
scope.selectedDate;
var surpressWatch1 = false;
var surpressWatch2 = false;
scope.$watch('selectedUnixTimestamp', function(newV, oldV) {
if(surpressWatch1) { surpressWatch1 = false; return; }
$scope.selectedDate = new Date(newV*1000);
surpressWatch2 = true;
});
scope.$watch('selectedDate', function(newV, oldV) {
if(surpressWatch2) { surpressWatch2 = false; return; }
$scope.selectedUnixTimestamp = Math.floor(newV.getTime()/1000 + 0.000001);
surpressWatch1 = true;
});
However, maintaining such code structure might become cumbersome.
Another method could entail:
scope.selectedUnixTimestamp;
scope.selectedDate;
scope.$watch('selectedUnixTimestamp', function(newV, oldV) {
if(newV*1000 === scope.selectedDate.getTime()) { return; }
$scope.selectedDate = new Date(newV*1000);
});
scope.$watch('selectedDate', function(newV, oldV) {
if(scope.selectedUnixTimestamp*1000 === newV.getTime()) { return; }
$scope.selectedUnixTimestamp = Math.floor(newV.getTime()/1000 + 0.000001);
});
Although effective, this solution may prove costly for more complex data transformations than multiplication by 1000.
An alternative strategy could involve monitoring a primitive value instead of a date object:
scope.$watch('selectedDate.getTime()', function(newV, oldV) {
While suitable for this example, it does not offer a comprehensive resolution to the overarching issue.