【问题标题】:Angularjs SPA + Flask-Socketio events being fired multiple timesAngularjs SPA + Flask-Socketio 事件被多次触发
【发布时间】:2016-03-22 17:48:36
【问题描述】:

我在使用 angularjs 和 socketio 聊天应用程序时遇到的问题是,当向另一个用户发送消息时,用于显示消息的事件处理程序被触发多次,因此同一条消息多次出现在屏幕上,我只希望它显示一次消息。有人可以解释为什么会发生这种情况以及如何解决这个问题。

这是我的客户端代码。我使用 ngRoute 来更改页面,并且在切换页面时使用服务来保存数据。

     // This method populates the chatbox on startup
     // Also it is emitted to on server side to display a new message
     socket.on("populateMessagesArchived", function (message) {
         $scope.tempArray = [message[0], message[1], message[2]];
         $scope.messages.push($scope.tempArray);
         $scope.$apply();
         $scope.tempArray = [];
         $scope.message="";
     });


     // When a users sends a message, this is invoked
     // this function gets the roomNum and message where roomNum corresponds to the roomNum where user sent message
     $scope.getMessage = function(roomNum, message) {
       $scope.tempArray = [roomNum, message, $scope.username];
       $scope.messages.push($scope.tempArray);
       socket.emit("storeMessagesInDatabase", {"uuid": $scope.uuid, "message": message, "roomNum": roomNum, "username": $scope.username});
       var element = document.getElementById('msgpane');
       element.scrollTop = element.scrollHeight;      
       $scope.tempArray = [];
       $scope.message="";
     };

服务器代码: 基本上将消息添加到 postgresql 数据库。并处理登录检查和注册。但我认为问题不在于这里。我注意到的是,当我切换页面时,会建立另一个 socketio 连接。 socketio 连接的数量对应于打印出的消息数量。但我不确定如何解决这个问题。任何帮助将不胜感激。

@socketio.on('userLoggedIn', namespace='/iss')
def loggedIn(user):        
    userData = user
    connect = connectToDB()
    cursor = connect.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute("""select uuid, username from users WHERE username = %s AND password = crypt(%s, password);""", (userData['username'], userData['password']))
    #query1 = cursor.mogrify("""select * from users WHERE username = %s AND password = 'crypt(%s, password)';""", (userData['username'], userData['password']))
    query = cursor.fetchone()
    if query is not None:
        # Here we get the messages from the database and pass them to client side
        tempArray = retrieveMessagesFromDB()
        print tempArray
        emit('userAuthorized', query["uuid"], namespace='/iss')

        for message in tempArray:
            // emitted to client side to populate the chat room on login of a user
            emit("populateMessagesArchived", message, namespace='/iss')  
    else:
        emit('userNotAuthorized', namespace='/iss')

#
# retrieves the messages from the database to populate the chat rooms
#
def retrieveMessagesFromDB():
    connect = connectToDB()
    cursor = connect.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute("""select messages.room_num, messages.message, users.username from users join messages on users.uuid = messages.uuid;""")
    query = cursor.fetchall()
    return query

#
# storeMessagesInDatabase - stores messages in the database
#
@socketio.on('storeMessagesInDatabase', namespace='/iss')
def storeMessagesInDatabase(userInfo):
    # userInfo is Json and we set it equal to tempDict
    tempDict=userInfo
    tempArray=[]
    tempArray.append(tempDict["roomNum"])
    tempArray.append(tempDict["message"])
    tempArray.append(tempDict["username"])
    connect = connectToDB()
    cursor = connect.cursor()

    try:
        cursor.execute("""insert into messages (uuid, message, room_num) values (%s, %s, %s);""", (str(tempDict["uuid"]), tempDict["message"], str(tempDict["roomNum"])))
    except:
        connect.rollback()
    connect.commit()   
    # Messages emitted back to client to be outputted on screen
    emit("populateMessagesArchived", tempArray, namespace='/iss', broadcast=True)
    tempArray=[]

【问题讨论】:

    标签: python angularjs events flask socket.io


    【解决方案1】:

    我解决问题的方法是使用 angular-socket-io 库。它提供了一个 forward() 函数来停止事件处理程序的重复,从而解决了消息重复的问题。另外,现在我了解 MPA 会破坏页面切换上的套接字,而 SPA 不会。我建议将 socketio 代码放在一个只会被调用一次的更高级别的模块中。有关更多解释,请参见此处:

    https://*.com/a/36223423/3103677

    【讨论】: