将此websocket工厂更改为angularjs中的socket.io工厂



由于后端使用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')

最新更新