Skip to content

Handle more than one namespace #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 12, 2019

Conversation

focux
Copy link
Contributor

@focux focux commented Apr 13, 2019

The following PR contain all the changes needed in order to be able to handle more than one namespace or connection. This PR doesn't add breaking changes, so it won't be a problem for the people who's already using this package.

To be able to use several namespace and to avoid adding breaking changes I added a key to the plugin option object called useConnectionNamespace, what this does is telling the plugin that you are going to be using more than one namespaced connection and you want to put every connection in their own $socket key. So, supposing that useConnectionNamespace option is true and you are connecting to ws://localhost/chat, your connection object will be in Vue.prototype.$socket.chat.

If useConnectionNamespace is false, it will work as it is right now.

@MetinSeylan
Copy link
Owner

MetinSeylan commented Apr 15, 2019

hi @focux thanks for pr, can you use options param instead of useConnectionNamespace and can you update readme docs for this feature then we can merge it, thanks

@focux
Copy link
Contributor Author

focux commented Apr 15, 2019

By using options object, you mean to put useConnectionNamespace inside that object?

@MetinSeylan
Copy link
Owner

yes

@focux
Copy link
Contributor Author

focux commented Apr 16, 2019

Awesome. I just pushed the changes.

@dxh9845
Copy link

dxh9845 commented Apr 19, 2019

Hello,
This sounds exactly like what I need, but I'm not quite sure how this would be used. Could I have an example of how this would work for multiple namespaces?

@focux
Copy link
Contributor Author

focux commented Apr 21, 2019

Of course. If you have more than one namespace, you would use like this, e.g:

Vue.use(
  new VueSocketIO({
    connection: `ws://localhost/myNamespaceOne`,
    options: { useConnectionNamespace: true },
    debug: false,
    vuex: {
      store,
      actionPrefix: 'SOCKET_SOMETHING_',
    },
  }),
);

// another connection
Vue.use(
  new VueSocketIO({
    connection: `ws://localhost/myNamespaceTwo`,
    options: { useConnectionNamespace: true },
    debug: false,
    vuex: {
      store,
      actionPrefix: 'SOCKET_ANOTHER_',
    },
  }),
);

Then you listen each connection events using their respective action prefix. Also, each connection will be in the $socket object under their namespace name: e.g. Vue.$socket.myNamespaceTwo

@waylaidwanderer
Copy link

Adding an option to set a custom name would be nice, for the use-case where you're connecting to socket servers on separate domains. Or if the namespace has a dash in it, you would have to do something like this.$socket['my-namespace'].emit('foo') which would be good to avoid.

@waylaidwanderer
Copy link

