What methods can be used to differentiate between value equality and reference equality when watching something?

In the world of AngularJS, the $watch function comes with an interesting twist - it has an optional third parameter.

By default, this parameter is set to false, which means the watch will only trigger if the watched reference changes. But if you set it to true, the watch will be triggered when the watched object itself changes, as determined by angular.equals.

However, I find myself in need of a way to make the watch fire its function when either the watched reference or its content changes. Is there a solution to achieve this dual functionality?


To illustrate the challenges I'm facing:

Imagine I have an array of editable objects:

var allItems = [{id: 1, a: []}, {id: 2, a: [2, 3, 5]}, {id: 3, a: [4, 6]}, {id: 4, a: []}];

A specific scope in my editor holds a reference to the value of the a property. For example, after selecting item 1:

$scope.currentItem = [];

Additionally, a watch is placed on this field to compare values:

$scope.$watch('currentItem', watchFunc, true);

Now, consider two scenarios:

  • The user modifies the selected item's [] to [4, 6] using the editor UI.
  • The user switches to item 3.

From within watchFunc, these changes may seem identical at first glance. To distinguish between them, I must somehow keep track of the previous ID and compare it to the current one. This allows me to differentiate whether a new item was selected or just updated.

Yet, complications arise in situations like:

  1. Selecting item 1.
  2. Switching to item 4.
  3. Performing edits on item 4.

Here lies the issue - the second action won't trigger the watch based on angular.equals. Consequently, the selected item ID won't update during the selection change. Only later, in step 3, will the different item ID be recognized, leading to the oversight of the actual editing action.


Of course, this scenario simplifies the complexity; in reality, my data structure involves nested arrays of objects with diverse properties.

Answer №1

Check out this working example on jsfiddle: http://jsfiddle.net/qybc07zs/

I hope this meets your requirements.

Here's the testing scenario:

1. Choose option 1
2. Enter 4 => Add
3. Enter 6 => Add
4. Choose option 3
5. $watch event triggers

AngularJS:

var myApp = angular.module('myApp',[]);

myApp.controller('myCtrl', myCtrl);

function myCtrl($scope) {
    $scope.allItems = [{id: 1, a: []}, {id: 2, a: [2, 3, 5]}, {id: 3, a: [4, 6]}, {id: 4, a: []}];
    $scope.currentItemObject = {};
    $scope.currentItem = [];

    $scope.updateCurrentItem = function(item){
        $scope.currentItem = angular.copy(item.a);
    };

    $scope.addItem = function(){
      if($scope.newItem !== undefined){
        $scope.currentItem.push($scope.newItem);
        $scope.newItem = undefined;
      }
    };

    $scope.$watch(function(){ 
      return {id: $scope.currentItemObject.id, a: $scope.currentItem};
    },function(newValue, oldValue){
      if(newValue !== oldValue){
        console.log('$watch', $scope.currentItem);
        $scope.lastUpdate = new Date().toLocaleTimeString();
      }
    },true);
}

HTML:

<div ng-controller="myCtrl">
  <label>Items: 
    <select ng-options="item as item.id for item in allItems" 
      ng-model="currentItemObject"
      ng-change="updateCurrentItem(currentItemObject)">
      <option style="display:none" value="">-- select item --</option>
    </select>
  </label>
  <div>
    <label>New item
      <input type="number" ng-model="newItem">
    </label>
    <button type="button" ng-click="addItem()">
      Add
    </button>
  </div>

  <div>
    Current item: {{currentItem}}  
  </div>
  <br/>
  <pre>
    Last update: {{lastUpdate}}
  </pre>
</div>

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

The post method is functioning properly in browsers such as Firefox, Internet Explorer, and Chrome; however, it is not working in the Edge browser

I am encountering an issue with a post method in the Edge browser. Even though I am able to receive responses for the same request in other browsers like Internet Explorer, Chrome, and Firefox, Edge seems to be not responding at all. Despite conducting a s ...

Issue with Angular Bootstrap Modal autofocus functionality malfunctioning

I successfully integrated a bootstrap modal using angular bootstrap into my project. If you want to check out the code I used, here is the plunker link. Inside the modal, there is only one input field (textbox) that the user needs to fill, along with sav ...

Changing an element in an array by using a specific input

