I've been using UI-Router for my Angular application. The data I fetch through an asynchronous $http
call helps in creating a connection object, which I want to make available as a singleton. My aim is to prevent potential timing issues that may arise when the socket
property in the Service is assigned asynchronously. I want other states in the router to access or create another connection object through the singleton, ensuring reusability instead of generating new connections each time.
Below is a basic structure of the code I'm trying to make functional. I'm uncertain if something can be achieved through the state's resolve
method.
Any ideas or recommendations on how this can be accomplished?
var myapp = angular.module('me', ['ui.router'])
.service('connectionService', ['$http', function($http) {
var socket;
// Call and store $http result in a singleton
// Should the $http.get be encapsulated in a function? If so, how should it be invoked
// and returned for synchronous access by a service user?
$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) {
// Accessing socket here
}
})
.state('nextState', {
url: "/next",
templateUrl: "next.html",
resolve: {
socket: // Should reference the same socket object from home state
},
controller: function(socket) {
// Accessing same socket here
}
})
})
Update
In my attempt to clarify the issue further, I've made some changes to the original code a few times. Previous comments helped correct certain errors I encountered. Hence, I reverted the above code back to its initial state and added new code below. However, I'm still facing a challenge where the resolve
within the ui-router state isn't resolving before entering the controller. Consequently, calling an emit()
on the supposed resolved socket
object results in an error stating "Cannot read property 'emit' of undefined." Additionally, I seek to maintain the same socket
object across states. Any suggestions or guidance would be highly appreciated.
var myapp = angular.module('me', ['ui.router'])
.service('connectionService', ['$http', function($http) {
var socket;
// Fetch and store $http result in a singleton
$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
The following solution works, but it lacks the elegance found in the answer provided by Muli Yulzari, which has been accepted as the correct one.
After exploring the creation of a custom promise and utilizing it as a resolution for ui-router, I have confirmed that only one connection is created across states, with entry into the controller being deferred until the connection is resolved. Therefore, I believe this solution effectively addresses the problem at hand. I omitted a version of the code that deals with an onConnect
event on the connection prior to resolving the promise for 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");
}
})
})