AngularJS: Understanding the 'controller as' syntax and the importance of $watch

Is it possible to subscribe to property changes when using the controller as syntax?

controller('TestCtrl', function ($scope) {
  this.name = 'Max';
  this.changeName = function () {
    this.name = new Date();
  }
  // not working       
  $scope.$watch("name",function(value){
    console.log(value)
  });
});
<div ng-controller="TestCtrl as test">
  <input type="text" ng-model="test.name" />
  <a ng-click="test.changeName()" href="#">Change Name</a>
</div>  

Answer №1

Simply connect the appropriate context.

$scope.$watch(angular.bind(this, function () {
  return this.name;
}), function (newVal) {
  console.log('Name changed to ' + newVal);
});

Check out this example: http://jsbin.com/yinadoce/1/edit

LATEST UPDATE:

I must say that Bogdan Gersak's response is quite similar; both suggestions focus on correctly binding this with the relevant context. However, I personally find his solution more elegant.

That being said, it is important to grasp the fundamental concept behind this process.

SECOND UPDATE:

For those using ES6, you can achieve the desired context by utilizing an arrow function.

$scope.$watch(() => this.name, function (newVal) {
  console.log('Name changed to ' + newVal);
});

See this example for a better understanding.

Answer №2

My typical approach is as follows:

controller('ExampleCtrl', function ($scope) {
    var vm = this;

    this.title = 'John';
    this.changeTitle = function () {
        this.title = new Date();
   }

   $scope.$watch(function () {
       return vm.title;
   },function(value){
        console.log(value)
   });
});

Answer №3

Here is an example of how you can achieve this:

   $scope.$watch("test.name",function(value){
        console.log(value)
   });

Check out the demo on JSFiddle to see it in action.

Answer №4

Instead of relying on the "test" from "TestCtrl as test" method mentioned in another response, you have the option to assign "self" as your scope:

controller('TestCtrl', function($scope){
    var self = this;
    $scope.self = self;

    self.name = 'john';
    self.updateName = function(){
            self.name = new Date();
        }

    $scope.$watch("self.name",function(value){
            console.log(value)
        });
})

This approach allows you to avoid being limited by the name defined in the DOM ("TestCtrl as test"), and eliminates the necessity to .bind(this) to a function.

...which can be utilized with the original html markup provided:

<div ng-controller="TestCtrl as test">
    <input type="text" ng-model="test.name" />
    <a ng-click="test.updateName()" href="#">Update Name</a>
</div>

Answer №5

In AngularJs 1.5, the default $ctrl is used for implementing the ControllerAs structure.

$scope.$watch("$ctrl.name", (newValue) => {
    console.log(newValue)
});

Answer №6

Here's a cool trick you can do with AngularJS: passing a function as the first argument of $watch():

 app.controller('TestCtrl', function ($scope) {
 this.name = 'Max';

// Check out this function
 $scope.$watch(function () {}, function (value){ console.log(value) });
 });

This means we can reference our `this.name` property:

app.controller('TestCtrl', function ($scope) {
    this.name = 'Max';

    // This is awesome
    $scope.$watch(angular.bind(this, function () {
    return this.name; // Accessing the `this` context from above!!
    }), function (value) {
      console.log(value);
    });
});

If you want to learn more about the controllerAs syntax in AngularJS, check out this informative post.

Answer №7

One suggestion is to utilize the $onChanges angular component lifecycle method.

For more information, refer to the documentation at: https://docs.angularjs.org/guide/component specifically in the Component-based application section

Answer №8

Implementing a $watch feature in ES6 format posed a challenge that I had not anticipated. Here's the solution:

// Assuming
// controllerAs: "ctrl"
// or
// ng-controller="MyCtrl as ctrl"
export class MyCtrl {
  constructor ($scope) {
    'ngInject';
    this.foo = 10;
    // Option 1
    $scope.$watch('ctrl.foo', this.watchChanges());
    // Option 2
    $scope.$watch(() => this.foo, this.watchChanges());
  }

  watchChanges() {
    return (newValue, oldValue) => {
      console.log('new', newValue);
    }
  }
}

Answer №9

REMINDER: This method may not function properly if the View and Controller are intertwined within a route or directive definition object. The following solution is effective only when "SomeController as SomeCtrl" is specified in the HTML. As pointed out by Mark V. in the comment below, it's advisable to follow the approach suggested by Bogdan.

In my implementation, I start the controller with var vm = this; to avoid any confusion with the keyword "this." Then, I assign vm.name = 'Max';, and in the watch function, I use return vm.name. Similar to @Bogdan's usage of "self," I utilize "vm" for consistency. This variable, whether named "vm" or "self," is necessary because the context of "this" changes within the function (thus, returning this.name wouldn't be effective). Additionally, remember to inject $scope in your elegant "controller as" solution to access $watch. For further guidance, refer to John Papa's Style Guide: https://github.com/johnpapa/angularjs-styleguide#controllers

function SomeController($scope, $log) {
    var vm = this;
    vm.name = 'Max';

    $scope.$watch('vm.name', function(current, original) {
        $log.info('vm.name was %s', original);
        $log.info('vm.name is now %s', current);
    });
}

Answer №10

Learn how to implement this technique without using $scope and $watch by checking out the article on Avoiding Common Mistakes in AngularJS

When utilizing the "controller as" syntax, it is recommended to refrain from relying on $scope for cleaner code.