FYI, I tried using your pull request, and the way your code works now is pretty much non-functional.
For example, the following code breaks any socket.io functions (for example, calling this.$socket.namespace.connect():

    Vue.prototype.$socket = {
        ...Vue.prototype.$socket,
        [namespace]: this.io
    };

using the spread operator stops this.$socket from being a Socket class object and instead becomes just a plain object.

Vue.prototype.$vueSocketIo is also always replaced every time you do Vue.use(new VueSocketIO(...)), which is undesirable behavior.

Lastly, all events are put into the same event listener, so if you have same event names from different namespaces, they will overwrite each other (this.$vueSocketIo.emitter.addListener() and this.$vueSocketIo.emitter.removeListener()), and there's a similar issue with using sockets: {} to listen for events.

I really needed this feature so I did my own fork which fixes these issues, and also adds support for manually-specified namespace names and for using sockets_namespace_name: {}: master...waylaidwanderer:namespaced

I probably won't be making a PR for this but @focux you can feel free to copy my changes if you think they're helpful.

@focux
Copy link
Contributor Author

focux commented May 4, 2019

@waylaidwanderer I agree with almost all you said, honestly I did it fast and didn't take that into account.

For example, the following code breaks any socket.io functions (for example, calling this.$socket.namespace.connect():

I don't agree with that though, this.$socket is not a Socket.IO class but indeed this.socket.namespace it is, I don't see why this.$socket.namespace.connect() would break.

This weekend I have some time so I can do the tweaks to the PR, I will give it a check to your PR.

EDIT: This PR was intended to add namespace support for Vuex listeners, not for components. Now you mention that, I will add component support.

@focux
Copy link
Contributor Author

focux commented May 5, 2019

I added namespace support to components, I didn't add it before because I was just using Vuex listeners, but @waylaidwanderer was right in some of the things he said (related to components).

Right now, it should be working as expected with components and also with vuex, keeping also backward compatibility to those who doesn't use namespaced connections. In case someone wants to use my fork until @MetinSeylan can review the PR, I built the code and pushed it, this way you can install it through npm.

@LionelPaulus
Copy link

Hey @focux, did you find a solution to be able to set a custom name, like @waylaidwanderer asked? #207 (comment)

@focux
Copy link
Contributor Author

focux commented May 5, 2019

@LionelPaulus Thanks for pointing that out. I just added a property to the options object called namespaceName, this way you can set the name you want for the namespace, if this option is not setted it will automatically detect the namespace name from the Socket.IO url.

@LionelPaulus
Copy link

@focux Thanks! 🚀

And how can I specify which namespace I would like to use when I listen socket events from component side like that:

new Vue({
    sockets: {
        connect: function () {
            console.log('socket connected')
        },
        customEmit: function (data) {
            console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
        }
    }
})

@focux
Copy link
Contributor Author

focux commented May 5, 2019

@LionelPaulus This how you use it, supposing that the namespace name is myCustomNamespace:

new Vue({
  sockets: {
    myCustomNamespace: {
      connect: function () {
        console.log('socket connected')
      },
      customEmit: function (data) {
          console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
      }
    }
  }
})

@waylaidwanderer
Copy link

I don't agree with that though, this.$socket is not a Socket.IO class but indeed this.socket.namespace it is, I don't see why this.$socket.namespace.connect() would break.

@focux you're right, I meant to write this.$socket.connect(). The reasson I brought this up is because I wrote my code to be backwards compatible - it actually supports using 1 non-namespaced socket instance with namespaced socket instances, as long as you declare the non-namespaced one first. You don't need to update any of your component code (e.g. changing sockets: {} to sockets: { namespace: {} }) as it simply adds a socket_namespace: {} if you're using namespaces.

@focux
Copy link
Contributor Author

focux commented May 6, 2019

@waylaidwanderer Awesome! My fork is also backward compatible. I just hope that @MetinSeylan have the time to review this PR.

@waylaidwanderer
Copy link

@focux Yup, I can see that, as long as you only use namespaced or non-namespaced sockets only. However on your fork you can't use both namespaced and non-namespaced sockets together which may be an issue.

@focux
Copy link
Contributor Author

focux commented May 7, 2019

@waylaidwanderer Actually, you can use it, the only thing is that you will have to add namespaceName key in the options object, this way we put the socketio inside that property.

@waylaidwanderer
Copy link

@focux Ah yes, that would work as well. Good work 👍

You may want to update the documentation about namespaceName and caveats like that though.

@focux
Copy link
Contributor Author

focux commented May 7, 2019

@waylaidwanderer I'm only waiting for @MetinSeylan before I go ahead with the docs, he hasn't said anything so I don't want to work on it if this is not going to be merge haha.

@databasedav
Copy link

databasedav commented May 23, 2019

How do you use a socket.io callback function under a mutation listener (e.g. SOCKET_function) with this?

@focux
Copy link
Contributor Author

focux commented May 26, 2019

@gitavi You have to set an unique prefix for your namespaced connection, using the vuex property in the options objects, e.g. mutationPrefix: "SOCKET_myUniqueName_" and then you listen to events in your mutations like this e.g. SOCKET_myUniqueName_function

@databasedav
Copy link

@focux sorry I meant acknowledgement callbacks.

@focux
Copy link
Contributor Author

focux commented May 26, 2019

@gitavi e.g. this.$socket.myNamespace.emit('event', callback)

@databasedav
Copy link

@focux like given I have a vuex mutation listener SOCKET_myUniqueName_function, is the callback just another argument to the listener like

SOCKET_myUniqueName_function (state, payload, callback) {
  ...
  callback()
}

?

@focux
Copy link
Contributor Author

focux commented May 26, 2019

@gitavi I think you have a misconception with socketio. You receive an acknowledge after you emit an event, I don't really understand what you are trying to do.

@databasedav
Copy link

The acknowledge is a function on the server side that I should be able to call with arguments from the client like in the example I linked:

io.on('connection', (socket) => {
  socket.emit('an event', { some: 'data' });

  socket.emit('ferret', 'tobi', (data) => {
    console.log(data); // data will be 'woot'
  });

  // the client code
  // client.on('ferret', (name, fn) => {
  //   fn('woot');
  // });

});

On the server I do this.$socket.myNamespace.emit('event', callback) as you said and I would like to call the callback under the my mutation listener SOCKET_myNamespace_function just like the client code in the example calls the callback (data) => {console.log(data)} with fn('woot'). Let me know if this makes it clearer!

@ing200086
Copy link

Does anyone have a minimum working product of this on github? I am trying to understand how to have this and allow my components to listen to different rooms. This seems to be down the right path, but I get a function error if I try and put @focux 's code from the 5th in my component. I'd share my code so we could show what not to do... but somehow think it would be better to show what you are supposed to do. :)

@focux
Copy link
Contributor Author

focux commented Jul 13, 2019

@ing200086 Hey! Can you show us how you are trying to do it and also, are you trying to subscribe to the events through components or vuex?

@ing200086
Copy link

ing200086 commented Jul 15, 2019

@focux Thanks for the quick reply. I am using it on the vue object level (not showing the namespace example here since it wasn't working and I had to get some working code).

import Vue from 'vue';
import Task from "./vue/Task.vue";
import SocketSlider from "./vue/SocketSlider.vue";
import VueSocketIO from 'vue-socket.io'
import socketio from "socket.io-client";
import SocketTimer from "./vue/SocketTimer";
import SocketRoomExample from "./vue/SocketRoomExample.vue";

Vue.use(new VueSocketIO({
    debug: true,
    connection: socketio.connect('/', { }),
}));

new Vue({
    el: '#root',
    components: {
        'task': Task,
        'socket-slider': SocketSlider,
        'socket-timer': SocketTimer,
        'socket-room-example': SocketRoomExample
    }
});

Then in the component (SFC) I would attach the subscribers as such:

<template>
    <div>
        <div class="input-group mb-5">
            <input type="text" v-model="roomName">
            <button @click="joinRoom">Join</button>
        </div>
        <div class="input-group mb-5">
            <input type="text" v-model="message">
            <button @click="sendMessage">Send</button>
        </div>
        <div>
            <li v-for="m in messages" v-text="m"></li>
        </div>
    </div>
</template>

<script>
    export default {
        name: "SocketRoomExample",
        data: function() {
            return {
                roomName: "",
                message: "",
                messages: [],
            }
        },
        sockets: {
            'getMessage': function(data) {
                // text messages given to the room. It would be nice to allow different getMessage listeners to different namespaces.
                this.messages.push(data['msg']);
            }
        },
        methods: {
            joinRoom() {
                this.$socket.emit('join_room', this.roomName)
            },
            sendMessage() {
                this.$socket.emit('room_message', {'room': this.roomName, 'msg': this.message })
            }
        }
    }
</script>

<style scoped>

</style>

I'd like to be able to use namespaces to deconflict listeners (I assume this is really the use of namespaces). This code is just example code. Would be curious how to be able to add additional namespace listeners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants