1

I've been refactoring my app to make use of UI.Router's state.resolve to clean up some controller logic. But then I started running into the following issue: How do you avoid the code-duplication found in fetching dependencies in the resolved assets and also found in updating data inside a controller?

state

$stateProvider.state 'someState', state =
    controller: 'SomeController'
    controllerAs: 'vc'
    resolve: { viewData: [
        '$http'
        '$q'
        '$stateParams'
        'someService'
        ($http, $q, $stateParams, someService)->
           someService.get $stateParams.id
           .then (rsp)-> rsp
           .catch (err)-> $q.reject err
]}

view controller

class SomeController
    constructor: (@$http, @$q, @$stateParams, @someService, @viewData)->
        # non-Coffee FYI - @arg is the same as "this.arg = arg"

    getViewData: ->
        someService.get @$stateParams.id
        .then (rsp)-> @viewData = rsp
        .catch (err)-> $q.reject err

** some service**

class SomeService
    constructor: (@$http, @$q)->

    get: (id)->
        $http.get "/url/#{id}"
        .then (rsp)-> rsp.data?.data
        .catch (err)-> $q.reject err

The state's resolved viewData function is nearly identical to the contoller's getViewData call. That seems awfully redundant. Any tricks on leveraging the same code?

I was even thinking of passing a function back in the resolved object that could be assigned to the controller that it could leverage when it needed to execute the same logic but I couldn't figure out the (non-ng) scope issues.

real life

  1. entering state, resolve object fetches initial viewData
  2. state's controller & UI render with current viewData already set
  3. view controller polls get service to check for updates on viewData every x minutes
jusopi
  • 6,791
  • 2
  • 33
  • 44
  • It depends. Is 'url' the same in all cases? Does $http request in 'viewData' should be executed on every state resolve or only once? – Estus Flask Nov 25 '15 at 20:10
  • After trying to understand what you meant, I noticed I had completely forgotten the service code. This may need a broader example as currently it's extremely trivial. So yes, the URL is the same regardless if it's the *resolve* object calling it or the view controller. In terms of the every vs once question, I'm not sure I follow. It wouldn't be cached if that's what you mean. – jusopi Nov 25 '15 at 20:25
  • Possible duplicate of [Angular Authentication : Avoid multiple resolve for different routes](http://stackoverflow.com/questions/33893836/angular-authentication-avoid-multiple-resolve-for-different-routes) – Estus Flask Nov 25 '15 at 20:31
  • I guess it is pretty much the same case of route resolver. – Estus Flask Nov 25 '15 at 20:32
  • That isn't an issue for me as I am using a parent state to handle any inherited resolved dependencies. This particular `viewData` request is unique to the view, meaning no other route/state would utilize this. The duplication is in how the resolved dependency and the subsequent controller request are nearly identical. Again a very trivial example but I'm trying to be as concise as possible – jusopi Nov 25 '15 at 20:36
  • [The answer](http://stackoverflow.com/questions/33893836/angular-authentication-avoid-multiple-resolve-for-different-routes/33895051#33895051) suits your case precisely. It is a piece of code that should be separated into a service which returns a function. – Estus Flask Nov 26 '15 at 06:04

1 Answers1

1

The common piece of code has to be separated into a service and wrapped in function:

app.factory('viewDataService', function ($http, $q, $stateParams, someService) {
  return function () {
    return someService.get($stateParams.id).then(function(rsp) {
      return rsp;
    })["catch"](function(err) {
      return $q.reject(err);
    });
  }
});

As it was shown here, this way someService.get can be called each time the service is being injected

resolve: {
  viewData: function (viewDataService) {
    return viewDataService();
  }
}
Community
  • 1
  • 1
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • I'm going to mark this as the answer since it is correct, I just wish I could find a better means to encapsulate this when there might be logic that needs to be performed outside of the service call (e.g. pulling $stateParams) but w.o having to pass that logic into the service. – jusopi Nov 27 '15 at 05:17
  • @jusopi Passing parameters from the outside as viewDataService function arguments looks fine to me, this ensures testability. But maybe I misunderstood you, feel free to post a question with current code if you still got issues on the subject. – Estus Flask Nov 27 '15 at 07:37