Tips for effectively exchanging information among angular services

Currently, I am in the process of refactoring a complex and extensive form that primarily operates within a controller. To streamline the process, I have started segregating related functions into separate modules or services. However, I am grappling with the issue of managing form data effectively without cluttering the controller or having to pass an overwhelming number of arguments to service functions.

My existing approach involves setting variables on the service, then attempting to access this saved data in other services. Unfortunately, this method doesn't seem to be yielding the desired results as injecting the service into another creates a new instance void of any saved values.

To showcase this methodology, here is a plunker demonstrating my implementation: 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();
  }
});

I am trying to avoid something like this:

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

Perhaps I should consider creating a third service to manage and transfer all data between the other services?

Or maybe it would be better to maintain all the data solely within the controller?

Answer №1

Why is the variable on the injected service returning undefined?

When using the let declaration, a private variable is created.

To access the variable, add a getter method:

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;
  }

}]);

Then utilize the getter method:

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

Is there a better way for services to communicate rather than directly setting variables?

I want to avoid clutter like this:

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

One solution is to use an object as an argument for multiple options:

$scope.options = {};

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

The form can then 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... -->

Directly setting variables in one service before computing in another can lead to unwanted coupling that makes code harder to understand and maintain. It's best practice to provide all necessary information to the pricing service in a cohesive manner.

Answer №2

Avoid injecting $scope in your AngularJs development as it is considered outdated. Instead, focus on utilizing components or the controllerAs syntax for a more modern approach.

Controllers should primarily handle data communication between services and your view.

Services are responsible for providing data functions such as retrieving a product or creating a new one, while the controller should manage actions like:

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

or

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

In your view, bind to properties of the product like:

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

When saving a product, submit the entire object back to the service through:

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

And within the controller:

$ctrl.save = function() {
  productService.save($ctrl.product);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

CKEditor5: Unable to access the 'pluginName' property because it is undefined

I am facing a challenge in creating a custom image plugin for CKEditor that can seamlessly integrate with my own image upload system. While trying to set up this plugin, I encountered some difficulties. Oddly enough, the "out-of-the-box" plugins work perfe ...

What are the steps for adding node packages to sublime text?

Is there a way to install node packages directly from Sublime Text instead of using the command line? If so, what is the process for doing this? I'm not referring to Package Control; I'm specifically interested in installing npm packages like th ...

Enhance the styling of elements generated through JavaScript in VueJs with custom CSS

I need help applying CSS to elements that I dynamically created using JavaScript. buttonClicked(event) { console.log(event); let x = event.clientX - event.target.offsetLeft; let y = event.clientY - event.target.offsetTop; let ripples = document.cre ...

Is it possible to utilize v-html with message data in Vue without any limitations on the <title> element?

Currently utilizing Vue.js 2 and my SDK is IntelliJ: I am attempting to bring HTML content into a Vue.js file. The main objective is to include the <title></title> attribute, as it seems that Vue doesn't have direct support for this feat ...

Errors are thrown when utilizing hydration with RTK Query

Looking for a solution with my local API and RTK Query, I've encountered an issue when implementing server-side rendering (SSR). Here's the code snippet I'm working with: const api = createApi({ reducerPath: 'data', baseQuery: ...

What method can I use to replace the status bar from the top?

Is there a way to smoothly slide in and out a <View/> on React Native iOS, similar to the animation sequences shown in the images below? ...

Could you share the most effective method for implementing a live search feature using javascript or jquery?

While attempting to create a live search for a dataset containing over 10,000 rows, I specified the DOM structure that is available. Despite my efforts to check each result after a single input during the live search process, my browser keeps hanging. Is t ...

Troubles with concealing dropdown menu when hovering

I have noticed that this issue has been raised before, but none of the solutions provided seem to fix my problem specifically. The submenu in my menu is not functioning as intended. It should be displayed when hovered over and hidden when not being hovere ...

After successfully executing an AJAX request three times, it encountered a failure

I have implemented a script to send instant messages to my database asynchronously. Here is the code: function sendMessage(content, thread_id, ghost_id) { var url = "ajax_submit_message.php"; var data = { content: content, thread_id: thread_id }; ...

Coldbox handler does not receive the data from AJAX call

In my current project, I encountered a strange issue while making an $.ajax call to a Coldbox handler method. When I dump the rc scope at the beginning of the handler, there's no data in it other than my usual additions on each request. Even more biz ...

Error: Module not found '!raw-loader!@types/lodash/common/array.d.ts' or its type declarations are missing

I encountered a typescript error while building my NEXT JS application. The error message was: Type error: Cannot find module '!raw-loader!@types/lodash/common/array.d.ts' Below is the content of my tsConfig.json file: { "compilerOptions& ...

Encountering issues with AJAX requests using jQuery

Hey there, I'm attempting to make an AJAX call in a C# page and I'm running into some issues. Below is my jQuery code: $(document).ready(function () { $.ajax({ type: "POST", url: "conteudo.aspx/GetNewPost", data: { i ...

Headless Chrome is showing an empty section on the webpage

While running a protractor script to test an Angular page using chromedriver, I have noticed that the results differ when using the "Headless" or "Normal" browser modes. For instance, when employing a "repeater" locator to showcase items in an empty list, ...

Generating numerous div elements with jQuery's zIndex property

While attempting to create a function that runs on the $(document).ready() event and is supposed to generate a new div, I encountered an issue. Whenever I try to create another div with the same DOM and Class attributes but in a different position, a probl ...

Firebase 9 - Creating a New Document Reference

Hey everyone, I need some help converting this code to modular firebase 9: fb8: const userRef = db.collection('Users').doc(); to fb9: const userRef = doc(db, 'Users'); But when I try to do that, I keep getting this error message: Fir ...

Retrieve an HTML element that is a select option with jQuery

I have a select input containing different options as shown below: <select id="myArea"> <option class="myClass_1" style="color:red;" value="1">Area 1</option> <option class="myClass_2" style="color:green;" value="2">Area 2& ...

What is the reason for requiring both a promise and a callback in order to store JSON data in a global variable?

In order to expose fetched JSON data to a global variable, it is necessary to use either a promise or a callback function. However, my current code is utilizing both methods... Currently, I am creating a promise using the .done function in jQuery. Within ...

Developing a MySQL DB-driven Node.js dashboard without the need for a poller

After exploring various StackOverflow posts on the topic, I haven't been able to find a solution that fits my specific situation. We have multiple monitoring instances across our network, each monitoring different environments (such as Nagios, Icinga ...

Transmitting a JSON string to my backend system to insert into my database, but unfortunately, no data is being added

I've been facing a challenging issue with my code Currently, I am attempting to insert an object into my database using jQuery/AJAX. Despite not encountering any errors, the data is not getting added to my DB. Here is the snippet of my JS/JQuery cod ...

Is there a way to configure json-server, when utilized as a module, to introduce delays in its responses

json-server provides a convenient way to introduce delays in responses through the command line: json-server --port 4000 --delay 1000 db.json However, when attempting to achieve the same delayed response using json-server as a module, the following code ...