1

I think I've a flaw in the design of my AngularJS app.

I have a service which contains one of my model items and the methods associated with adding/deleting them. This service is injected into my core app and also into a directive. My issue is removing items in the model from the directive. I am doing this because the directive physically represents each of the model items. The change is happening, but its not pushing its way back to the core app which exposes the model. I realize that this is because i'm not telling it to update, but my issue is I don't really understand how to. Adding $apply in the directive after calling the service causes an error that an apply is already in progress, so it feels like this is the wrong approach.

I've put my model in the service as I only want a single instance of this data for the entire app.

Can anyone suggest a better way of achieving this? Is storing the model in the service a good idea?

Service:

angular.module("App").service("widgetService", function () {
    this.widgets = [];

    this.removeWidget = function() {
         <!-- remove item from this.widgets -->
    }

    this.getWidgets = function() {
        return this.widgets;
    }

    this.setWidgets = function(widgets) {
        this.widgets = widgets;
    )
}

Core app:

App.controller("CoreAppController", function($scope, $http, widgetService) {
    $scope.widgets = widgetService.getWidgets();
}

HTML:

<widget data-ng-repeat="widget in widgets" />

Directive:

myKSS.MyKSSApp.directive("widget", function($compile) {
    return {
        restrict: "E",
        replace: true,
        templateUrl: "partials/Widget.html",
        scope: {
        },
        controller: function($scope, $element, $attrs, widgetService) {
            $scope.removeWidget = function() {
                widgetService.removeWidget();
            };
        }
    }
}
jwest
  • 735
  • 3
  • 11
  • 20
  • You might simplify your life by using $rootScope instead. It's generally not recommended to use a service as data Storage. $rootScope is already part of the model for the page, and should update everything when you alter it. – Ben Lesh Feb 05 '13 at 14:39
  • Hmmm, thanks. So to ask a possibly stupid question, why is it not recommended to store data in a service? I can see why $rootScope would be a sensible alternative though. – jwest Feb 05 '13 at 14:42
  • Mostly because $rootScope is geared specifically for storing data that you can persist to all of your controllers/views. It's not that you "should never", it's just that there's usually no advantage to doing so outside of maybe some isolation/privatization. – Ben Lesh Feb 05 '13 at 14:54
  • I do remember reading about this in the Angular docs, but I'm struggling to find the page. – Ben Lesh Feb 05 '13 at 14:54
  • Makes perfect sense. One reason I did this was because I read another article that stored model data in a service :-) I guess everyone does it differently, but I want to do this the "right" way and you're quite right that $rootScope serves exactly that purpose. Thanks for your input - every day i'm picking up new stuff about Angular from you guys! – jwest Feb 05 '13 at 15:08
  • 2
    I wouldn't recommend to use `$rootScope`. One of the reasons is violation separation of concerns: imagine you've added `widgets` to your $rootScope in one module and you want to have another module that would have other `widgets`. They will both "polute" rootScope. Other reason is that it would be hard to track dependencies. Also it will increase size of rootScope so you'll have different objects mixed in one place... And about storing data in Servicies here is question about that: http://stackoverflow.com/questions/14241900/should-an-angular-service-have-state/ – Valentyn Shybanov Feb 05 '13 at 15:20
  • [It's in the FAQ](http://docs.angularjs.org/misc/faq): "don't create a service whose only purpose in life is to store and return bits of data.". It doesn't really go into any detail as to *why*, sadly, but I've already outlined that. I'm not poo-pooing @ValentynShybanov's answer, it works and is a valid solution, and should probably be accepted... however, "violation of separation of concerns" is a bit of an appeal to authority here... if you're sharing data for the purpose of displaying/altering it throughout your app, that is what $rootScope is for. – Ben Lesh Feb 05 '13 at 15:37
  • 2
    I agree, that if you are *only* storing data, then using `service` is bad idea - in this case `value` is much better. But in @jwest question there could be some logic when adding/removing widget, that could be put to `service`. – Valentyn Shybanov Feb 05 '13 at 16:04
  • Thanks both. This kind of differing opinion is what helps us newbies learn more and more. Great stuff. Just to confirm, my service doesn't just store and manipulate the data, but agreed, this wasn't clear from my question. – jwest Feb 05 '13 at 18:55
  • The more I use Angular the more I see rootScope as just that -- the "root" of all scopes, useful for making event broadcasting and digest cycles work (i.e., a way to find all scopes in the application). I'm leaning toward all data models in services, and hence (non-root) scopes just ["refer to the application models"](http://docs.angularjs.org/guide/scope). @Valentyn, thanks for your interpretation of the FAQ -- I never could make sense of that, since services do seem like a good place to store model data. – Mark Rajcok Feb 06 '13 at 04:55

1 Answers1

0

There is one thing I've notices: you shouldn't give a possibility to change value of widgets variable of service - otherwise you couldn't track adding/removing (I mean this.setWidgets method of service should be removed)

Everything else should work (with some other minor changes): http://plnkr.co/edit/fQMwfI5d7nikjhXTXSC6?p=preview

Valentyn Shybanov
  • 19,331
  • 7
  • 66
  • 59
  • Thanks Valentyn, that certainly does work exactly as I would like it to. However, this is interesting - why would you have adding items in the Controller rather than the Service? Surely this doesn't then encapsulate all the related functionality into the Service? This is essentially telling me that I shouldn't have the model in the Service. – jwest Feb 05 '13 at 14:46
  • I've updated plunker moved actual modification of array to service, so hiding how you add widgets there. – Valentyn Shybanov Feb 05 '13 at 15:00
  • My code prior to posting this question is almost identical. I can see no noticeable differences, but removing items is not updating the view. I will try to work out and post an update plunker if I have any trouble. Thanks. – jwest Feb 05 '13 at 19:13
  • Argh! A simple error in my code ended up with me thinking it wasn't working at all. Issue resolved! – jwest Feb 05 '13 at 20:10