问题
I'm trying to build real time service messaging with MongoDB and AngularJS. For some reason, when there is new data in my 'messaging' collection, the Messaging.getAllMessages() service is not triggered and my data is not updated in the view, using $watchCollection.
This is in my services.js, the important function is getAllMessages():
angular.module('myApp')
.factory('Messaging', function($resource, $http, $q, $rootScope){
var myData = {};
return {
sendMessage: function(message, success, error) {
$http.post('/api/v1/users/' + message.to.id + '/messages', message).success(function(res) {
toastr.success('Message sent');
}).error(function(error) {
toastr.error("Error on save");
});
},
getAllMessages: function(userId) {
var deferred = $q.defer();
if(!myData) {
deferred.resolve(myData);
} else if (userId && userId !== '') {
$http.get('/api/v1/users/' + userId+ '/messages').success(function(data) {
myData = data;
deferred.resolve(myData);
// update angular's scopes
$rootScope.$$phase || $rootScope.$apply();
});
} else {
deferred.reject();
}
return deferred.promise;
},
markAsRead: function(ids, success, error) {
var deferred = $q.defer();
if(!myData) {
deferred.resolve(myData);
} else if (ids && ids !== '') {
$http.put('/api/v1/users/' + ids.userId + '/messages/' + ids.messageId).success(function(data) {
myData = data;
deferred.resolve(myData);
// update angular's scopes
$rootScope.$$phase || $rootScope.$apply();
});
} else {
deferred.reject();
}
return deferred.promise;
},
getMessage: function(ids, success, error) {
return $http.get('/api/v1/users/' + ids.userId + '/messages/' + ids.messageId);
},
deleteMessage: function(ids, success, error) {
return $http.delete('/api/v1/users/' + ids.userId + '/messages/' + ids.messageId);
}
}
});
This is in directive.js:
angular.module('myApp').directive('messaging', ['$log', 'Messaging', function($log, Messaging){
return {
scope: true,
restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment
templateUrl: '/views/templates/messaging-dropdown.html',
replace: true,
link: function($scope, iElm, iAttrs, controller) {
Messaging.getAllMessages($scope.user.id).then(function(myData) {
$scope.allMessages = myData;
$scope.newMessages = 0;
$scope.$watchCollection('allMessages', function(newVal, oldVal){
if(newVal !== oldVal) {
$scope.newMessages = 0;
// Count the number of unread messages
for (var i = myData.length - 1; i >= 0; i--) {
if(myData[i].read === false) {
$scope.newMessages++;
}
};
}
}, true);
}, function() {
// request failed (same as 'return false')
$scope.allMessages = 'i got the error';
});
}
};
}]);
And this is the template, messaging-dropdown.html:
<div>
<a ng-click="showMessages()" ng-class="{clicked: messagesToggled}">
<i class="fa fa-envelope"></i>
<span class="badge" ng-show="newMessages > 0">{{newMessages}}</span>
</a>
<ul class="dropdown-menu" ng-show="messagesToggled">
<li ng-repeat="message in allMessages | limitTo: 5 | orderBy:'sent'">
<a ng-href="/users/{{message.to.id}}/messages/{{message._id}}" ng-class="{unread: !message.read}">
<img ng-src="{{message.from.image}}" alt="" class="img-circle">
<span class="body">
<span class="from">{{message.from.name}}</span>
<span class="message">
{{message.text}}
</span>
<span class="time">
<span>{{message.sent}}</span>
</span>
</span>
</a>
</li>
<li class="footer">
<a ng-href="/users/{{user.id}}/messages">See all <b>{{allMessages.length}}</b> messages</a>
</li>
</div>
As you see the $scope.newMessages is not updated by the watch, when there is new data in the array returned by the serive. I'm missing something, is there need for socket.io or Pusher/Pubnub to achieve the desired behaviour? Thanks in advance for any help.
回答1:
Thanks @Sunil for pointing that out. I originally thought, that $watch() and $watchCollection(), will pull automatically and check the service for new data based on specific interval provided by the $digest() cycle.
I released that my solution needs some kind of trigger, so that the new data to be pulled from the service when available. So I implemented private messaging channels using socket.io for triggering updates on message sent from the other party.
Here is the updated directive:
Updated directive.js:
angular.module('myApp').directive('messaging', ['$log', 'Messaging', 'messagingSocket', function($log, Messaging, messagingSocket){
// Runs during compile
return {
scope: true,
restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment
templateUrl: '/views/templates/messaging-dropdown.html',
replace: true,
link: function($scope, iElm, iAttrs, controller) {
messagingSocket.emit('setUser', {id: $scope.user.id});
$scope.allMessages = function(){
Messaging.getAllMessages($scope.user.id).then(function(myData) {
$scope.allMessages.messages = myData;
}, function() {
// request failed (same as your 'return false')
$scope.allMessages = 'i got the error';
});
};
$scope.allMessages();
messagingSocket.forward('new message', $scope);
$scope.$on('socket:new message', function (event, data) {
$scope.allMessages();
});
$scope.$watchCollection('allMessages.messages', function(newVal, oldVal){
if(newVal !== oldVal) {
$scope.newMessages = 0;
// Count the number of unread messages
for (var i = $scope.allMessages.messages.length - 1; i >= 0; i--) {
if($scope.allMessages.messages[i].read === false) {
$scope.newMessages++;
}
};
}
}, true);
}
};
}]);
来源:https://stackoverflow.com/questions/20821083/service-with-promise-and-watchcollection-not-updating-the-values