Как правильно привязать свойство сервиса в AngularJS?

У меня есть фабрика, где у меня есть пара предопределенных партнеров (это может быть что угодно, я думал, что это пример, который легко понять). Во время выполнения мы выбираем текущего партнера (основываясь на некоторой логике, которую я здесь пропустил).

angular.module('myApp').factory('PartnersService', function ($location, $log) {

  var partners = {
    firstPartner: {
      name: 'Default Partner',
      id: 1 // just an extra property as example 
    },
    secondPartner: {
      name: 'Other Partner',
      id: 2
    }
  };

  // set default value
  var partner = partners.firstPartner;

  var initPartner = function () {
    // based on some logic (omitted), select partner
    partner = partners.secondPartner;
    $log.log("initPartner should have changed partner to " +  partner.name);
  };

  return {
    initPartner: initPartner,
    partners: partners,
    partner: partner,
  };
});

Затем я хотел бы получить доступ к партнеру как PartnersService.partner и посмотрите, как он меняется, например, из контроллера:

angular.module('myApp').controller('myController',
  function ($scope, $log, PartnersService) {
    // PartnersService.partner is the default partner (firstPartner)
    PartnersService.initPartner();
    // After initPartner, PartnersService.partner is still
    // the default, but I expected it to change
});

Я нашел некоторые обходные пути (по моему мнению... они обходные пути?), Но это кажется мне неестественным, поэтому я хотел бы спросить, есть ли лучший способ.

Смотрите мой полный, рабочий пример на JS Bin. Я прошу прощения, если вы найдете пример немного длинным, но я хотел убедиться, что пользователи Stack Overflow понимают мои проблемы и могут указать, если что-то не так с моим мнением.

Обходной путь 1 (получатель?):

angular.module('myApp').controller('myController', function ($scope, $log, PartnersService) {
  PartnersService.initPartner();
  var partner = PartnersService.getPartner();
  $log.log('I could use a getter: ' + partner.name);
});

angular.module('myApp').factory('PartnersService', function ($location, $log) {

  var getPartner = function () {
    return partner;
  };

  return {
    getPartner: getPartner,
    // ...
  };
});

Обходной путь 2 (гнездо в литерале объекта):

angular.module('myApp').controller('myController', function ($scope, $log, PartnersService) {
  PartnersService.initPartner();
  $log.log('or nest the partner in an extra object literal: '
     + PartnersService.extraObjectLiteral.partner.name);
});

angular.module('myApp').factory('PartnersService', function ($location, $log) {
  var partners = { /*...*/ }
  var extraObjectLiteral = {
    partner: partners.firstPartner
  };

  var initPartner = function () {
    // based on some logic (omitted), select partner
    extraObjectLiteral.partner = partners.secondPartner;
  };

  return {
    extraObjectLiteral: extraObjectLiteral,
    //...
  };
});

3 ответа

Решение

Изменить на:

this.partner = partners.secondPartner;

в вашем методе initPartner. Это решит это.

Что вы действительно делаете, когда делаете

  var partners = { ... }
  var partner = partners.firstPartner;

вы создаете локальные объекты в классе, но они не являются открытыми членами класса. И с

  return {
    initPartner: initPartner,
    partners: partners,
    partner: partner
  };

вы создаете члены класса и копируете значения локальных переменных в члены класса. В вашем методе initPartner вы изменяете локальный объект, но объект класса остается неизменным.

Я хотел бы пойти с подходом использования геттеров и сеттеров, так как он следует шаблону раскрытия модуля, который действительно предпочтителен для поддержки более чистого, более читаемого и понятного кода.

Заводской код выглядит следующим образом.

(function () {
    angular.module('myApp').
        factory('PartnersService', PartnersService);

    function PartnersService($location, $log) {
        var partners = {
            firstPartner: {
                name: 'Default Partner',
                id: 1 // just an extra property as example 
            },
            secondPartner: {
                name: 'Other Partner',
                id: 2
            }
        };
        var partner;

        //Set default value
        setDefaultPartner();

        var service = {
            getPartner: getPartner,
            setPartner: setPartner
        };
        return service;

        /*** Function Declarations ***/

        /* Accessible Functions */

        function getPartner() {
            return partner;
        }

        function setPartner() {
            var selectedPartner = initPartner();

            return selectedPartner;
        }

        /* Private Functions */

        function initPartner() {
            /*
             * Some (omitted) logic to select partner
             */

             partner = partners.secondPartner;
             $log.log("initPartner should have changed partner to " +  partner.name);

             return partner;
        }

        function setDefaultPartner() {
            partner = partners.firstPartner;
        }
    }
})();

Обратите внимание, что единственными открытыми / доступными членами остаются getPartner а также setPartner (который вызывает initPartner) функции.

Контроллер будет следующим.

(function () {
    angular.module('myApp').
        controller('myController', myController);

    function myController($scope, $log, PartnersService) {
        $scope.partner = PartnersService.getPartner();
        $log.log("should be default partner: " + $scope.partner.name);

        $scope.partner = PartnersService.setPartner();
        $log.log("After setPartner which calls initPartner, the current partner is now the " + $scope.partner.name);
    }
})();

Модифицируя свою корзину JS, ниже приводится лог консоли.

"should be default partner: Default Partner"
"initPartner should have changed partner to Other Partner"
"After setPartner which calls initPartner, the current partner is now the Other Partner"

Я не думаю, что вам не хватает ничего очевидного. Оба ваших обходных пути кажутся мне подходящими, за исключением того, что в последнем случае я, вероятно, использовал бы следующую структуру:

var partners = { ... };

var initPartner = function () {
    retObj.partner = partners.secondPartner;
};

var retObj = {
    initPartner: initPartner,
    partners: partners,
    partner: partners.firstPartner
};   

return retObj;
Другие вопросы по тегам