Monitoring data updates within an Angular directive

Is there a way to activate a $watch variable in an Angular directive when modifying the data within it (eg. adding or removing data), without assigning a completely new object to that variable?

Currently, I am loading a basic dataset from a JSON file using my Angular controller, which also contains a few functions:

App.controller('AppCtrl', function AppCtrl($scope, JsonService) {
    // Load the initial data model
    if (!$scope.data) {
        JsonService.getData(function(data) {
            $scope.data = data;
            $scope.records = data.children.length;
        });
    } else {
        console.log("I already have the data... " + $scope.data);
    }

    // Adds a resource to the 'data' object
    $scope.add = function() {
        $scope.data.children.push({ "name": "!Insert This!" });
    };

    // Removes a resource from the 'data' object
    $scope.remove = function(resource) {
        console.log("I'm going to remove this!");
        console.log(resource);
    };

    $scope.highlight = function() {

    };
});

I have a <button> that correctly calls the $scope.add function, and the new object is properly added to the $scope.data set. The table I have updates each time the "add" button is clicked.

<table class="table table-striped table-condensed">
  <tbody>
    <tr ng-repeat="child in data.children | filter:search | orderBy:'name'">
      <td><input type="checkbox"></td>
      <td>{{child.name}}</td>
      <td><button class="btn btn-small" ng-click="remove(child)" ng-mouseover="highlight()"><i class="icon-remove-sign"></i> remove</button></td>
    </tr>
  </tbody>
</table>

However, the directive I created to watch $scope.data does not trigger when these actions occur.

In HTML, I define my tag as:

<d3-visualization val="data"></d3-visualization>

This tag is associated with the following directive (trimmed for simplicity):

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");
            });
        }
    }
});

I receive the message "I see a data change!" at the beginning, but not after clicking the "add" button.

How can I trigger the $watch event when simply adding/removing objects from the data object, rather than receiving a whole new dataset to assign to the data object?

Answer №1

To ensure thorough checking of nested objects, it is essential to activate deep object dirty checking. By default, AngularJS only monitors the reference of the top-level variable that you are observing.

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I detect a change in data!");
            }, true);
        }
    }
});

Refer to Scope. By setting the third parameter of the $watch function as true, deep dirty checking can be implemented.

Note that performing deep dirty checking can be resource-intensive. Therefore, if your goal is to monitor only the children array rather than the entire data variable, watch the variable directly.

scope.$watch('val.children', function(newValue, oldValue) {}, true);

The introduction of version 1.2.x brought forth $watchCollection

This feature enables monitoring the properties of an object and triggering updates whenever any property changes (for arrays, this includes monitoring the array items; for object maps, this entails watching the properties)

scope.$watchCollection('val.children', function(newValue, oldValue) {});

Answer №2

If you desire to activate your data deeply, you must include the third argument true in your listener. By default, it is set to false, meaning that your function will only trigger when the variable changes and not its field.

Answer №3

Here is my custom directive using jqplot to visualize data once it is ready:

    app.directive('lineChart', function() {
        $.jqplot.config.enablePlugins = true;

        return function(scope, element, attrs) {
            scope.$watch(attrs.lineChart, function(newValue, oldValue) {
                if (newValue) {
                    // alert(scope.$eval(attrs.lineChart));
                    var plot = $.jqplot(element[0].id, scope.$eval(attrs.lineChart), scope.$eval(attrs.options));
                }
            });
        }
});

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

What is the best way to extract all ID, Name values, and locations from my JSON object and store them in an

I am working with a JSON object named 'tabledata' array. Let's say I want to iterate through all the objects inside it and extract the ID values, so the output would be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. I also need to access other key-value pai ...

Develop a custom dropdown menu using JavaScript

I've been working on creating a dropdown menu that appears after selecting an option from another dropdown menu. Here's the HTML code I'm using: <br> <select id ="select-container" onchange="addSelect('select-container') ...

I keep receiving multiple header errors from ExpressJS even though I am positive that I am only sending a single header

Can someone please help with the issue I'm facing in the code below: router.put("/:_id", async (req: Request, res: Response) => { try { // Create the updated artist variable const artist: IArtist = req.body; const updatedArt ...

Variable Scope is not defined in the TypeScript controller class of an AngularJS directive