With the usage of either JavaScript or Jquery, I possess an array that has been arranged in accordance with Start Date (coordinates): [{ elem: '<div id="task7">', startDate: 864, endDate: 999, order: 0 }, { elem: '<div id ...

What is the method for utilizing tsx within the Vue Composition setup function?

As I write tsx in @vue/composition-api setup() function, like so: <script lang="tsx"> import { defineComponent,} from '@vue/composition-api'; export defineComponent({ setup() { const foo = { bar: 1 baz: render() ...

Identifying child elements in jQuery with identical IDs

Consider this HTML setup: <div id="contentRead"> ... <div id="List"></div> </div> ... <div id="contentWrite"> ... <div id="List"></div> </div> ...

The parameter "file" needs to be a string data type, but npm run deploy to gh-pages received an undefined data type instead

I encountered an error while attempting to deploy a react application to gh-pages. Despite having successfully done it in the past, this is the first time I am facing this specific issue. The error message reads: The "file" argument must be of type string ...

An npm list is always full of modules

As I prepare to install a package using npm, I noticed that my folder for the new project already has numerous items listed when I run npm list. Is it normal for the folder not to be empty at this stage? Have I made an error somewhere? ...

Updating state in React with a nested promise within the useEffect hook

Trying to update the component state using React.useState with the help of useEffect. The reason behind using useEffect is that the API call response (EmployeeState > init()) determines what gets displayed on the UI. Component: import { EmployeeState } ...

Is there a way to utilize redux to trigger the opening of my modal when a button is clicked?

I'm facing a challenge with opening my modal using redux when clicking <CheckoutButton/>. Despite my efforts, the modal keeps appearing every time I reload the browser. I've reviewed my code but can't pinpoint the issue. What am I doin ...

Reduce the file size of CSS and JS files for Magento

We are facing an issue with minifying CSS and Javascript for our Magento website. Currently, the size of our website is 1.1 MB and we aim to reduce it to 1 MB or even lower if possible. I tried using the "CSS Settings" and "Javascript Settings" functions ...

Click event not triggering on dynamically inserted DIV using jQuery

After the page is rendered in the DOM using jquery, the following code is added: owldata = '<div class="item"><div class="ifl-removepic" ng-click="deleteDocument("'+e.target.result+'");"></div><img src="' + e.targe ...

Using base64 encoding and setting the binary type for Websockets

I'm feeling a bit lost. I've been working on understanding the mechanics of Websockets by setting up a server in node.js and a client-side application. One thing that's really puzzling me is how Websockets handle data transmission. Should da ...

Looking for assistance with REGEXP to handle a 404 path rendering issue

I am attempting to utilize REGEXP in order to display a 404 page within a react app based on the path, but I am struggling to make it work. This is the specific REGEXP pattern I am trying to achieve: The entire path should not be '/' and should ...

What is the best way to apply dynamic wrapping of a substring with a component in Vue.js?

My Objective My goal is to take a user input string and render it with specific substrings wrapped in a component. Specifically, I am looking to identify dates within the string using a regex pattern and then wrap these dates in a Vuetify chip. Progress ...

Dark opaque background image with Material-UI styling

I'm enclosing all the widgets within a CardMedia component, and adding an image. return ( <CardMedia image={bg} className={classes.bg}> <main className={classes.content}> <div className={classes.toolbar} /> <Grid contai ...

Updating an array element in Mongoose, the MongoDB ORM

I am currently in the process of making changes to a MongoDb collection that contains an array of documents called items. To achieve this, I am utilizing the express and mongoose frameworks. This is how my schema is structured: const mongoose = require(" ...

Customizing ExtJS 4.1: Mastering field overrides

Seeking guidance on applying a plugin to all fields(numberfield, textfield, datefield, etc.) within the ExtJS 4.1 library. Does anyone have suggestions on how to achieve this? I understand that all fields are derived from BaseField. I attempted the follow ...

How can the '!!user' syntax be utilized? What outcome does this code snippet produce?

I am looking to implement an angular route guard in my application. I came across this code snippet but I am confused about the line where user is mapped to !!user. Can someone explain the purpose of map(user => !!user) in this context? canActivate( ...

Uploading PDF files using PHP without the need for traditional input boxes

Currently, I am utilizing a JavaScript code to add annotations to a PDF document within the browser. Once I have completed adding annotations, I save the modified document on my device and then proceed to upload it using another form. However, my goal is ...

Is it possible to assign a class to the initial word within a designated class?

How can I customize the first and second word of a specific class in my code? This is an example of my current code: <h2 class="content-heading">Latest News</h2> I would like to achieve something similar to this: <h2 class="content-headi ...