由于后端使用nodejs和socket.io,我不得不将前端websocket服务更改为socket.io。但存在一些问题。websocket工厂定义如下:
app.factory("WebSocketDataProvider", function() {
var WebSocketDataProvider = function(scope) {
var queuedReqs = [];
var wssock = null;
var modelGraphIdIdx = {};
function getWebSocket() {
if ("WebSocket" in window) return new WebSocket(WS_URI);
else if ("MozWebSocket" in window) return new MozWebSocket(WS_URI);
else return null;
}
function onOpenWssock() {
console.log("Connected. Extensions: [" + wssock.extensions + "]");
console.log("Submitting queued requests:", queuedReqs.length);
while(queuedReqs.length > 0) wssock.send(queuedReqs.shift());
}
function onCloseWssock(e) {
console.log("Disconnected (clean=" + e.wasClean + ", code=" + e.code + ", reason='" + e.reason + "')");
wssock = null;
}
function onMessageWssock(e) {
var data = JSON.parse(e.data);
if(data.error) {
setGlobalAlerts(data);
flashAlertsBar();
} else if(data.annoEvents) {
// annotations //
scope.$apply(function(){scope.globalAnno.status = 'dispatching'});
if(scope.modelType === 'adhoc') {
data._id = scope.graph._id;
var ce = new CustomEvent(data._id, {'detail': data });
wssock.dispatchEvent(ce);
} else {
for(var i in modelGraphIdIdx) {
data._id = i;
var ce = new CustomEvent(data._id, {'detail': data });
wssock.dispatchEvent(ce);
}
}
scope.$apply(function(){scope.globalAnno.status = 'dispatched'});
} else {
// graph data //
var ce = new CustomEvent(data._id, {'detail': data });
wssock.dispatchEvent(ce);
}
}
function initializeWebSocket() {
wssock = getWebSocket();
if(wssock !== null) {
wssock.onopen = onOpenWssock;
wssock.onclose = onCloseWssock;
wssock.onmessage = onMessageWssock;
}
}
this.addGraphIdEventListener = function(graphId, funct) {
wssock.addEventListener(graphId, funct);
modelGraphIdIdx[graphId] = true;
if(Object.keys(modelGraphIdIdx).length === scope.modelGraphIds.length) {
// trigger annotation request as all graph elems are loaded //
scope.globalAnno.status = 'load';
}
}
this.removeGraphIdEventListener = function(graphId, funct) {
if(wssock !== null) wssock.removeEventListener(graphId, funct);
}
this.requestData = function(query) {
try {
wssock.send(JSON.stringify(query));
} catch(e) {
if(e.code === 11) {
queuedReqs.push(JSON.stringify(query));
} else {
//reconnect
queuedReqs.push(JSON.stringify(query));
initializeWebSocket();
}
}
}
this.closeConnection = function() {
wssock.close();
}
initializeWebSocket();
}
return (WebSocketDataProvider);
});
我用这个问题改进这个AngularJS工厂来使用socket.io user65873的答案是这样的:
var ScopedSocket = function (socket, $rootScope) {
this.socket = socket;
this.$rootScope = $rootScope;
this.listeners = [];
this.childSockets = [];
};
ScopedSocket.prototype.removeAllListeners = function () {
var i;
for (i = 0; i < this.listeners.length; i++) {
var details = this.listeners[i];
this.socket.removeListener(details.event, details.fn);
}
for (i = 0; i < this.childSockets.length; i++) {
this.childSockets[i].removeAllListeners();
}
};
ScopedSocket.prototype.on = function (event, callback) {
var socket = this.socket;
var $rootScope = this.$rootScope;
this.listeners.push({event: event, fn: callback});
socket.on(event, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
};
ScopedSocket.prototype.emit = function (event, data, callback) {
var socket = this.socket;
var $rootScope = this.$rootScope;
socket.emit(event, angular.fromJson(angular.toJson(data)), function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
});
};
ScopedSocket.prototype.of = function (channel) {
var childSocket = new ScopedSocket(this.socket.of(channel), this.$rootScope);
this.childSockets.push(childSocket);
return childSocket;
};
app.factory('Socket', ['$rootScope', function ($rootScope) {
//var socket = $rootScope.socket;
var queuedReqs = [];
var modelGraphIdIdx = {};
return function(scope) {
var socket = io(WS_URI);
var scopedSocket = new ScopedSocket(socket, $rootScope);
scopedSocket.on('connect', function(){
while(queuedReqs.length > 0) scopedSocket.emit('queuedReqs', queuedReqs.shift());
});
scopedSocket.on('error', function(data){
setGlobalAlerts(data);
flashAlertsBar();
});
scopedSocket.addGraphIdEventListener = function(graphId, funct) {
scopedSocket.on(graphId, funct);
modelGraphIdIdx[graphId] = true;
if(Object.keys(modelGraphIdIdx).length === scope.modelGraphIds.length) {
// trigger annotation request as all graph elems are loaded //
scope.globalAnno.status = 'load';
}
}
scopedSocket.removeGraphIdEventListener = function(graphId, funct) {
if(scopedSocket !== null) scopedSocket.socket.removeListener(graphId, funct);
}
scopedSocket.requestData = function(query) {
try {
scopedSocket.emit('query', JSON.stringify(query));
} catch(e) {
if(e.code === 11) {
queuedReqs.push(JSON.stringify(query));
} else {
//reconnect
queuedReqs.push(JSON.stringify(query));
}
}
}
scopedSocket.closeConnection = function() {
}
scope.$on('$destroy', function() {
scopedSocket.removeAllListeners();
});
return scopedSocket;
};
}]);
但我在控制台上得到了这个错误:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.2.9/$rootScope/inprog?p0=%24apply
at Error (native)
at http://localhost:3000/libs/angular/1.2.9/angular.min.js:6:449
at m (http://localhost:3000/libs/angular/1.2.9/angular.min.js:96:353)
at h.$apply (http://localhost:3000/libs/angular/1.2.9/angular.min.js:103:31)
at Socket.processRecievedData (http://localhost:3000/libs/angular-graphing.js:271:12)
at http://localhost:3000/app.js:747:22
at h.$eval (http://localhost:3000/libs/angular/1.2.9/angular.min.js:102:308)
at h.$apply (http://localhost:3000/libs/angular/1.2.9/angular.min.js:103:48)
at Socket.<anonymous> (http://localhost:3000/app.js:746:20)
at Socket.Emitter.emit (http://localhost:3000/libs/socket.io-client/socket.io.js:1194:20)
at Socket.onevent (http://localhost:3000/libs/socket.io-client/socket.io.js:832:10)
我不知道我的代码哪里错了。所以我想得到一些帮助。
如果你不想麻烦做所有这些艰苦的工作,socket.io已经有了一个Angular接口(btford Angular socket-io),除非这样做是为了好玩:
您收到的错误是因为$apply
调用已完成,而另一个$apply
正在进行中。就在这里:
else if (data.annoEvents) {
// annotations //
scope.$apply(function () {
scope.globalAnno.status = 'dispatching'
});
if (scope.modelType === 'adhoc') {
data._id = scope.graph._id;
var ce = new CustomEvent(data._id, {
'detail': data
});
wssock.dispatchEvent(ce);
} else {
for (var i in modelGraphIdIdx) {
data._id = i;
var ce = new CustomEvent(data._id, {
'detail': data
});
wssock.dispatchEvent(ce);
}
}
scope.$apply(function () {
scope.globalAnno.status = 'dispatched'
});
}
问题是代码执行得太快了,以至于在状态更改之前,是angular的范围,对于"dispatching",会启动将其更改为"dispatched"的调用。为了测试您的代码是否有效,您可以在最后调用scope.$digest()
一次,而不是调用$apply()
:
else if (data.annoEvents) {
// annotations //
scope.globalAnno.status = 'dispatching' // it will not be updated in the angular scope
if (scope.modelType === 'adhoc') {
data._id = scope.graph._id;
var ce = new CustomEvent(data._id, {
'detail': data
});
wssock.dispatchEvent(ce);
} else {
for (var i in modelGraphIdIdx) {
data._id = i;
var ce = new CustomEvent(data._id, {
'detail': data
});
wssock.dispatchEvent(ce);
}
}
scope.globalAnno.status = 'dispatched'
scope.$digest();
}
$digest
调用的问题是status='dispatching'
不会在角度范围内更新,因此您将无法在UI或其他地方看到它。
现在,您可能需要重新思考体系结构。我建议您使用Angular事件。如果你仔细想想,你已经在处理socket.io/websocket中的事件了,为什么不让它们处理angular呢。(角插座io有类似的方法,所以你可以检查一下它的例子)
与其将状态存储在变量scope.globalAnno.status = 'dispatching'
上,不如从根作用域发出它,并在需要的地方使用它:$rootScope.$emit('socket:dispatching')
。