Managing circular watches in AngularJS

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:

  1. Watcher #2 triggers - updates selectedUnixTimestamp
  2. Watcher #1 triggers - updates selectedDate
  3. 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.

Answer №1

Working with circular watches can be tricky. It's best to avoid them whenever possible.

There may be alternative methods that could provide a better solution for your specific scenario.

Utilize a single watch function:

You have the option to use a function as the first parameter for the watch. This function will continue to be called until the returned value stabilizes (matches the previous value). You can create a $watch statement like this:

$scope.$watch(function() {
    return {
        timestamp: scope.selectedUnixTimestamp, 
        date: scope.selectedDate
    }
}, function(newVal, oldVal) { 
    // The newVal and oldVal variables here are structured based on the object returned in the watch function, containing properties like timestamp and date.
    // If necessary, you can compare newVal.date to oldVal.date (same goes for timestamp) to determine which has actually changed.
}
true);  // A deep watch is required (using true as a parameter) to track the object's properties

Answer №2

One of the core beliefs of the Angular framework is that a single, reliable value should exist in the model for synchronization with services like REST.

To adhere to this principle, it is advised to avoid creating circular watchers. Instead, when faced with multiple ways to modify a model's value, directives should be written that utilize the ngModelController instance along with appropriate formatter and parser functions.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Does AngularJS have a hook for pre or post $apply functionality?

I am looking to execute a callback function every time the $apply method is invoked on a scope. Are there any pre/post hooks for $apply or events that I can listen for in order to accomplish this? ...

Implementing a Unique Shuffle Algorithm in JavaScript for AMP Compatibility

Is there a way to make this JavaScript Random Shuffle code in a table AMP compliant? Specifically, is there a way to convert the stringId to be the table's Id? <script>function shuffleTable() { var table = document.getElementById("tableId"); va ...

Exploring Enum properties within an angular js select drop down

I am working with an enum in Java that looks like this: public enum myEnum{ enum1("enumDisplayVal1"), enum2("enumDisplayVal2") myEnum(String displayValue) { this.displayValue = displayValue;} private String displayValue; public String getDisp ...

Nested views within Angular providing a multitude of perspectives

Currently, I am tackling a project at my workplace with my colleagues, but we are collectively stumped on how to proceed. Consequently, the details may come across as nebulous. Allow me to present a preview of the expected layout: Template The plan is fo ...

Animating with CSS3 triggered by JavaScript

Can you help me with an issue I'm having? When attempting to rotate the blue square using a function, it only works once. After that, the page needs to be reloaded in order for the rotation function to work again. Additionally, after rotating 120 degr ...

Ensure there is a sufficient gap between the top and bottom icons within the Material-UI Drawer

I'm having difficulty articulating this, but I'd like to add two different sets of icons to the Drawer component. Set 1 should be displayed at the top in a standard column format, similar to the examples provided by them. Set 2 should go at the b ...

The issues with VUE3 compounding filters are causing unexpected results

Currently, I am attempting to filter search results using multiple filter options. After trying various methods, I have found that when applying only 2 filters, the search works as expected. However, when adding 3 or more filters, it includes additional re ...

Issue with the positioning of data labels in the top row on Highcharts Gantt chart not following the y offset properly

Currently, I am working on creating a milestone comparison chart where I want the data labels to be positioned above the milestone markers. These data labels consist of two rows of text. To achieve this, I have customized the chart.height property to incr ...

What is the best way to showcase a JSON array in a tabular layout?

I have a JSON array structured like this: [ { id: "001", name: "apple", category: "fruit", color: "red" }, { id: "002", name: "melon", category: "fruit", color: "green" }, ...

Javascript Error: Attempting to setAttributeNode on a checkbox that is invalid

I encountered an error and I'm unsure how to fix it, could you please assist me? Here's the code snippet: var ls_contextid1 = JSON.parse(localStorage.getItem('completedArray')) || []; for (var i = 0; i < ls_contextid1.length; ...

Ensuring Consistent Visual Harmony Across Linked Elements

As part of my project developing an iPad app with PhoneGap and jQuery Mobile, I am looking to incorporate a preview pane within a carousel. This preview pane should display smaller versions of the other panes scaled inside it. The panes are dynamic and upd ...

Discovering the maximum value in AngularJS using an expression (complete with a bug demonstration)

How can I set a maximum value for an input that can be either a numeric amount or a percentage? Is it possible to use ng-max to set the max value to 100 if the input is a percentage, and leave it blank if it's a numeric amount? I attempted the follo ...

Screen white on the right side

I am encountering an issue where my screen shows white on the side when I scroll to the right. I am unsure how to remove it and cannot locate the problem in the code. Here is a link to the code: https://jsfiddle.net/y3j01hbt/ In the provided code, there i ...

Finding if a certain id exists within a variable using JavaScript or jQuery

A PHP function is utilized to retrieve data from AJAX, resulting in the following: > Object {0: "<div class="message_text" id="297"><div > class="use…-$('.messages').prop('clientHeight'))</div></div>", 1: & ...

Mocking a named class-export in TypeScript using Jest

I have a node module that exports several classes, including one called Client, which I utilize to create clients with various APIs as methods. I'm currently attempting to test my module, which has a dependency on this node module, using Jest. Howeve ...

Exploring the use of Onsen and AngularJS for iOS interfaces, with a focus

I'm currently developing my mobile app using Onsen and AngularJS. The app consists of four tabs, with tab two containing a form and tab four containing another form. When running the app on an iOS platform, I encounter an issue where filling out the t ...

The server is receiving incorrect exchange rates from Yahoo Finance

My current project involves the use of Node.js, and I have a question regarding the differences between the data sent by Yahoo Finance to the server machine versus a "regular" machine. var http = require('http'); var link = "http://download ...

Encountering a problem with JavaScript and HTML

I have made changes to the script below and encountered an issue where the first and second drop-down menus cannot be closed after selecting an option. I would like them to function similarly to the third drop-down menu. Can someone please assist me with ...

Setting a value to an anchor tag in AngularJS

A user is presented with a list of options (e.g. 1. Apple; 2. Banana; 3. Mango). They are provided with a textbox where they can input their desired option and then click "send" to proceed. Current Setup: HTML: <p ng-repeat="opt in objHistory.OptionI ...

Ways to refresh a div element without causing it to blink

I'm new to web development and I have an HTML webpage that retrieves data from a database and displays information based on the retrieved data. I would like to update the data every second without causing the webpage to blink. Can you please modify th ...