14517

how to $watch currentUser service for changes in my nav controller?

Question:

I have a have navbar with a "nav" controller, which displays the current user's image. However, i'm not able to get the nav image to change if the user updates their profile photo.

here 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 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'm obviously confusing something here. I'm reviewing information on $watch, but this is not working as I expected. Perhaps because Account.getUser doesn't return anything for the $watch to compare, and instead uses a callback..

If anyone could point where i'm going wrong -- much appreciated.

Answer1:

For one you are not using $watch quite right, you don't need to pass in callback functions because you can always return promises to the caller.

Consider following example where NavCtrl listens to changes in currentUser, which is kept, and shared among controllers, in your userService. MainCtrl is where you call the service to fetch the user (mimicking login or similar).

When user is not known or is not changed, you can return user information from service using $q or when it needs to update, using $http. These both return promises.

<strong>HTML</strong>

<body> <!-- nav bar --> <div ng-controller="NavCtrl"> Nav <div ng-bind="user.name"></div> </div> <br> <!-- content --> <div ng-controller="MainCtrl"> Main <div ng-bind="'User cached: ' + !!isCached"></div> <br> <input type="button" ng-click="getUser()" value="Get"> </div> </body>

<strong>JavaScript</strong>

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); // 1...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; });

<a href="https://i.stack.imgur.com/hiAhf.png" rel="nofollow"><img alt="imgur" class="b-lazy" data-src="https://i.stack.imgur.com/hiAhf.png" data-original="https://i.stack.imgur.com/hiAhf.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<hr />

Related plunker here <a href="https://plnkr.co/edit/DzfAtI" rel="nofollow">https://plnkr.co/edit/DzfAtI</a>

Answer2:

You can do something like this using a instance variable in the service,

When you update the currentUser variable in the service after the update api, since you are storing it in currentUser in a global variable, you can send that as a response of service.

<strong>I have done this without using either watch or rootscope, please find the code below, also run the snippet</strong>

Here is the code based on the code from @Mikko,

<pre class="snippet-code-js lang-js prettyprint-override">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; }); <pre class="snippet-code-html lang-html prettyprint-override"><!DOCTYPE html> <html ng-app="app"> <head> <script data-require="angular.js@1.6.0" 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"> 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>

<a href="https://plnkr.co/edit/2yI87MLwo6kmXSyDLaMZ?p=preview" rel="nofollow"><strong>Here is the working demo</strong></a>

Answer3:

You can use $rootScope instead of $watch.

In that way you can update it from any controller.

Recommend

  • Expand collection elements in Watches window while debugging?
  • Can $watch 'miss'?
  • Angular appending is fast but destroying is slow(1 second)
  • Grunt build not populate scripts.js with bower_components
  • How to update a table based on an XML parameter
  • Looping through Generic Class's collection property
  • Applescript folder actions support for changed / altered / updated files?
  • Spring integration - how to check a directory for files, without polling?
  • Keep a Windows Service running without a timer
  • Watch for updated properties in Wicket
  • Error 3251 on .oldValue control property
  • track changes of nodes bound in JavaFX
  • Changing Key Of An Property in Json
  • How do I create closures for model getter-setter in angular?
  • Know when a file changes on windows 8
  • How to run .ear file in JBoss 6?
  • How to prevent TreeItem selection?
  • Adding a new element into the DOM with angularjs does not initiate it
  • Generating anchors with PyYAML.dump()?
  • HighCharts - Show tooltip on column where value is 0 or null
  • Configure Spring's MappingJacksonHttpMessageConverter
  • Using $compile in a directive triggers AngularJS infinite digest error
  • Updating Dojo provide
  • How to autopopulate a field in SugarCRM form
  • Function calls are not supported. Consider replacing the function or lambda with a reference to an e
  • Is it possible to access block's scope in method?
  • Java Scanner input dilemma. Automatically inputs without allowing user to type
  • AES padding and writing the ciphertext to a disk file
  • bootstrap to use multiple ng-app
  • retrieve vertices with no linked edge in arangodb
  • Hits per day in Google Big Query
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Change div Background jquery
  • Linking SubReports Without LinkChild/LinkMaster
  • apache spark aggregate function using min value
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • costura.fody for a dll that references another dll
  • Observable and ngFor in Angular 2
  • UserPrincipal.Current returns apppool on IIS
  • java string with new operator and a literal