Question

How can I switch out a service on-the-fly and have all components (relying on the service) automatically be bound to the data on the new strategy?

I have a Storage service and two storage strategies, StorageStrategyA and StorageStrategyB. Storage provides the public interface to controllers and other components to interact with:

angular.module('app').factory('Storage', function ($injector) {
    var storage;
    var setStrategy = function (name) {
        storage = $injector.get(name);
    };

    setStrategy('StorageStrategyB');

    return {
        getItems: function () {
            return storage.getItems();    
        }
        // [...]
    };
});

But when the strategy is changed, the two-way binding breaks and the view doesn't update with items from getItems() from the new strategy.

I've created a Plunker to illustrate the problem.

Is there a way to combine the strategy pattern with AngularJS and keep the two-way binding?

Please note that in my actual app I cannot just call Storage.getItems() again after the strategy has been changed, because there are multiple components (views, controllers, scopes) relying on Storage and the service change happens automatically.

Edit:
I have forked the Plunker to highlight the problem. As you can see, the data in the upper part only updates because I manually call Storage.getItems() again after the strategy has been changed. But I cannot do that, because other component - for example OtherController - are also accessing data on Storage and also need to automatically get their data from the new strategy. Instead, they stay bound to the initial strategy.

Was it helpful?

Solution

Javascript works on references. Your array items in app is same reference as items of strategyB items initially with the below statement and when you update the StrategyB items automatically items in your view gets updated(since same reference).

$scope.items = Storage.getItems();

So, when you switch strategy you are not changing the reference of items. It still points to StrategyB items reference.

You have to use the below mechanism to change the reference.

Then you can do something where you can communicate between controllers to change the items reference.

Please find the plunkr I have updated.

$rootScope.$broadcast("updateStrategy");

And then update your item list and others.

$scope.$on("updateStrategy",function(){
  $scope.name = Storage.getName();
  $scope.items = Storage.getItems(); //Here changing the reference.
  //Anything else to update
});

OTHER TIPS

the two way binding is still ok, you have a reference issue. when the AppController set up the $scope.items set to the StorageStrategyB items, then when you switch to StorageStrategyA, the AppController $scope.items is still set to StorageStrategyB items.

angular.module('app').controller('AppController', function ($scope, Storage) {



    Storage.setStrategy('StorageStrategyB');

    $scope.current = Storage.getName();

    $scope.items = Storage.getItems();  

    $scope.setStrategy = function (name) {
        Storage.setStrategy(name);
        $scope.current = Storage.getName();
        $scope.items = Storage.getItems();  
        console.log( $scope.items);
        console.log($scope.current);
    };

    $scope.addItem = function () {
        Storage.addItem($scope.item);
        $scope.item = '';
    };

});

You forgot

 $scope.items = Storage.getItems(); 

nice question :)

plnkr

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top