0

I am making a web page with ui-router. Before we enter the controller, I want some operations to be done: 1) create a temporary folder in the server and write some files; 2) record the name of the folder and some other data. So naturally I choose to use resolve.

.state('panels', {
    controller: 'PanelsCtrl',
    resolve: {
        init: ['codeService', function (codeService) {
            return codeService.init()
        }]
    },
    ...
});

app.service('codeService', ['$http', function ($http) {
    var srcP = "default" // private variable
    this.getSrcP = function () { return srcP };

    this.init = function () {
        return $http.post("/writeFiles", ...) // write files in a temporary folder of the server
            .then(function (res) {
                srcP = res.data;
                return srcP
            }
    }
};

app.controller('PanelsCtrl', ['$scope', 'codeService', 'init', function($scope, codeService, init) {
    $scope.src = codeService.getSrcP();
    ...
}

The above code works. However, I feel odd about 1) I resolve an asynchronous function (ie, init) rather than data (that people usually resolve); 2) I use a side effect of init to record data (ie, srcP) in a service.

It seems that, in comparaison with resolving data, it is easier when we have more data to be recorded, we just need to have more private variables and make more side effects in the service.

Does anyone know if what I do is a bad/common/good practice? Additionally, why codeService in resolve: {...} and codeService injected to PanelsCtrl share same private variables?

SoftTimur
  • 5,630
  • 38
  • 140
  • 292
  • 1
    Most of angular guidelines says that resolve promises inside the services isn't a good practice. In your case you can resolve the promise inside the resolve of `panles` state, and instead of inject `init` in your controller, you can inject `srcP` directly. But my question is, the `srcP` is just for `PanelsCtrl` or is a common value that you are sharing to others controllers? Anyway, in some cases you can break the guidelines for benefit of your solution :) – The.Bear Mar 05 '17 at 03:24
  • `srcP` is just for `PanelsCtrl`, and there are other values I want to record too. Should I inject an object containing all the values to `PanelsCtrl`? – SoftTimur Mar 05 '17 at 03:27
  • One thing I don't understand is why `codeService` in `resolve: {...}` and `codeService` injected to `PanelsCtrl` share same private variables? are they actually one same object? – SoftTimur Mar 05 '17 at 03:30
  • 1
    To your last question, yes... the `codeService` is a singleton. Check this [post](http://stackoverflow.com/questions/23074875/angularjs-factory-and-service) to see the services and factory details. – The.Bear Mar 05 '17 at 03:33
  • Respect to "there are other values I want to record too", these values came form others endpoints? Or you can obtain all information after http post to `/writeFiles`? – The.Bear Mar 05 '17 at 03:36
  • Actually, `this.init` is more complicated than just `/writeFiles`, but yes, all the values can be obtained from different stages of `this.init`. I think I need a rule to decide which values should be injected to `PanelsCtrl` and which values should be kept as private variables of `codeService`. – SoftTimur Mar 05 '17 at 03:40

2 Answers2

1

I think that's a better approach if you clean up the service and don't resolve the promise inside it.
You can let UI-ROUTER to resolve the promise and inject the result data in the controller...

.state('panels', {
    controller: 'PanelsCtrl',
    resolve: {
        init: ['codeService', function (codeService) {
            return codeService.init()
        }]
    }
});

app.service('codeService', ['$http', function ($http) {
    this.init = function () {
        return $http.post("/writeFiles");
    }
};

app.controller('PanelsCtrl', ['$scope', 'init', function($scope, init) {
    $scope.src = init; //init is the resolved data of codeService...
}

I've just made an example. Check this jsFiddle.

The.Bear
  • 5,621
  • 2
  • 27
  • 33
  • `Most of angular guidelines says that resolve promises inside the services isn't a good practice` ==> I finally understand what `resolve promises inside the services` refers to, do you have any link of these guidelines? – SoftTimur Mar 05 '17 at 09:55
  • @SoftTimur It is unlikely that you will see a guide that says this explicitly. It's just a direct consequence of how resolvers work. They unwrap a promise and inject its value. – Estus Flask Mar 05 '17 at 13:06
  • 1
    Check this guideline [Traps, anti-patterns and tips about AngularJS promises](http://blog.ninja-squad.com/2015/05/28/angularjs-promises/) – The.Bear Mar 05 '17 at 15:23
0

I think theoretically there's nothing wrong causing side effects in resolve, that's what it's there for. Take Restangular as an example. You keep calling for resources in resolve, the cacheing is handled by Restangular, which is basically a side effect.

However, I see some problems with the server-side approach. Storing files on the server is usually a bad idea (think about scaling/compromised immutability of the infrastructure). So I'd rather utilize the DB for this, and you could turn your folder abstraction to a DB resource.

fodma1
  • 3,485
  • 1
  • 29
  • 49
  • I just write data from DB to a temporary folder in the server for temporary uses, and I clean the folder regularly. – SoftTimur Mar 05 '17 at 09:49