2

Imagine a case where you have a data service which query a database. For working properly you have to deal with a several $scope variables:

$scope.trackerIds, //Array of objects
$scope.dateTo,
$scope.dateFrom,

Is it best: 1) to compute a value object inside the controller then pass it to a service

for example:

var vo = {
    trackers: _.map(trackersIds, function(trackerId, key){
        return {
            trackerId: trackerId,
            geoip : $scope.trackers[trackerId].geoip
        }
    }),
    dates:[$scope.dateFrom.toISOString(), $scope.dateTo.toISOString()],
}

var dataPromise = dataservice.getTracesBuckets(vo);

2) Or send directly the $scope to the service

var dataPromise = dataservice.getTracesBuckets($scope);

In the first case there will be logic in the controller to collect and arrange $scope variables. In the second case the service will be coupled to the controller because he must know about the $scope structure to call the right variables.

I am in a "what is less worth" case, so I wonder if I am not missing something important. I am completely rebuilding my architecture thanks to Johnpapa Angular Style guide but there is no help on this.

dagatsoin
  • 2,626
  • 6
  • 25
  • 54
  • 2
    It is best practice not to use `$scope` for your controller data at all. Use controller-as – LionC Oct 26 '15 at 10:43
  • Yep you are right, I have not implemented that yet. But the problem stays the same, does not? – dagatsoin Oct 26 '15 at 10:44
  • @LionC, Can you provide some justification/Ref material for your comment. It sounds helpful! – Rayon Oct 26 '15 at 10:48
  • Think of service methods as API's. They take specific parameters and responds to them. Its better not to pass the `$scope` to service and good to pass the parameters that are needed for the service method. –  Oct 26 '15 at 10:48
  • 2
    @RayonDabre [I wrote a longer answer about that topic once](http://stackoverflow.com/questions/32755929/what-is-the-advantage-of-controller-as-in-angular/32756679#32756679) – LionC Oct 26 '15 at 10:49
  • @xyz very interesting. I tend to think the same about the analogy with an API. Have you a also a link reference to post as an answer ? Plus, is the $scope variables manipulation in my example is considered as logic code? – dagatsoin Oct 26 '15 at 10:52
  • @DanielN: Sorry, I do not have a reference. IMO, variables manipulation also can be considered as logic code(in case, if you have conditionals to build the object for the service). In my experience, I used have prepare methods in service to create the data for service and only pass the required params to the prepare methods. I like to keep the controller as simple as possible(ie: with out logic, formatters, etc) –  Oct 26 '15 at 11:01
  • @LionC While it might be better to use `controllerAs` instead of `$scope` to be better for the future, but they are just style preference in current state. It is not right to pass `vm` into a service even you use `controllerAs`, just to answer OP's question. – Icycool Oct 26 '15 at 11:17

3 Answers3

4

Services are View-Less components, It's always wrong to pass view-model to a service...

You Must use your Controller to transform view-model in model, then, use the model in service (ex, data access via ajax)...


UPDATE: according to the angular specs, The Controller is a BL-Component, the difference with services is that in controllers we have to put the only-business-logic that the view needs to work.... In service, instead, we have to put all business logic that is view-less... in other words, the controller must only transform the Domain-Model in View-Model and viceversa! In your example you pass the $scope object to the service, doing this you link the service with the view and this is a bad practice, if something changes in the $scope you have to change the service too! Use Ctrl to transform vm in m

Hitmands
  • 13,491
  • 4
  • 34
  • 69
  • So code in a controller to handle some $scope variables is not considered as logic code? (because it is "forbidden" to put logic code in a controller) – dagatsoin Oct 26 '15 at 10:55
  • @DanielN It is business logic which is 'forbidden'. – Estus Flask Oct 26 '15 at 10:58
  • @DanielN All code is expressing logic. It is business logic(the "core" logic of an application) that should be kept out of controllers as much as possible – LionC Oct 26 '15 at 11:00
  • Yep, controller is a Business Logic Component, but the $scope isn't! $scope is intimately linked with the view... You need to use the controller to manipulate the $scope object... By the way, this is just a Design Pattern, you can do everything you want to do! – Hitmands Oct 26 '15 at 11:02
  • I am a bit confused with the two last comments above. You says that controller is a Business Logic Component but @LionC says controllers have to avoid business code. Is the $scope variables manipulation in my example is considered as business code? – dagatsoin Oct 26 '15 at 11:18
  • according to the angular specs, The Controller is a BL-Component, the difference with services is that in controllers we have to put the only-business-logic that the view needs to work.... In service, instead, we have to put all business logic that is view-less... in other words, the controller must only transform the Domain-Model in View-Model and viceversa! In your example you pass the $scope object to the service, doing this you link the service with the view and this is a bad practice, if something changes in the $scope you have to change the service too! Use Ctrl to transform vm in m – Hitmands Oct 26 '15 at 11:30
  • Thx you @Hitmands . This is very clear now. Could you past this comment in your answer so I could accept it. – dagatsoin Oct 26 '15 at 12:52
1

Services should be business-logic units that are unaware of how (or by whom) they are used - so it would be an anti-pattern to pass your whole $scope to a service, when that service should not even know $scope exists.

Instead, define a clear contract for the business logic your service is performing, giving each function exactly the parameters it needs, in the simplest form possible.

In the end this boils down to a basic principle of MVC(/MVVM) - decoupling Model and View as much as possible.

LionC
  • 3,106
  • 1
  • 22
  • 31
0

I think the convenient method is:

couple your code in your controller then pass it to service:

in your "constructor" of controller

var vo = doJob($scope.trackers, $scope.dateFrom, $scope.dateTo)
var dataPromise = dataservice.getTracesBuckets(vo);

and in your function in controller

doJob(trackers, dateFrom, dateTo) {
    var vo = {
        trackers: _.map(trackersIds, function(trackerId, key){
            return {
                trackerId: trackerId,
                geoip : trackers[trackerId].geoip
            }
        }),
        dates:[dateFrom.toISOString(), dateTo.toISOString()]
    }
}
asdf_enel_hak
  • 7,474
  • 5
  • 42
  • 84