如何在角度服务之间正确共享数据

时间:2019-04-16 21:51:55

标签: javascript angularjs angularjs-service

我正在尝试重写一个大型复杂的表单,该表单在控制器中执行所有操作。我首先将相关功能分为各自的模块/服务。 我不明白如何在不使控制器拥挤或不要求将过多参数传递给服务功能的情况下维护表单数据。

我当前的方法是在服务上设置变量,然后在其他服务中使用该服务并尝试访问保存的数据。这似乎不起作用。我认为这是因为将服务注入到另一个实例中会创建一个没有所有保存值的新实例。

以下是总结此方法的小插曲:https://plnkr.co/edit/vyKtlXk8Swwf7xmoCJ4q

let app = angular.module('myApp', []);

app.service('productService', [function() {
  let products = [
    { name: 'foo', value: 'foo' },
    { name: 'bar', value: 'bar' },
    { name: 'baz', value: 'baz' }
  ];

  let selectedProduct = null;

  this.getAvailableProducts = function() {
    return products;
  }

  this.setSelectedProduct = function(product) {
    selectedProduct = product;
  }
}]);
app.service('storeService', ['productService', function(productService) {
  let states = [
    { name: 'SC', value: 'SC' },
    { name: 'GA', value: 'GA' },
    { name: 'LA', value: 'LA' }
  ];

  let selectedState = '';

  this.getAvailableStates = function() {
    return states;
  }

  this.setSelectedState = function(state) {
    selectedState = state;
  }

  this.getPrice = function() {
    // This console.log will always return undefined.
    // productService.selectedProduct is not available.
    console.log(productService.selectedProduct);
    if (productService.selectedProduct == "foo" && selectedState == 'SC') {
      return 10;
    }
    return 5;
  }
}]);
app.controller('myController', function($scope, storeService, productService) {
  $scope.name = '';
  $scope.deliveryState = '';
  $scope.selectedProduct = null;
  $scope.price = 0;

  $scope.productSelection = productService.getAvailableProducts();
  $scope.states = storeService.getAvailableStates();

  $scope.productChanged = function() {
    productService.setSelectedProduct($scope.selectedProduct);
    $scope.price = storeService.getPrice();
  }

  $scope.stateChanged = function() {
    storeService.setSelectedState($scope.deliveryState);
    $scope.price = storeService.getPrice();
  }
});

我正试图避免这样的事情:

$scope.price = storeService.getPrice(
     $scope.state,
     $scope.selectedProduct,
     $scope.servicePackage,
     $scope.serviceFee,
     $scope.shippingSelection,
     // etc…
);

我应该创建第三个服务来设置并获取其他服务上的所有数据吗?

我应该只维护控制器上的所有数据吗?

2 个答案:

答案 0 :(得分:2)

why do I get undefined when accessing a variable on the injected service?

The let declaration creates a private variable.

Add a getter for the variable:

app.service('productService', [function() {
  let products = [
    { name: 'foo', value: 'foo' },
    { name: 'bar', value: 'bar' },
    { name: 'baz', value: 'baz' }
  ];

  let selectedProduct = null;

  this.getAvailableProducts = function() {
    return products;
  }

  this.setSelectedProduct = function(product) {
    selectedProduct = product;
  }

  //ADD getter

  this.getSelectedProduct = function() {
      return selectedProduct;
  }

}]);

And use the getter:

  this.getPrice = function() {
    // This console.log will always return undefined.
    // productService.selectedProduct is not available.
    console.log(productService.selectedProduct);
     ̶i̶f̶ ̶(̶p̶r̶o̶d̶u̶c̶t̶S̶e̶r̶v̶i̶c̶e̶.̶s̶e̶l̶e̶c̶t̶e̶d̶P̶r̶o̶d̶u̶c̶t̶ ̶=̶=̶ ̶"̶f̶o̶o̶"̶ ̶&̶&̶ ̶s̶e̶l̶e̶c̶t̶e̶d̶S̶t̶a̶t̶e̶ ̶=̶=̶ ̶'̶S̶C̶'̶)̶ ̶{̶
     if (productService.getSelectedProduct() == "foo" && selectedState == 'SC') {
      return 10;
    }
    return 5;
 }

Update

Should my services be communicating like that or is there a different, more accepted method?

I am trying to avoid something like this:

$scope.price = storeService.getPrice(
     $scope.state,
     $scope.selectedProduct,
     $scope.servicePackage,
     $scope.serviceFee,
     $scope.shippingSelection,
     // etc…
);

One way to avoid this is use an object as an argument to provide multiple options:

$scope.options = {};

$scope.price = storeService.getPrice(
     $scope.selectedProduct,
     $scope.options
);

The form can populate the options object directly:

<select ng-model="options.state">
    <option ng-repeat="state in states">{{ state.name }}</option>
</select><br>

<select ng-model="options.serviceFee">
    <option ng-repeat="fee in feeList">{{ fee.name }}</option>
</select><br>

<!-- //etc... -->

The setting of a variable in one service before computing something in another service creates an undesirable coupling that makes the code difficult to understand, debug, maintain, and test.

Instead all the information needed from the controller should be provided to the pricing service in a coherent manner.

答案 1 :(得分:0)

You should not be injecting $scope, $scope is an outdated way of developing AngularJs and you should look into components or controllerAs syntax.

The controller should only be marshalling data between services and your view.

Services should provide data functions like get a product or create a new product and the controller should be doing things like

$ctrl = this;
$ctrl.product = productService.new();

or

$ctrl.product = productService.get(productId);

Then in your view you bind to properties of the product

<input name="name" ng-model="$ctrl.product.name">

And when you save a product you pass the whole thing back to the service

<form name="productForm" ng-submit="productForm.$valid && $ctrl.save()">

and in the controller

$ctrl.save = function() {
  productService.save($ctrl.product);
}
相关问题