Guide on how to have controller wait for promise to be resolved by an Angular service

Currently, I have a service that initiates an AJAX request to the backend.

Service:

    function RetrieveCompanyDataService(options)
    {
        this.url = '/company';
        this.Companies = undefined;
        this.CompaniesPromise = $http.get(this.url);

    }

Controller:

var CompaniesServiceObj = new RetrieveCompanyData();
CompaniesServiceObj.CompaniesPromise.then(function(data){
   $scope.Companies = data;
});

I am interested in having my service manage the ".then" function rather than the controller. Furthermore, I would like the controller to be able to interact with the data RETURNED by the service after the promise within the service has been fulfilled.

In essence, I aim to retrieve and utilize the data as follows:

var CompaniesServiceObj = new RetrieveCompanyData();
$scope.Companies = CompaniesServiceObj.Companies;

The completion of the promise should be handled internally by the service itself.

Is there a way for me to achieve this? Or am I restricted to accessing the resolution of that promise from outside the service?

Answer №1

If you only need to manage the response from $http within the service itself, you can include a then function in the service to further process and then return from that then function, as shown below:

function RetrieveCompanyDataService(options) {
  this.url = '/company';
  this.Companies = undefined;
  this.CompaniesPromise = $http.get(this.url).then(function(response) {
    /* handle response within the service */
    return response
  })
}

However, you will still need to use a promise in the controller, but the data you receive will have already been handled by the service.

var CompanyData = new RetrieveCompanyDataService();
CompanyData.CompaniesPromise.then(function(dataHandledByService) {
  $scope.Companies = dataHandledByService;
});

Answer №2

It's not a problem to achieve this!

The key thing to remember is to maintain the same object reference (arrays are objects in JavaScript) in your service.

Below is our straightforward HTML:

<div ng-controller = "companiesCtrl"> 
  <ul ng-repeat="company in companies">
     <li>{{company}}</li>
  </ul>
</div>

This is how the service is implemented:

serviceDataCaching.service('companiesSrv', ['$timeout', function($timeout){
  var self = this;

  var httpResult = [
    'company 1',
    'company 2',
    'company 3'
  ];

  this.companies = ['preloaded company'];
  this.getCompanies = function() {
    // simulating an asynchronous operation
    return $timeout(function(){
      // maintaining the array object reference!!
      self.companies.splice(0, self.companies.length);

      // if you use the following code:
      // self.companies = [];
      // the controller will lose the reference to the array object as we are creating a new one
      // consequently, it will not receive the changes made here!
      for(var i=0; i< httpResult.length; i++){
        self.companies.push(httpResult[i]);
      }
      return self.companies;
    }, 3000);                    
}}]);   

And here is the controller set up as requested:

serviceDataCaching.controller('companiesCtrl', function ($scope, companiesSrv) {
  $scope.companies = companiesSrv.companies;
  companiesSrv.getCompanies();
});

Explanation

As mentioned earlier, the key is to maintain the reference between the service and the controller. Once this is done correctly, you can bind your controller scope to a public property of your service without issues.

Here is a fiddle that sums it up.

In the code comments, you can try uncommenting the part that does not work, and you'll see how the controller loses the reference. Essentially, the controller will continue pointing to the old array while the service updates with a new one.

One more crucial point to remember: the $timeout triggers a $apply() on the rootScope, which is why the controller scope refreshes independently. Without this, using a regular setTimeout() would not update the company list in the controller.

To address this issue, you can:

  • do nothing if your data is fetched with $http since it automatically calls $apply on success
  • wrap your result in a $timeout(..., 0);
  • inject $rootScope in the service and call $apply() on it after the asynchronous operation completes
  • add $scope.$apply() in the controller on the getCompanies() promise success

Hope this explanation helps!

Answer №3

If you pass the $scope into GetCompanies and assign $scope.Companies to the data from the service.

 function GetCompaniesService(options, scope)
 {
    this.url = '/company';
    this.Companies = undefined;
    this.CompaniesPromise = $http.get(this.url).then(function(res) {
      scope.Companies = res;
    });

 }

It is important to use the data in the correct order after setting it. This is why promises are used in the first place.

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

Generating a fresh array of unique objects by referencing an original object without any duplicates

I can't seem to figure out how to achieve what I want, even though it doesn't seem too complicated. I have an array of objects: { "year": 2016, "some stuff": "bla0", "other stuff": 20 }, "year": 2017, "some stuff": "bla1", ...

Incorporate the npm mqtt package into my Angular application

I'm trying to incorporate the package https://www.npmjs.com/package/mqtt#connect into my Angular project. Since it's not a standard Angular package, I'm unsure of what I need to include in the module and controller to make it function correc ...

"Resetting the state of a form in AngularJS2: A step-by

