Reputation: 379
I use SignalR for my app, and it works with simple javascript,but now I'm trying to rewrite it with angular-wrapper https://github.com/JustMaier/angular-signalr-hub, but I run into the error while trying to connect to my hub:
Error: SignalR: Connection must be started before data can be sent. Call .start() before .send()
at Object.signalR.fn.signalR.send (http://localhost:47124/Scripts/jquery.signalR-2.1.1.js:789:23)
at Object.hubProxy.fn.hubProxy.invoke (http://localhost:47124/Scripts/jquery.signalR-2.1.1.js:2609:24)
at Hub.invoke (http://localhost:47124/Scripts/angular-signalr-hub.js:30:28)
at Hub.(anonymous function) [as loadRecentPhotos] (http://localhost:47124/Scripts/angular-signalr-hub.js:49:24)
at Object.PhotoMarkers.load (http://localhost:47124/Scripts/AngApp.js:47:17)
at http://localhost:47124/Scripts/angular.js:10836:21
at Scope.$eval (http://localhost:47124/Scripts/angular.js:12673:28)
at pre (http://localhost:47124/Scripts/angular.js:19943:15)
at nodeLinkFn (http://localhost:47124/Scripts/angular.js:6684:13)
at compositeLinkFn (http://localhost:47124/Scripts/angular.js:6098:13) <div class="modal-body" ng-init="markers.load()">
Here is my code:
angular.module('FlickrMaps', ['SignalR'])
.factory('PhotoMarkers', ['$rootScope', 'Hub', function ($rootScope, Hub) {
var PhotoMarkers = this;
//Hub setup
var hub = new Hub('photos', {
listeners: {
'addTotalPhotosCount': function (total) {
PhotoMarkers.totalCount = total;
$rootScope.$apply();
},
'initPhotoMarker': function (photo) {
var photolocation = new window.google.maps.LatLng(photo.Latitude, photo.Longitude);
PhotoMarkers.all.push(new window.google.maps.Marker({
position: photolocation,
title: photo.Title,
photoThumb: photo.PhotoThumbScr,
photoOriginal: photo.PhotoOriginalScr
}));
$rootScope.$apply();
},
'photosProcessed': function () {
PhotoMarkers.processedPhotosCount++;
$rootScope.$apply();
},
},
methods: ['loadRecentPhotos'],
errorHandler: function (error) {
console.error(error);
}
});
//Variables
PhotoMarkers.all = [];
PhotoMarkers.processedPhotosCount = 0;
PhotoMarkers.totalCount = 0;
//Methods
PhotoMarkers.load = function () {
$('#myModal').modal({
backdrop: 'static',
keyboard: false
});
hub.loadRecentPhotos();
};
return PhotoMarkers;
}])
.controller('MapController', ['$scope', 'PhotoMarkers', function ($scope, PhotoMarkers) {
$scope.markers = PhotoMarkers;
}])
Does anyone acquainted with that?
Upvotes: 4
Views: 4222
Reputation: 18922
What helped me when wrapping SignalR for AngularJS use was this:
** Convert all SignalR (jQuery) promises to Angular $q promises. **
The benefits:
All callers have a consistent promise interface (e.g. finally()
rather than always()
)
Promise is rejected on error (no need for try
...catch
), e.g. if connection is not started, as per your question
Angular's $digest
cycle is fired
With that in mind, you could change your Hub.invoke
method to this:
Hub.invoke = function(method, args) {
try {
var jQueryPromise = Hub.proxy.invoke.apply(Hub.proxy, arguments);
return $q.when(jQueryPromise); // <- return as $q promise
} catch(e) {
return $q.reject(e); // <- exception to $q.reject
}
and Hub.connect
to
Hub.connect = function (queryParams) {
...
return $q.when(Hub.connection.start(startOptions)); // <- return $q promise
};
Upvotes: 0
Reputation: 1298
I have a basic working example of this on Github. Hope it helps. While it does use authentication, if this is not important for you. You can simply remove the authentication part.
https://github.com/louislewis2/AngularJSAuthentication
Regards
Louis
Upvotes: 1
Reputation: 4290
This is discussed [here]
vmlf01's comment is:
The hub created has a ".promise" property that corresponds to the return value from ".start()". I initialize my connection using a promise and resolving it in the done() callback when the connection is established, like so:
function initialize() {
var deferred = $q.defer();
foldersHubConnection = new Hub('Folders', {
listeners: folderHubListeners(),
methods: folderServerCalls(),
rootPath: config.eventsServerEndpoint
});
foldersHubConnection.promise.done(function () {
deferred.resolve(foldersHubClient);
});
foldersHubConnection.promise.fail(function (error) {
deferred.reject(new Error("Error connecting to Events Server: " + error));
});
return deferred.promise;
}
Upvotes: 0
Reputation: 48972
You must be running into asynch issues. When you invoke method, the connection is not open yet.
Try fixing your signalr-hub.js
, I think we should use different promises for stop and start.
Hub.disconnect = function () {
Hub.stopPromise = Hub.connection.stop();
};
Hub.connect = function () {
Hub.startPromise = Hub.connection.start();
};
And:
angular.forEach(options.methods, function (method) {
Hub[method] = function () {
var args = $.makeArray(arguments);
args.unshift(method);
return Hub.startPromise.then(function(){
//invoke method only after startPromise is resolved
return Hub.invoke.apply(Hub, args);
}
};
});
Or:
angular.forEach(options.methods, function (method) {
Hub[method] = function () {
var args = $.makeArray(arguments);
args.unshift(method);
var def = $q.def();
Hub.startPromise.then(function(){
//invoke method only after startPromise is resolved
Hub.invoke.apply(Hub, args).done(function(){
def.resolve();
});
}
return def.promise;
};
});
Don't forget to inject $q
into your Hub. This returns an angular promise instead of a jQuery promise. Therefore, angular is aware of the event, we don't need to use $rootScope.$apply()
in your callback.
And:
//Adding additional property of promise allows to access it in rest of the application.
Hub.startPromise = Hub.connection.start();
Upvotes: 3
Reputation: 789
According to the source code of the SignalR-Hub project, the connection should be started when creating a new hub. Have you checked your browser console for possible connection errors?
The library also provides access to the start()
promise. Attach yourself to this promise to check wether the hub has successfully ben connected to your server. Example:
hub.promise
.done(function(){ console.log('connection established'); })
.fail(function(){ console.log('connection error'); });
the promise object is the access to the promise of the signalR start method.
Upvotes: 0