Sunday 11 June 2017

angularjs - Synchronous service using async service in Angular

I have a link generator service that is able to generate links to specific content types (users' details page, content items' details pages etc).



This service is really easy to use and has synchronous functions:



links.content(contentInstance); // /items/123
links.user(userInstance); // /users/234


I now have to introduce separate routing for logged in user to change from /users/id to /users/me.



The only change I'd need to add to my link generator service is to check whether userInstance.id == loggedInUser.id and return a different route URL. This is not a problem as long as my logged-in user's info would be synchronously available. but it's not...



I have a userService.getMyInfo() that returns a promise. The first time it's called it actually makes a server request but subsequent calls return a resolved promise with already cached data.



So how should I implement my user link URL generation in my link generator service?



Edit



Ok. So to see better what I have at the moment and where I'm having the problem. I'm pretty aware that async will stay async and that it can't be converted to synchronous (and it shouldn't be).



This is some more of my code, that will make it easier to understand.



linkGenerator.user



angular
.module("App.Globals")
.factory("linkGenerator", function(userService) {
...
user: function(userInstance) {
// I should be calling userService.getMyInfo() here
return "/users/{0}/{1}".format(userInstance.id, userInstance.name);
},
...
});


userService.getMyInfo



angular
.module("App.Globals")
.service("userService", function($q, cacheService, userResource) {
...
getMyInfo: function() {
if (cacheService.exists("USER_KEY"))
// return resolved promise
return $q.when(cacheService.get("USER_KEY"));

// get data
return userResource
.getCurrentUser()
.$promise
.then(function(userData) {
// cache it
cacheService.set("USER_KEY", userData);
});
},
...
});


Controller



angular
.module("App.Content")
.controller("ItemDetailsController", function(linkGenerator, ...) {
...
this.model = { ... };
this.helpers = {
...
links: linkGenerator,
...
};
...
});


View



View uses ItemDetailsController as context notation.



...
ng-bind="::item.author.name">

...


Notes



As you can see my view generates links to item authors. The problem is that my linkGenerator (as you can see from the code may not have the information yet whether it should generate one of the correct links to user details view.



I know I can't (and don't want to) change my async code to synchronous, but what would be the best way to make this thing work as expected?



One possible solution



For the time being I've come up with a solution that does the trick, but I don't really like it, as I have to supply my logged in user's ID to linkGenerator.user(userInstance, loggedInUserId) function. Then I set up my routing so that I add resolve to my route where I call userService.getMyInfo() which means that my controller is not being instantiated until all promises are resolved. Something along this line:



routeProvider
.when("...", {
templateUrl: "path/to/my/details/template",
controller: "ItemDetailsController".
controllerAs: "context",
resolve: {
myInfo: function(userService) {
return userService.getMyInfo();
}
}
})
...


Then I also add an additional helper to my controller



this.helpers = {
...
links: linkGenerator,
me: myInfo.id,
...
};


And then I also change link generator's function by adding the additional parameter that I then supply in the view.



linkGenerator.user = function(userInstance, loggedInUserId) {
if (userInstance.id === loggedInUserId)
return "users/me";
return "users/{0}/{1}".format(userInstance.id, userInstance.name);
}


and in the view





And I don't to always supply logged in user's ID. I want my service to take care of this data on its own.

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...