I have implemented a custom directive to wrap ag grid like so: function MyDirective(): ng.IDirective { var directive = <ng.IDirective>{ restrict: "E", template: '<div style="width: 100%; height: 400px;" ag-grid="vm.agGrid ...

What is the best method for establishing a page location during rendering/onload?

It seems like there is a JavaScript event happening here using the onload attribute. But for the life of me, I can't seem to crack this code. <body onload="moveToHere('reference')"> I'm stuck and could really use some assistance ...

What is the method for displaying data on a browser instead of downloading it when creating a link?

Clicking on this link will trigger the download of the f.txt file. Rather than downloading it, I would like the data to be displayed directly in the browser. For example, when you click this link, the data is shown directly in the browser. While I have ...

Store the output of a MySQL query as a variable in JavaScript code

As I work on developing a discord bot, one area that I am focusing on involves implementing a database for certain functions. My current challenge revolves around creating a command that retrieves the names of all tables stored in the database. While I hav ...

The initial render of Keen-slider in NextJS can sometimes encounter difficulties when using server-side rendering

While incorporating the keen-slider library v5.4.0 into my nextJS project with TypeScript, I encountered a peculiar issue. The error arises when the keen-slider is initially loaded for the first time, resulting in mismatched transform types causing items ...

The specified userID cannot be located within the array of objects

Trying to understand a tutorial on nodejs and expressjs that teaches how to implement user permissions on routes. However, I'm facing issues with a simple middle ware function designed to set the req.user as it keeps showing up as undefined. Below is ...

How is the purpose of nesting functions within functions achieved in nodejs?

Take a look at this example: var tools1 = require('../tools/tools1'); var test_func = function(arg1, arg2, arg3) { var local_var_1 = "lc1"; var local_var_2 = "lc2"; return function(data) { var result = tools1.doSth(local_va ...

I seem to be having an issue with the decimal point on my calculator - I only want to see one dot

Only need one dot for the calculator to function properly. The current situation with multiple dots is causing confusion. I intend to address other aspects of the code later, but I'm currently struggling with this issue. function dec(d) { let ...

Is it possible to connect to a Node server from outside the network if the application is only listening on 'localhost'?

When utilizing the Express framework and we implement app.listen(port), the app will be located at localhost:port/ On a local machine, it is clear how to access this address using a local browser running on the same machine. Even clients within the same n ...

Although it may not be a constructor, the types certainly align perfectly

Although this question has been asked countless times before, none of these solutions seem to work in my case. Whenever I try to call the Config constructor, I encounter a TypeError: Config is not a constructor. Despite researching on Stack Overflow and M ...

When attempting to perform conditional rendering in React using a stateless functional component, I encounter an error stating "Unexpected token, expected ,"

Here is the code snippet: 'use strict' import React from 'react' import { connect } from 'react-redux' import { Panel, Col, Row, Well, Button } from 'react-bootstrap' const Cart = ({ cart }) => { const cartI ...

Using jQuery for communication between a parent and a popup tab

There have been numerous discussions on the topic of communication between a popup and its parent using window.opener.$('#myDiv'). However, once a popup is launched, how can the parent target and manipulate a specific div within the popup? ...

The Cross-Origin Request has been blocked due to the Same Origin Policy prohibiting access to the remote resource. The reason for this is that the CORS preflight response was unsuccessful

SERVERSIDE // Establishing Headers app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE"); res.header("Access-Control-Allow-Headers ...

What is the best way to wait for information from a meteor call?

As someone new to Angular 1.5, I am facing an issue where the data fetched from Meteor.call() is not showing up in the HTML. Below is my code: hubs.js class HubsController { data = []; constructor($scope, $uibModal) { $scope.viewModel(this); ...

Effortlessly apply mapping, filtering, reducing, and more in JavaScript

Array#map and Array#filter both create a new array, effectively iterating over the original array each time. In languages like rust, python, java, c#, etc., such expression chains only iterate once, making them more efficient in certain cases. While this ...

Executing a php function upon onchange event triggered by CKEditor

My task involves invoking a PHP function when I suspect it is being triggered by an ajax call. Utilizing ckeditor, I aim to detect any keyboard activity and believe that using onchange will serve this purpose. Subsequently, I plan to execute a function t ...

Exploring the interaction between nested view controllers and ng-click in AngularJS

There are 8 different jade views, but only one is loaded and filled with jquery into a div that has a controller. I have two questions: Do I need to define the controller again on top of my partial view (the same controller as the main one)? All views h ...