Controlling factory JSON data with $http calls using two separate controllers

I'm attempting to retrieve a JSON response from a factory, save it as a variable, and make it accessible from two different controllers.

Below is the code I am utilizing:

storyFactory.js

var story = angular.module('story.services', []);

story.factory('storyAudio', [ '$http', function ($http) {

  var json = {};

  function getJSON(story_id, callback) {  
    $http({
      url: 'https://api.domain.co/get/' + story_id,
      method: "GET"
    }).success(function (data) {
      json = data;
      callback(data);        
    });
  };

  return {  
    getSubaudios: function(story_id, callback) {
      getJSON(story_id, function(result) {
        callback(result);
      });
    },
    getTopbar: function(callback) {
      callback(json);
    }
  };
}]);

StoryCtrl.js

var storyCtrl = angular.module('story', ['story.services']);

storyCtrl.controller('storyCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {

  var data = this;
  data.story = {};

  storyAudio.getSubvideos($stateParams.story_id, function(response) {
    data.story = response;
  });

}]);

TopbarCtrl.js

var topbarCtrl = angular.module('topbar', ['story.services']);

topbarCtrl.controller('topbarCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
  var data2 = this;
  data2.story = {};

  storyAudio.getTopbar(function(response) {
    data2.story = response;
  });    
}]);

The issue lies in the TopbarCtrl response, as I am receiving an empty data2.story when calling it in the HTML.

This is because it lacks a callback for the $http response, causing it to display the var json with the current status, which is an empty object.

Is there a way to load the second controller only when the variable has content?

Thank you in advance.

Answer №1

In this scenario, I recommend loading the data using the getSubaudios method and creating a reference to the data for other controllers to utilize. You can achieve this by implementing the following approach...

story.factory('storyAudio', function($http) {
    var factory = {
        story: {}
    };

    factory.getSubaudios = function(story_id) {
        return $http.get('https://api.domain.co/get/' + story_id).then(function(response) {
            return angular.extend(factory.story, response.data);
        });
    };

    return factory;
})

By utilizing angular.extend() instead of directly assigning a value to the factory's story property, any existing references will be preserved even after the data is loaded.

You can then load the data like this:

storyCtrl.controller('storyCtrl', function(storyAudio) {
    var data = this;
    storyAudio.getSubaudios($stateParams.story_id).then(function(story) {
        data.story = story;
    });
})

Subsequently, you can access the story data via reference in your controller:

topbarCtrl.controller('topbarCtrl', function(storyAudio) {
    this.story = storyAudio.story;
})

Answer №2

My understanding seems correct, but please notify me if it isn't.

There are a couple of issues that I've identified. Firstly, there is a typo present in your StoryCtrl.js file. The function being called is "storyAudio.getSubvideos" whereas it should be "getSubaudios" according to your factory.

Even after correcting the typo, there could still be a potential issue. The timing of the promise return from the initial call is crucial. Since promises operate asynchronously, there is a chance that the "json" variable may not be set before the second controller attempts to access it.

To address this issue, it's essential to ensure that the first call completes before accessing the "json" variable in the service. One approach that comes to mind is to return and store the promise in the service as shown below...

  var dataPromise;
  function getSubaudios(story_id){
        if(!dataPromise){
          dataPromise = $http({
            url: 'https://api.domain.co/get/' + story_id,
            method: "GET"
          });
        }
        return dataPromise;
  }

  return {  
    getSubaudios: getSubAudios
  };

Subsequently, in your controllers, you can simply invoke the service and utilize .then to extract data from the promise upon its return...

storyAudio.getSubaudios($stateParams.story_id).then(function(response){
   data.story = response; //or data2.story = response;
});

For a practical illustration, refer to this plunkr example. I've utilized the $q library to simulate a promise returned from an $http request, showcasing the concept.

Answer №3

Like Phil's response, using Angular extend or angular copy will retain the same references in both controllers. This eliminates the need for watchers in each controller to monitor for changes. There are a few methods available for sharing data between AngularJS controllers, which can be found here.

Alternatively, you can directly bind the object being returned to the update function to ensure the references remain intact.

storyServices.factory('storyAudio', ['$http', function($http) {
  return {
    data: { json: '' },
    getSubaudios: function(story_id) {
      $http.get('http://jsonplaceholder.typicode.com/posts/' + story_id)
        .then(function(response) {
          this.data.json = response.data.body;
        }.bind(this));
    }
  };
}]);

var storyCtrl = angular.module('story').controller('storyCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
    $scope.data = storyAudio.data;
    storyAudio.getSubaudios(2);
}]);

var topbarCtrl = angular.module('story').controller('topbarCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
    $scope.data2 = storyAudio.data;
}]);

View the Plunk example here with added scopes to demonstrate the outcome.


Sidenote:

Consider it misleading to label a non-controller as "storyCtrl" and then assign it a controller of its own:

var storyCtrl = angular.module(...); // This is not a controller.
storyCtrl.controller(...);   // This is the controller!

Another sidenote:

It's advisable to shift from using .success() to .then(successCallback), as the former is an outdated approach. This switch aligns with the standard convention for promises. More information can be found here.

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

Utilizing a loop for setting variable values in JavaScript

