What is the best way to monitor the currentUser service for updates in my navigation controller?

My navbar has a "nav" controller that displays the current user's image. However, I'm facing an issue where the nav image does not update when the user changes their profile photo.

Below is my service:

'use strict';
angular.module('clientApp').factory('Account', function ($http, toastr) {
  var currentUser = null;
  function getUser(callback, user) {
    console.log('get user user ', user)
    // if the user has already been retrieved then do not do it again, just return the retrieved instance
    if (currentUser !== null && currentUser !== user) {
      callback(currentUser);
    }
    if (currentUser === null) {
      // retrieve the currentUser and set it as a property on the service
      $http.get('/api/me').then(function (res) {
        // set the result to a field on the service
        currentUser = res.data;
        // call the callback with the retrieved user
        callback(currentUser);
      });
    }
  }

  //updates the currentUser variable
  function updateProfile(profileData, callback) {
    console.log('profile data ', profileData);
    $http.put('/api/me', profileData).then(function (res) {
      currentUser = res.data;
      callback(currentUser);
    }).catch(function (response) {
      if (response.data.message.length) {
        for (var i = 0; i < response.data.message.length; i++) {
          toastr.error(response.data.message[i]);
        }
      }
    });
  }
  return {
    getUser: getUser,
    updateProfile: updateProfile
  };
});

Watcher in the nav controller:

  $scope.user = {};

  //watches for currentUser changes in account service
  $scope.$watch(Account.getUser(function(user){}, $scope.user), function (newValue, oldValue) {
    console.log('user update watch', newValue)
      $scope.user = newValue;
  });

I seem to be confused about something here. I have been studying information on $watch, but it is not behaving as I anticipated. This could be because Account.getUser doesn't return anything for the $watch to compare, and instead uses a callback...

If anyone can help me identify where I am mistaken — I would greatly appreciate it.

Answer №1

One thing to keep in mind is that your utilization of $watch may not be optimal. You don't necessarily need to pass callback functions since you can always return promises to the caller.

Let's look at an example where the NavCtrl monitors changes in the currentUser, which is maintained and shared across controllers in your userService. The MainCtrl is responsible for invoking the service to retrieve the user data (simulating a login process).

In cases where the user information is either unknown or unchanged, you can use $q to return user details from the service. When an update is required, you can utilize $http for this purpose as both methods return promises.

Code Snippet

<body>
  <!-- navigation bar -->
  <div ng-controller="NavCtrl">
    Nav
    <div ng-bind="user.name"></div>      
  </div>
  <br>

  <!-- main content -->
  <div ng-controller="MainCtrl">
    Main
    <div ng-bind="'User cached: ' + !!isCached"></div>
    <br>
    <input type="button" ng-click="getUser()" value="Get">
  </div>
</body>

JavaScript Logic

angular.module('app', [])

.controller('NavCtrl', function($scope, userService) {
  var unwatch = $scope.$watch(function() {
    return userService.currentUser;
  }, function(user) {
    $scope.user = user || { name: 'No user' };
  });

  $scope.$on('$destroy', unwatch);
})

.controller('MainCtrl', function($scope, userService) {
  $scope.getUser = function() {
    var id = Math.floor((Math.random() * 10) + 1); // generating random ID between 1 and 10
    userService.getUser(id).then(function(user) {
      $scope.isCached = user.cached;
    });
  };
})

.factory('userService', function($q, $http) {
  var userService = {
    currentUser: null,
    getUser: function(id) {
      if (userService.currentUser && userService.currentUser.id === id) {
        userService.currentUser.cached = true;
        return $q.when(userService.currentUser); 
      } else {
        var url = 'https://jsonplaceholder.typicode.com/users/' + id;
        return $http.get(url).then(function(response) {
          userService.currentUser = response.data;
          return userService.currentUser;
        })
      }
    }
  };

  return userService;
});

https://i.sstatic.net/hiAhf.png


Check out the related plunker https://plnkr.co/edit/DzfAtI

Answer №2

If you want to achieve a similar functionality, you can make use of an instance variable within the service.

After updating the currentUser variable in the service following the update api call, as you are storing it globally, you can then return it as a response from the service.

I was able to accomplish this without relying on either watch or rootscope. Check out the code snippet below and run it:

Take a look at the code based on @Mikko's original code:

angular.module('app', [])

.controller('NavCtrl', function($scope, userService) {
  $scope.getUser = function() {
    var id = Math.floor((Math.random() * 10) + 1); // 1...10
    userService.getUser(id).then(function(user) {
      console.log('user',user);
      $scope.user = user;
    });
  };

})

.controller('MainCtrl', function($scope, userService) {
  
})

.factory('userService', function($q, $http) {
  var userService = {
    currentUser: null,
    getUser: function(id) {
      if (userService.currentUser && userService.currentUser.id === id) {
        userService.currentUser.cached = true;
        return $q.when(userService.currentUser);
      } else {
        var url = 'https://jsonplaceholder.typicode.com/users/' + id;
        return $http.get(url).then(function(response) {
          userService.currentUser = response.data;
          console.log(userService.currentUser);
          return userService.currentUser;
        })
      }
    }
  };

  return userService;
});
<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script data-require="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="fa9b949d8f969b88cb859aadaca89a8cdad">[email protected]</a>" data-semver="1.6.0" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <div ng-controller="NavCtrl">
      Content from navigation controller.
      <br><br>
      <input type="button" ng-click="getUser()" value="Get">
      <br><br>
      User Details:
      <br><br>
      <div ng-bind="user"></div>      
      <br><br>
      Name:
      
      <div ng-bind="user.name"></div>      
    </div>
    <br>
    
  </body>

