I'm having issues getting PubNub's subscribe message handler to fire. I'm working on a web client that will listen for messages from mobile apps. Up until recently, this code worked fine. I could send a message from my phone and see the web app get auto-updated. But in the last few days, the web app is no longer getting updated.
It's an Angular app that I've been writing in CoffeeScript. I have a MessageService that handles all the bootstrapping for PubNub. The subscribe method of my service is passed an entity id arg to set as the channel name to listen on, and passes a function reference via the messageHandler argument.
angular.module('exampleApp').service 'MessageService', ($http, $interval) ->
pubnub = null
subscribePromise = null
config =
subscribe_key: 'demo'
# Sanity check. This gets triggered upon connection with the correct
# channel name/entity id.
connectionHandler = ->
_.forOwn arguments, (arg) -> console.log arg
return {
getChats: (id) ->
# Calls an API to fetch all of the chat messages. These aren't transmitted over
# PubNub because we do other fun things to adhere to HIPAA compliance.
return $http.get 'path/to/api/endpoint/' + id
subscribe: (id, messageHandler) ->
pubnub = pubnub or PUBNUB.init config
pubnub.subscribe({
channel: id
message: (data) ->
if not not subscribePromise
$interval.cancel subscribePromise
subscribePromise = null
messageHandler data
connect: connectionHandler
})
# Interval-based workaround to function in spite of PubNub issue
subscribePromise = $interval messageHandler, 10000
}
Here's an example of the messageHandler implementation in one of my controllers.
angular.module('exampleApp').controller 'MessageCtrl', (MessageService) ->
$scope.messageId = 'some entity id'
# This message handler never gets fired, despite passing it to pubnub.subscribe
onMessageUpdated = (data) ->
console.log data
MessageService.getChats($scope.messageId).then (messages) -> $scope.messages = messages
MessageService.subscribe $scope.messageId, onMessageUpdated
Like I mentioned, this code was working not long ago, but out of the blue, the message handler stopped firing at all. Haven't touched it in more than a month. The thing that's driving me nuts is that I can open up the dev console in PubNub and watch the messages come in from the phones, but for some reason, that message handler never seems to get called.
I'm using the "edge" version of pubnub.js, so I'm wondering if there was some recent update that broke my implementation. Anything else you folks can see that I may be missing or doing wrong? Any help is appreciated.
// Edit
Just a quick update. I've tried rolling back as far as 3.5.47 and still no change in behavior. I coded a quick workaround using Angular's $interval service to allow the app to at least function while I get this issue figured out. Updated code example above w/ relevant changes.
Quick update. After moving on to some other tasks and circling back to this after a year or so, we decided to take another stab at the problem. The interval-based polling was working fine for our initial implementation as described above, but we now have need for a more robust set of features.
Anyway, we ended up grabbing the latest stable version of the JS client (3.7.21), and so far it appears to have fixed our issue.
Related
I am making a PWA where users can answer the forms. I want it to make also offline, so when a user fills out a form and does not have the internet connection, the reply will be uploaded when he is back online. For this, I want to catch the requests and send them when online. I wanted to base it on the following tutorial:
https://serviceworke.rs/request-deferrer_service-worker_doc.html
I have managed to implement the localStorage and ServiceWorker, but it seems the post messages are not caught correctly.
Here is the core function:
function tryOrFallback(fakeResponse) {
// Return a handler that...
return function(req, res) {
// If offline, enqueue and answer with the fake response.
if (!navigator.onLine) {
console.log('No network availability, enqueuing');
return;
// return enqueue(req).then(function() {
// // As the fake response will be reused but Response objects
// // are one use only, we need to clone it each time we use it.
// return fakeResponse.clone();
// });
}
console.log("LET'S FLUSH");
// If online, flush the queue and answer from network.
console.log('Network available! Flushing queue.');
return flushQueue().then(function() {
return fetch(req);
});
};
}
I use it with:
worker.post("mypath/add", tryOrFallback(new Response(null, {
status: 212,
body: JSON.stringify({
message: "HELLO"
}),
})));
The path is correct. It detects when the actual post event happens. However, I can't access the actual request (the one displayed in try or fallback "req" is basically empty) and the response, when displayed, has the custom status, but does not contain the message (the body is empty). So somehow I can detect when the POST is happening, but I can't get the actual message.
How to fix it?
Thank you in advance,
Grzegorz
Regarding your sample code, the way you're constructing your new Response is incorrect; you're supplying null for the response body. If you change it to the following, you're more likely to see what you're expecting:
new Response(JSON.stringify({message: "HELLO"}), {
status: 212,
});
But, for the use case you describe, I think the best solution would be to use the Background Sync API inside of your service worker. It will automatically take care of retrying your failed POST periodically.
Background Sync is currently only available in Chrome, so if you're concerned about that, or if you would prefer not to write all the code for it by hand, you could use the background sync library provided as part of the Workbox project. It will automatically fall back to explicit retries whenever the real Background Sync API isn't available.
I have the following publisher and subscriber code.
It works for the first time when the app starts, but when I try to insert data directly into the Mongo database, it will not automatically update the user screen or I don't see the alert popping.
Am I missing something?
Publish
Meteor.publish('userConnections', function(){
if(!this.userId){
return;
}
return Connections.find({userId: this.userId});
})
Subscribe
$scope.$meteorSubscribe('userConnections').then(function () {
var userContacts = $scope.$meteorCollection(Connections);
alert("subscriber userConnections is called");
if (userContacts && userContacts[0]) {
....
}
}, false);
First off, if you are not using angular-meteor 1.3 you should be. The API has changed a lot. $meteorSubscribe has been deprecated!
To directly answer your question, $meteorSubscribe is a promise that gets resolved (only once) when the subscription is ready. So, it will only ever be called once. If you look at the documentation for subscribe you'll see how to make the binding "reactive", by assigning it to a scope variable. In your case it would be something like:
$scope.userContacts = $scope.$meteorCollection(Connections);
Doing it this way, when the collection gets updated, the $scope.userContacts should get updated as well.
I am trying to open two views in succession, both as modals in an Appgyver project. when i do supersonic.ui.modal.hide() and supersonic.ui.modal.show(some_view), the second view does not show. If I throw in an alert('here') in between the modal.hide() and modal.show(), it seems to work. What is the problem here? It's the same with supsersonic.ui.layers.pop() and supersonic.ui.layers.push(another_view) in succession.
Usage sample:
}else if (option === 'chooseLocation'){
$scope.currentOption = "location you chose.";
$scope.f = true;
supersonic.ui.modal.hide().then(function(){
supersonic.ui.modal.show("chooseLocation");
$localStorage.locationOption = 'lastUsed';
});
}
another snippet that i tried this morning that's not related to modals, but also doesn't work:
supersonic.ui.layers.popAll().then(function(){
var view = new supersonic.ui.View("searchresults#index?"+paramText);
supersonic.ui.layers.push(view);
});
The error I get on the log screen:
landing#drawer 11:23:29.382 error
"supersonic.ui.layers.popAll rejected: {}"
This seems to be a bug in the Supersonic API.
The Javascript success callback is invoked when the native wrapper has received the API call. This is invalid behaviour. The Javascript success callback should be invoked when the native wrapper has completed the API call (expected behaviour).
As a workaround, use Use Steroids.js events such as didclose to determine when the native API call has completed.
See Steroids modals and Steroids layers for further documentation.
I have filed a new bug in AppGyver Github issues
First off, I just started trying to add SignalR 2 to my existing Angular SPA project.
I have a main controller which starts the hub right away that is feeding some messages to the client. Inside, I have several sub pages and each could subscribe to a different hub for services. The problems is that the client doesn't receive message because it is hooked up after the hub is already started in the main controller.
As a test, if I comment out the hub start in the main controller, the one in the sub controller works fine.
From what I read, it is by design that you have to hook up all client calls before starting the hub. I don't understand...if it is a service, I should be able to subscribe or unsubscribe anytime after the hub is started. Why not? How to workaround?
Because no response in the 12 hours (which is quite unusual in so), I had to dig around myself. I think, I was misled by the answers from SO on related questions that you have to subscribe all client call before starting the connection, as mentioned e.g. here. I found in Hubs API Guide, one section says
Define method on client (without the generated proxy, or when adding
after calling the start method)
So, it is possible to add client method after connection is started. The trick is to use so-called "without the generated proxy". That limitation is for "with generated proxy".
The following is my working example taken from SignalR get started tutorial.
This is the main controller using "with generated proxy":
$.connection.statusHub.client.updateStatus = function (status) {
$scope.status = status;
$scope.$apply();
}
$.connection.hub.start();
This is in a subcontroller using "without generated proxy":
var connection = $.hubConnection();
var proxy = connection.createHubProxy('stockTickerHub');
proxy.on('updateStockPrice', function (stock) {
var st = $scope.stocks.firstOfKey(stock.symbol, 'symbol');
st.lastPrice = stock.lastPrice;
$scope.$apply();
});
var hub = $.connection.stockTickerHub;
connection.start().done(function () {
hub.server.getAllStocks().done(function (stocks) {
$scope.stocks = stocks;
});
});
Note that it doesn't work if I use "with generated proxy" in the subcontroller like this:
var hub = $.connection.stockTickerHub;
hub.client.updateStockPrice = function (stock) {
var st = $scope.stocks.firstOfKey(stock.symbol, 'symbol');
st.lastPrice = stock.lastPrice;
$scope.$apply();
};
$.connection.hub.start().done(function () {
hub.server.getAllStocks().done(function (stocks) {
$scope.stocks = stocks;
});
});
To prove the limitation of "with generated proxy" mode, this code works if I comment out the one in the main controller.
By the way, I was so confused by the term with or without generated proxy in the Guide, and in both cases, it is still called xxxProxy. Can't they find a better name? Or somebody has an explanation?
I have an AngularJS application that I intend to have receive communications via SignalR from the server, most notably when data changes and I want the client to refresh itself.
The following is my hub logic:
[HubName("update")]
public class SignalRHub : Hub
{
public static void SendDataChangedMessage(string changeType)
{
var context = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
context.Clients.All.ReceiveDataChangedMessage(changeType);
}
}
I use the following within my API after the data operation has successfully occurred to send the message to the clients:
SignalRHub.SendDataChangedMessage("newdata");
Within my AngularJS application, I create a service for SignalR with the following javascript that's referenced in the HTML page:
angular.module('MyApp').value('signalr', $.connection.update);
Within the root for the AngularJS module, I set this up with the following so that it starts and I can see the debug output:
$(function () {
$.connection.hub.logging = true;
$.connection.hub.start();
});
$.connection.hub.error(function(err) {
console.log('An error occurred: ' + err);
});
Then I've got my controller. It's got all sorts of wonderful things in it, but I'll show the basics as relate to this issue:
angular.module('MyApp').controller('MyController', function($scope, signalr) {
signalr.client.ReceiveDataChangedMessage = function dataReceived(changeType) {
console.log('DataChangedUpdate: ' + changeType);
};
});
Unfortunately, when I set a breakpoint in the javascript, this never executes though the rest of the program works fine (including performing the operation in the API).
Some additional (hopefully) helpful information:
If I set a breakpoint in the SignalRHub class, the method is successfully called as expected and throws no exceptions.
If I look at Fiddler, I can see the polling operations but never see any sign of the call being sent to the client.
The Chrome console shows that the AngularJS client negotiates the websocket endpoint, it opens it, initiates the start request, transitions to the connected state, and monitors the keep alive with a warning and connection lost timeout. There's no indication that the client ever disconnects from the server.
I reference the proxy script available at http://localhost:port/signalr/hubs in my HTML file so I disregard the first error I receive stating that no hubs have been subscribed to. Partly because the very next message in the console is the negotiation with the server and if I later use '$.connection.hub' in the console, I'll see the populated object.
I appreciate any help you can provide. Thanks!
It's not easy to reproduce it here, but it's likely that the controller function is invoked after the start of the connection. You can verify with a couple of breakpoints on the first line of the controller and on the start call. If I'm right, that's why you are not called back, because the callback on the client member must be defined before starting the connection. Try restructuring your code a bit in order to ensure the right order.