hagsteel / swampdragon

swampdragon
Other
557 stars 74 forks source link

[frontend] How to handle arriving messages published to concrete channel #192

Open aantipov opened 8 years ago

aantipov commented 8 years ago

Hi I'm a frontend developer. Backend sends a lot of messages published to different channels. I can see from browser console the message arrived has channel property. But the problem is a callback passed to swampdragon.onChannelMessage doesn't get that channel information. It gets strange channels list instead. So when a message arrives I can't figure out the channel it was published to and therefore handle it properly. I found the code where that channel info is stripped off https://github.com/jonashagstedt/swampdragon/blob/master/swampdragon/static/swampdragon/js/dist/swampdragon.js#L261

if ('channel' in e.data) {
  var channel = swampDragon.channels[e.data.channel];
  delete(e.data['channel']);
  swampDragon.settings.onchannelmessage(channel, e.data);
  return;
}

So my question is how to figure out what channel the message arrived was published to in order to be able to handle the message properly?

AlexejStukov commented 8 years ago

You should be able to access the channel information by using

swampdragon.onChannelMessage(function (channels, message) {
    for(var i in channels) {
        if (channels[i] == "your_channel") {
            // your code goes here
        }
    }
}

and "your_channel" is set by the swampdragon.subscribe("your_route", "your_channel", ...) function. Your backend developer sets "your_route" in the Router as route_name = "your_route". Hope that helps you out.

aantipov commented 8 years ago

@AlexejStukov A simple example: in my controller I have three subscriptions:

swampdragon.subscribe("my_route", "my_channel_1", ...)
swampdragon.subscribe("my_route", "my_channel_2", ...)
swampdragon.subscribe("my_route", "my_channel_3", ...)

then if I have the suggested solution:

swampdragon.onChannelMessage(function (channels, message) {
    for(var i in channels) {
        if (channels[i] == "my_channel_1") {
            callback_1();
        }
        if (channels[i] == "my_channel_2") {
           callback_2();
        }
        if (channels[i] == "my_channel_3") {
           callback_3();
        }
    }
}

Then each of the callbacks callback_1, callback_2 and callback_3 will always be called on each message regardless of the channel value passed by the server, because channels property is just an array of channels defined in swampdragon.subscribe calls.

AlexejStukov commented 8 years ago

@aantipov Ah, now I see your problem. You are subscribing to the same route with 3 different channels. If you want to handle different kinds of messages you have 3 possibilities:

// ...
if (channels[i] == "my_channel") {
    if ("something" in message.data) {
    // your code
    }
}
swampdragon.subscribe('your_route', "channel_1", {"something":"value_1"}, function (context, data) {
    // subscription success
    }, function (context, data) {
    // subscription fail
});

I would use the first option, if it's the same source sending you different kinds of information, the second approach, if it's the same kind of data, but from different sources and the third if you have different sources. Also you can mix and match that to your liking.

aantipov commented 8 years ago

@AlexejStukov Thanks for helping. I do use the first option as a workaround. But I don't like this, because I can see that our server does send the channel value and I can't use it. Why does swapdragon use channels at all if it prevents onChannelMessage callback from using vital channel info sent by a server? Why does swampdragon substitute channel info sent by a server for a useless channels list, that I construct by myself? I can't understand that logic of swampdragon? What were the ideas behind this logic?

AlexejStukov commented 8 years ago

@aantipov It enables you to bundle several server_channels to one channel. I think it is intended to use get_subscription_contexts(self, **kwags) or get_subscription_channels(self, **kwargs) or two different routes, depending on your situation. But I'm not quite sure of that. You might want to ask @jonashagstedt.

AlexejStukov commented 8 years ago

@aantipov As an example I use

swampdragon.open(function() {
    {% for object in object_list %}
    swampdragon.subscribe('object-route', "object", {"pk":"{{ object.pk }}"}, function (context, data) {
        }, function (context, data) {
    });
    {% endfor %} {# edit #}
});

in my template and

def get_subscription_contexts(self, **kwargs):
    obj = self.get_object(**kwargs)
    return {"object": kwargs["pk"]}

in my routers.py. With this you can also add authentication (with swampdragon-auth) pretty easy (with an if in both files).