Using both JavaScript and JQuery. Let's imagine there is an array called ListArray with various sentences inside. Sounds easy enough. Is there a way to achieve this? var List = for (var i = 0; i < 10; i++) { //iterate over an array here to c ...

Extract URL parameter with AngularJS

I have obtained this URL from the backend and I need to extract only one parameter from here (the game number). https://www.example.com/#/?category=all&provider=all&game=3189&type=fun How can I retrieve the game number (3189) in the controlle ...

What is the best way to enforce a required bindings property in an AngularJS component?

Suppose I have the following component defined: angular.module('myApp').component('myComponent', { templateUrl: 'myComponent.html', bindings: { myPropOne: '<', myPropTwo: '<' ...

What could be causing the issue with script.onload not functioning properly in a Chrome userscript?

I am trying to use a userscript to load another script file on a website. However, I am encountering issues with the js.onload event not working as expected. Here is the userscript file: // ==UserScript== // @name Code highlight // @description ...

What is the best way to dynamically render classes based on conditions in a table using React Bootstrap?

I am looking for a way to dynamically change the color of a class based on the transaction data in a table. import React from "react"; import Table from "react-bootstrap/Table"; import "./TableComponent.css"; const TableComponent = ({ Movement }) =&g ...

Remove specific data from jQuery datatables using data attribute

My jQuery datatable is loaded with data from a database without any filtering by default, providing all the necessary information for users. In addition to the built-in search functionality of jQuery datatables, I have incorporated custom search input fiel ...

How can we eliminate duplicate arrays of objects within a multi-dimensional array using ReactJS and JavaScript?

let galleryItems = [ {id: 1029, name: 'College-Annual-Day.jpg', ext: 'jpg', mime: 'image/jpeg', size: 91153, …}, {id: 1029, name: 'College-Annual-Day.jpg', ext: 'jpg', mime: 'image/jpeg', si ...

Tips for creating a reusable function in React.js?

I have a script that executes on input focus and passes certain values based on a specific logic. I would like to reuse this script for multiple input fields that trigger the focus event. How can I accomplish this? This is my current script: <input ...

Stack the labels of separate datasets on top of each bar in a bar chart using Chartjs: How can this be achieved?

chart.js 4.4.2 chartjs-plugin-datalabels I am aiming to achieve this effect const chartCtr = document.querySelector('#temp-chart1') as HTMLCanvasElement; new Chart(chartCtr, { type: 'line', plugins: [ChartDataLabels], opt ...

Adjust the scroll position when the height of a div is modified

Imagine we have a large div A with a height value and below it are other divs B, C, and more. If the user is viewing divs B or C, and A reduces its height by half, the scrolling position will remain the same. However, divs B and C will move up by that amo ...

Demonstrate the proper implementation of a Stepper component using Material UI in a React.js

I am trying to display a responsive screen using "progressive forms" with React.js and Material Ui. I have implemented the Stepper component for this purpose, but when I click the "Next Button", the button is hidden but the next step content with the text ...

Utilizing JavaScript Objects within AMD modules using RequireJS: A step-by-step guide

A module example is available for testing. define([ 'components/user/list/usersList.require', 'components/user/manage/userManage.require' ], function (usersListRequire, userManageRequire) { "use strict"; var userPath = ...

What is the best way to incorporate code into an Alexa Skill?

I'm encountering significant hurdles in grasping how to transform my Python or JS code into a functional skill for Alexa. I have snippets of code written in Python and partially in JS, yet despite my efforts, I am unable to incorporate it into an Alex ...

Tips for showcasing all values in a nested array

In my Vue application, I am dealing with a nested array where users can select one date and multiple times which are saved as one object. The challenge I am facing now is how to display the selected date as a header (which works fine) and then list all the ...

The reason for setting a variable as void 0 in JavaScript

Currently, I am delving into the libraries referenced in this particular article as well as other sources. There are some truly mind-boggling concepts contained within these resources, one of which is highlighted by the following line: var cb = void 0; I ...

What could be the reason my ng-style isn't functioning as expected?

The challenge I am facing involves setting a style using ng-style. I currently have an array: scope.order = [0, 1, 2, 3, 4, 5] My goal is to utilize this array to define the order within a div: <div class="col-md-3" ng-show="country.usesAddrDistrict" ...

Tips for modifying the audio session category in iOS using React Native

When using React Native, I know that to allow background audio playback (especially for react-native video), I need to adjust the audio session as outlined here in the Apple development documentation. However, I'm unsure of where to integrate the Obj ...

What is the best way to condense a repetitive method declaration and make it more concise?

I'm facing a situation where I have declared similar const values. Here's my current setup... import React from 'react' function Component_a() { const x = 5; const y = 10; const image_a = [...Array(x)].map((e, i) => ...

The form is unable to detect invalid fields because of a nested view and inherited model structure

Currently in my project, I am utilizing Angular UI's ui-router for handling nested views. Specifically, within my layout on a distinct record page, the structure is as follows: Upon initial loading, everything functions smoothly - validation works, c ...

Bootstrap5: Left-aligned Navigation Bar Pills and Right-aligned Text

I am trying to align all my navigation pills to the left, and then add a single text element that stays at the end of the navbar even when the page is resized. Navbar Image My attempt involved adding a div so that the navbar pills would take up 50% width ...