Looking to reset the form state from dirty/touched in angular? I am currently delving into the world of angular2 and working on a form with validation. In my journey, I came across this code snippet: <form *ngIf="booleanFlag">..</form> This ...

Tips for personalizing the css styles of an alert box?

I am in need of customizing the alert box using CSS attributes. Currently, I am able to achieve a similar effect with the code below: JQUERY: $('<div class="alertMessage">Error first</div>') .insertAfter($('#componentName' ...

I am trying to create a login application using angular and ionic, but I am facing an issue with accessing the $stateProvider routing for the login page through the <ion-nav-view> element

index.html Description: The index.html file is the main file containing all the view pages. It includes HTML code for setting up the structure of the application. <!DOCTYPE html> <html ng-app="loginApp"> <head> <meta charset="u ...

How can I create a computed field in TypeORM by deriving its value from other fields within the same Entity?

My goal is to implement a 'rating' field in my User Entity. Within the User Entity, there exists a relationship with the Rating Entity, where the User has a field called ratingsReceived that eagerly loads all Ratings assigned to that User. The & ...

How can I stop and hover over time in AngularJs Interval?

Within my UI, I have a time element that is continuously updated using AngularJS Interval. Even the milliseconds are constantly running. Is it possible to implement a feature where the time pauses when hovering over it? Any assistance would be greatly appr ...

Using $index in ng-class to dynamically apply form validation styles

Looking to validate an angular form input and add a class if it's not $valid. The catch is, it's nested inside an ng-repeat looping through an array of integers, with the input name based on the $index: <li ng-repeat="item in selectedItem.dat ...

I am seeking assistance with my code. It is passing most of the test cases, but is failing only two without any error messages. What could

I recently started teaching myself programming, so please excuse me if my question seems a bit basic. One of the challenges on CodeCamp requires defining a function that takes an array with 2 values as input and returns the Least Common Multiple (LCM) of ...

WebPack Error: When calling __webpack_modules__[moduleId], a TypeError occurs, indicating that it is not a function during development. In production, an Invalid hook call error

Encountering a WebPack error when utilizing my custom library hosted as a package and streamed with NPM Link. Interestingly, the production version functions flawlessly. Below are my scripts: "scripts": { "dev": "rm -rf build ...

Retrieve variable passed through ajax in PHP

I utilized AJAX to send PHP variables to me in the following manner: <script type="text/javascript"> $(document).ready(function(){ $("#disdiciButton").click(function(){ var log_user = <?php echo json_encode($log_user);?>; ...

AngularJS - Directives cannot pass their class name into inner template

My goal is to create a directive that can apply a class name conditionally. However, I encountered an issue where the code only works if the class name is hardcoded into the class attribute. When I attempt to use it with any expression, it fails to work. ...

Can you explain the process of utilizing Angular databinding to display nested information?

I'm facing a challenge with databinding when working with nested arrays in multiple timeslots and windows. Despite understanding the basics, I can't seem to make it work no matter how I try different approaches. It's really frustrating not k ...

`Exploring AngularJS scopes with the Batarang Chrome extension`

I am curious about AngularJs scopes and how they can be examined using the Batarang Chrome extension. Here is the HTML I have: <!doctype html> <html lang="en" ng-app="myApp"> <head> <meta charset="utf-8"> <title>My A ...

Sending a batch of items through an ajax request

I am faced with a challenge where I have a collection of data stored within multiple objects. Within each object, there is an ID and a status, while the main object contains a type and form id. The issue arises when trying to post the result via ajax as ...

Is it possible for jQuery UI Autocomplete to utilize values from various input fields simultaneously?

I've hit a roadblock with this code and it's been giving me trouble for some time now. Here is the current state of my auto-complete script: jQ19(function(){ // minLength:1 - how many characters user enters in order to start search jQ19 ...

Is there a way to eliminate the mongoose event listener registered onConnect in the Socket.io Leak issue?

Whenever a user connects via socket.io, the following subscribe event is triggered: socket.on('subscribe', function(room) { console.log('joining room', room.id); socket.join(room.id); socket.roomName = room.id; // ...

with every instance of an ajax request sent to a separate file

Currently, I have a forEach function that is printing the "local" column from a specific database: View image here Everything is working well up to this point, and this is the output I am getting: See result here Now, my goal is to send variables via P ...

Retrieve the fully loaded webpage source code in Java as it is displayed by the browser

There are certain webpages that utilize JavaScript and AJAX calls to populate fields during or after the page has loaded. An example can be found at this link, where the content in a size dropdown box is filled using JavaScript. I am wondering if it is po ...

Running an Express middleware function

While watching a tutorial on the Express framework, I came across some interesting code used by the instructor for handling requests. Here is how the route is set up: router.route('/').post(authController.restrictTo('admin', 'l ...