You can view a demonstration of this approach in action on JSFiddle. (A service is used to manage the name value to prevent stack overflow caused by ES5 Object.defineProperty's set and get methods.)

var app = angular.module('my-module', []);

app.factory('testService', function() {
    var name = 'Max';

    var getName = function() {
        return name;
    }

    var setName = function(val) {
        name = val;
    }

    return {getName:getName, setName:setName};
});

app.controller('TestCtrl', function (testService) {
    var vm = this;

    vm.changeName = function () {
        vm.name = new Date();
    }

    Object.defineProperty(this, "name", {
        enumerable: true,
        configurable: false,
        get: function() {
            return testService.getName();
        },
        set: function (val) {
            testService.setName(val);
            console.log(vm.name);
        }
    }); 
});

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

Ways to verify if a string is a number without using isNaN and with specified criteria

I am trying to determine if a string represents a valid number without relying on the isNaN function. The reason for this is that I need to be able to accept the ',' character, which isNaN ignores. Additionally, I do not want to allow negative nu ...

What is the best way to integrate new entries into the data source of a Kendo UI grid?

After successfully creating a kendo.data.dataSource, I managed to bind it to the KendoUI Grid on my page. However, when attempting dataSource.insert(0, [a : "b"]);, it surprisingly removes the existing data. The code snippet that illustrates this issue i ...

Generating dynamic anchor tags in Vue.JS

I have a JavaScript object that I want to convert into HTML elements and display it in Vue.js. So far, my approach has been to convert the object into strings representing HTML elements and then add them to the template. However, even though this method di ...

Enhance the functionality of Jquery UI drag and drop to be responsive even when in a zoom

I came across this drag and drop demo for Jquery UI that was working perfectly at a regular zoom level. However, when I zoomed out to 50%, things started acting strange. Can anyone provide suggestions on how to fix the demo? Link to Demo Any help would be ...

Converting a tree structure into JSON with the help of tree-model-js

Would it be possible to find a method to convert a TreeModel into a JSON string for storage purposes and then use tree.parse() to reconstruct it later on? Whenever trying JSON.stringify(root), an error is thrown due to cyclic references caused by children ...

The "keydown" event in React will not alter the state

I am currently developing an application that requires me to track the keys pressed by the user. I am utilizing keydown and keyup events for this purpose. However, I am facing a challenge where I do not want the same key to be registered multiple times whe ...

Is there a way to identify when an image extends beyond the boundaries of the browser window and subsequently reposition it?

Currently, I am implementing a method found at this link to display popup images of my thumbnails. However, there is an issue when the thumbnail is positioned close to the edge of the screen and the original image is too large, causing it to be cut off by ...

extract data obtained from an AJAX call

I am working on an ajax request in my code: var rootURL = "http://localhost/myapp/api/api.php"; $.ajax({ type: 'GET', url: rootURL + '/favourites', dataType: "json", success: function(list) { }, error: f ...

Object Literal vs Object-Oriented Javascript: Comparing the Two

When it comes to using Object-Oriented Programming (OOP) in JavaScript, I often find myself not utilizing it much. For instance, instead of defining a constructor function and setting up prototypes like this: function Person(name){ return this.name = name ...

Exploring Node JS Express Thread Clarity

Having recently delved into the world of node js, I've familiarized myself with its architecture. I grasp the concept of the event loop, the main thread (V8 engine thread), and the other threads handled by libuv. When the main thread needs to handle ...

Can you please guide me on retrieving information from an API using HTML, CSS, and JavaScript?

My task is to develop a web application using HTML, CSS, and JS with the following functionality: Retrieve a list of users from this API: https://jsonplaceholder.typicode.com/users. Upon clicking on a user, show a list of albums associated with that user ...

Issues with NGRepeat causing AJAX JSON errors

Facing an issue with displaying JSON data fetched from the server (Node.js) in NGrepeat. Various attempts have been made to troubleshoot using Firebug and Firefox Web Inspector. Despite the JSON data appearing correct when viewed in the Firebug console ( ...

Sending data from a PHP function to an AJAX call in the form of an associative array using json_encode() does not

Greetings, this is my first post so please forgive me if I miss anything or fail to explain clearly. All the code below is within the same PHP file. Here is my AJAX call: $.ajax( { type: "POST", url: window.location.href, data: {func: 'g ...

Canceling the http post request while subscribing to a service

After wrestling with this issue for a whole week, I am still unable to successfully send a basic post request to the Heroku server using Angular HttpClient. I've meticulously defined all the services in the provider section of the main app module. Str ...

Ajax dependent dropdown is failing to load properly on the live server

My dropdown menus for country, state, and city are chained to each other, where the state options depend on the selected country and the city options depend on the selected state. While this functionality works perfectly on my local server, it's not w ...

Checking CORS permissions with a pre-flight OPTIONS request

During development, I implement a middleware called cors using the following syntax: app.use(cors({origin: 'http://localhost:8100'})); However, I have noticed that for every route request, two requests are being made as shown in the image below ...

JavaScript is failing to add fields to objects as expected

I am facing an issue with a process involving an array of objects in my database (Mongo). Despite adding an additional property to the object just before saving it, the property does not reflect in the saved data. I have double-checked by logging the objec ...

Unable to add or publish text in CKEditor

In my ASP.NET MVC application, I am struggling to post the updated value from a CKEditor in a textarea. Here is the code snippet: <textarea name="Description" id="Description" rows="10" cols="80"> This is my textarea to be replaced with CKEditor ...

Issue: Incorrect comparison of two dates leading to inaccurate results

I have been attempting to compare two dates within a UI-GRID, and although I have the dates in the correct format, it seems to always provide a false result. Below is the code: filters: [{ condition: function (term, value) { if (!term) return ...

What is the best way to append a JavaScript object to a JSON file on a new line

What changes should be made to this function in order to append each object in the file on a new line? exports.addWaypoint = function(id, type, param){ var dataIn = fs.readFileSync('./markers.json'); var obj = JSON.parse(dataI ...