Reputation: 95
I am using UI-Router for an Angular app. I have data which I am pulling through an $http
asynchronous call that is used to create a connection object of sorts. My goal is to have some way of creating this object and making it available as a singleton. Although the chances of it happening are rare, I am looking to avoid timing issues where the socket
property in the Service is assigned asynchronously. When a different state in the router wants to get or create another connection object, it will do so through the singleton so that the same connection can be used instead of creating another connection.
Below is a skeleton of some code that I am attempting to get to work. I'm not sure if something can be done with the resolve
of the state.
Any thoughts or suggestions on how this can be done?
var myapp = angular.module('me', ['ui.router'])
.service('connectionService', ['$http', function($http) {
var socket;
// Somehow call and store result of $http in a singleton
// Would the $http.get get put in some kind of function? If so, how should it get called
// and returned so that a user of the service can get the 'socket' object synchronously?
$http.get("something/that/returns/data")
.success(function (result) {
this.socket= createConnection(result.data);
})
.error(function (err) {
//handle error
})
var getSocket= function() {
if (!this.socket) {
return createNewSocket();
} else {
return this.socket;
}
}
}])
myapp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("home");
$stateProvider
.state('home', {
url: "/home",
templateUrl: "home.html",
resolve: {
socket: // ??? Can something be done here ???
},
controller: function(socket) {
// I can haz socket?
}
})
.state('nextState', {
url: "/next",
templateUrl: "next.html",
resolve: {
socket: // Should be the same socket object as from the home state
},
controller: function(socket) {
// I can haz same socket?
}
})
})
Update
In the process of trying to explain the issue better, I have changed the original code a couple of times. The comments people have left corrected definite errors I was having. I have reverted the above code to the way it originally was, and have added new code below. The issue that I am still having is that the resolve
on the ui-router state isn't resolving before entering the controller. So, when calling an emit()
on the socket
object that is supposed to be resolved, I an error of "Cannot read property 'emit' of undefined". I also still want the socket
objects to be the same object between the states. I would appreciate any other suggestions or comments to steer me in the right direction.
var myapp = angular.module('me', ['ui.router'])
.service('connectionService', ['$http', function($http) {
var socket;
// Somehow call and store result of $http in a singleton
// Would the $http.get get put in some kind of function? If so, how should it get called
// and returned so that a user of the service can get the 'socket' object synchronously?
$http.get("something/that/returns/data")
.then(function (result) {
socket = createConnection(result.data);
}, function (err) {
// handle error
});
var getSocket = function() {
if (!socket) {
// handle error
} else {
return socket;
}
}
}])
myapp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("home");
$stateProvider
.state('home', {
url: "/home",
templateUrl: "home.html",
resolve: {
socket: function(connectionService) {
return connectionService.getSocket();
}
},
controller: function(socket) {
socket.emit("some msg");
}
})
.state('nextState', {
url: "/next",
templateUrl: "next.html",
resolve: {
socket: function(connectionService) {
return connectionService.getSocket();
}
},
controller: function(socket) {
socket.emit("some other msg");
}
})
})
Update 2
Working solution, but this is not as elegant as Muli Yulzari's answer that has been accepted as the correct answer.
I looked into creating my own promise and returning that as something for ui-router to resolve. Based on all of my debugging and stack traces, there is only one connection created among the states and the entering into the controller is deferred until the connection is resolved. So, I believe this is a working solution. I also have a version of the code that handles an onConnect
event on the connection before resolving the promise, but I have left it out for the sake of brevity.
var myapp = angular.module('me', ['ui.router'])
.service('connectionService', ['$http', '$q', function($http, $q) {
var socket;
this.getSocket = function() {
if (!socket) {
var deferred = $q.defer();
$http.get("something/that/returns/data")
.then(function (result) {
socket = createConnection(result.data);
deferred.resolve(socket);
}, function (err) {
// handle error
deferred.reject(socket);
});
return deferred.promise;
} else {
return socket;
}
}
}])
myapp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("home");
$stateProvider
.state('home', {
url: "/home",
templateUrl: "home.html",
resolve: {
socket: function(connectionService) {
return connectionService.getSocket();
}
},
controller: function(socket) {
socket.emit("some msg");
}
})
.state('nextState', {
url: "/next",
templateUrl: "next.html",
resolve: {
socket: function(connectionService) {
return connectionService.getSocket();
}
},
controller: function(socket) {
socket.emit("some other msg");
}
})
})
Upvotes: 1
Views: 351
Reputation: 2569
To my understanding, you want to initialize the socket according to data from $http.get
everytime the socket instance is invalid but do so asynchronously while the app loads.
.service('connectionService', ['$http', '$q', function($http, $q) {
//For caching the socket
var socket;
//Do the first get
tryGetData();
//tryGetData executes $http.get and returns a promise that when fulfilled -
//returns the new socket instance.
function tryGetData(){
return $http.get("something/that/returns/data")
.then(function (result) {
return socket = createConnection(result.data);
}, function (err) {
// handle error
});
}
//Whenever getSocket gets called, it first checks if the socket is instantiated. if not -
//assumes the $http.get failed and tries again to instantiate the socket calling
//$http.get again in the process.
//this function returns a promise for controllers to rely on.
this.getSocket = function(){
if(socket) return $q.resolve(socket);
return tryGetData();
}
}]);
The rest of your code looks fine.
Upvotes: 1