</html>

Check out the live demo here

Answer №3

Why not utilize $rootScope in place of $watch?

This enables you to easily make updates from various controllers.

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

Customize cell color in angularjs and ui-grid

In my ui-grid, I have two columns - firstName and lastName. The background color of the firstName column is set to blue. When I click on the header of the lastName column, I want to change the background color of the lastName column to blue and return the ...

Can the title of a page be modified without using HTML?

Is it possible to update the title of my website directly from the app.js file? (I am utilizing node.js and express.js) ...

When initiating Selenium RC, custom commands in the user-extensions.js file fail to load

Recently, I've been utilizing Selenium IDE to develop some tests. Within the IDE settings, I've specified a user-extensions.js file which is functioning as intended. Here's a snippet of its contents: Selenium.prototype.doactivateEnv = funct ...

Difficulty aligning Material2 icons and title text in CSS styling

I'm encountering a design problem when incorporating material2 icons into my Angular application. Specifically, I have a button that displays both a material2 icon and some text side by side from left to right. Currently, the icons are centered within ...

Exploring the world of SPA: Implementing Data Transfer Objects

Considering implementing a Single Page Application (SPA) using a JavaScript framework like Angular JS. Currently, I have multiple existing Web APIs containing the necessary information for the app. I need to add another API that will handle new data and re ...

How to implement an Angular Animation that is customizable with an @Input() parameter?

Have you ever wondered if it's possible to integrate custom parameters into an Angular animation by passing them through a function, and then proceed to use the resulting animation in a component? To exemplify this concept, consider this demo where t ...

Importing named exports dynamically in Next.js

Currently, I am in the process of learning Next.js and I want to utilize a function called getItem from the package found at https://www.npmjs.com/package/encrypt-storage In my attempt to do so using the code snippet below, I encountered an error stating ...

Is there a way to simplify my code and improve readability without sacrificing performance?

After completing the development of an explosive hoverboard model, it is now live on my website: . However, the code I've written seems to be quite messy and I'm struggling to optimize it without compromising performance. Is there a way to effici ...

Change the background color of the MUI ToggleButton that is currently selected

I need help figuring out how to change the background color of a selected toggle button. Currently, the buttons are functional but do not visually indicate when one is selected. I would like the first button (Btn 1) to have a default color, and if the us ...

Do I need to make any changes to the application when adding a new couchbase node to the cluster

Currently, I am utilizing the Node.js SDK to establish a connection with a couchbase cluster. Despite this, in the Node.js documentation, there is no clear instruction on how to input multiple IP addresses (of cluster nodes) when creating the cluster objec ...

What methods can be used to block the input of non-numeric characters in a text field?

I stumbled upon this particular inquiry. Although, the majority of responses involve intercepting key presses, checking the key code, and halting the event if it does not match an acceptable key code. However, there are some issues with this approach. ...

directive in Angular pointing to a specific controller

I'm confused about where this error is coming from because I thought I had the syntax correct. An error of type TypeError: undefined is not a function was encountered. This particular error occurs in line 13 of the directive, where it says var refre ...

Extracting information from XML and showcasing it in an HTML format

My current setup includes two hard-coded boxes from line 18 to line 70 in the HTML section. Now, I have an .XML file containing: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <collection> <beanRepresentation> <beanRepId> ...

Using GeoJson in Leaflet for map display

As I develop an Angular application with Leaflet, my goal is to showcase numerous municipalities within my country: https://i.sstatic.net/9cuSW.png In order to enhance navigation efficiency, I decided to divide my JSON file into multiple files and store ...

AngularJS - Troubleshooting View Scrolling Problem with CSS

One issue I'm facing is that when the content within my view causes a scrollbar to appear, it scrolls into whitespace even though the view's overflow is hidden. I suspect it might be a CSS problem related to my positioning system. Unfortunately, ...

Substituting a child instead of adding it to the table

I have a query regarding a button that dynamically adds rows to a table based on an array's data. My requirement is to append the first row, but for subsequent rows, I want them to replace the first row instead of being appended, ensuring that only on ...

When working with Angular, I encountered an issue where the base href='/ ' did not seem to function properly

Currently, I am utilizing Angular along with angular-ui-router for navigation purposes. The version of Angular being used is 1.6.4, whereas the angular-ui-router version is 0.4.2. Upon inspection in the Firefox console Inspector, it was discovered that u ...

modify handler for selecting input item in react JS

I am working on mapping option select data from an API, but I am having trouble changing the value of the select input. I have tried using a handler for change, but the input value remains fixed. Can someone please help me identify what error might be pres ...

Create spinning wheel - canvas

Hey there! I'm trying to create a cool spinning wheel using a canvas and JS. My wheel is supposed to have 10 segments, each saved as a .png file. https://i.sstatic.net/glN1p.jpg In order to achieve a "full circle", I want to draw these 10 segments i ...

Is polymer routing the best choice for large-scale websites?

I am currently working on a large project that consists of various layouts and structures, totaling around 100 different pages. Despite the variations in content, the core elements such as headers and menus remain consistent throughout. To streamline my ...