1

I am trying to use multiple components on a composite page and reference data from a common service. The problem is that I am trying to instantiate the object in the service from within the first component and then reference it in the second component; if I put the second component ahead of my first component, my value object hasn't been instantiated yet and the second component fails to show anything.

I was hoping two-way data binding would save me, but it isn't.

Composite HTML

<second-component></second-component>
<first-component></first-component>

First Component

angular.
module('simple').
component('firstComponent', {
    template: "<h3>Component 1</h3><ul ng-repeat='val in $ctrl.someVals'><li>{{val}}</li></ul>",
    controller: function(factory) {
        'use strict';
        // Responsible for creating an object in the service
        factory.changeData();
        this.someVals = factory.vals;
    }
});

Second Component

angular.
module('simple').
component('secondComponent', {
    template: "<h3>Component 2</h3><ul ng-repeat='val in $ctrl.someVals'><li>{{val}}</li></ul>",
    controller: function(factory) {
        'use strict';
        // As a sub-component, secondComponent depends on an object being present in the service. This
        // will be created by firstComponent, which will reside on the same page.           
        this.someVals = factory.vals;
    }
});

Factory Service

angular.
module('simple').
factory('factory', function() {
    var data = {};
    data.vals = {};
    data.changeData = function() {
        data.vals = {
            "a": 11,
            "b": 22,
            "c": 33
        };
    };
    return data;
});

I would like to see component 2 and component 1 referencing the same data after it is changed by component 1:

Component 2

  • 11
  • 22
  • 33

Component 1

  • 11
  • 22
  • 33

Instead I see:

Component 2

Component 1

  • 11
  • 22
  • 33

Is there a technique I should use for instantiating objects in one component and then sharing it with another component that might happen to live on the same page and not have the component order on the page matter? The timing clearly matters in this example. If I reverse the order of my components, it works fine. But graphically I need my second component to go first and it doesn't make sense instantiating my object in the service from within the second component as it is supposed to be a purely presentational component relying on service state data. Again, I thought two-way data binding would detect when the first component changes the object in the service, but it doesn't seem to work that way.

P.S. I'm fairly new to AngularJS. I have read a lot of great articles and watched some great videos; your suggestions on reference material or tutorials are most welcome!

R. Richards
  • 24,603
  • 10
  • 64
  • 64
Jason
  • 119
  • 1
  • 9
  • https://stackoverflow.com/questions/16286605/angularjs-initialize-service-with-asynchronous-data?rq=1 suggests using resolve. I never considered this; I'm going to read https://toddmotto.com/resolve-promises-in-angular-routes/ to learn more and perhaps solve my problem. – Jason Dec 01 '17 at 23:53
  • Why you cannot instatiate your `vals` object in service? Then data will be available for all components in same time – wprzechodzen Dec 02 '17 at 13:50
  • @wprzechodzen That's what I am thinking I need to do. Ideally, I would like my user to provide some input on what the initial state of the object in the service is. Do you have any suggestions for instantiating vals in the service with, let's say, a single parm from a user? – Jason Dec 02 '17 at 15:25
  • The simplest solution is just to change your current instance of the `vals` object instead of changing reference. So: write `data.vals.a = 11; data.vals.b =22;` – wprzechodzen Dec 02 '17 at 15:35

1 Answers1

1

As per your requirement you need to call the service again in . By means of $emit you can send a message to second component from first component and there by call the service function again. First Component

factory.changeData();
notify();
this.someVals = factory.vals;
function notify(){
 $rootScope.$broadcast('notifier', message);
}

Second Component

function updateVals() {
 this.someVals = factory.vals;
}
$scope.$on('notifier', updateVals)
Harsha ANS
  • 136
  • 3
  • That's pretty neat, thanks! It certainly works. Some drawbacks are the need to inject $rootScope into the first component for the broadcast, and the need to inject $scope into the second component for the listener. I am also interested if there is another way of initializing the object before either of the components start looking at it. – Jason Dec 02 '17 at 15:16
  • Communication between controllers is possible via stateParams,broadcast,emit. Where as stateParams dont work in this case. For the object creation prior, use if condition on selectors to prevent initialization of components before object creation. – Harsha ANS Dec 04 '17 at 10:30