Bird Dad
Bird Dad

Reputation: 427

How to use angular 1.x services to update one component from another?

So I have a parent component that has a ton of smaller components inside of it. The general idea is that I need one component to take in the data in an input and display it on another component. This is what I have which doesn't quite work, more details on the problem afterwards:

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

app.service('dataService', function() {
  let data = {
    key1: "",
    key2: "",
    key3: {
      key4: 0,
      key5: 0
    }
  }

  this.getKey1 = function() {
    return data.key1;
  }

  this.updateKey1 = function(str) {
     data.key1 = str;
  }
}

app.component('display', {
  controller: displayController,
  template: '<p>{{ keyOne }}</p>'
});

app.component('input', {
  controller: inputController,
  template: '<input ng-model="$ctrl.key1" ng-change="sendKey1()">'
}

function displayController($scope, dataService) {
  const vm = this;
  const self = $scope;
  vm.$onInit = onInit;
  function onInit() {
    self.keyOne = dataService.getKey1();
  }
}

function inputController($scope, dataService) {
  const vm = this;
  const self = $scope;
  vm.$onInit = onInit;
  function onInit() {
    self.sendKey1 = function() {
      dataService.updateKey1(vm.key1)
  }
}

So the update works, but then it doesn't pass it to the display component. If I log the data object after updating it it's correct but it doesn't show up on the view.

Upvotes: 0

Views: 1273

Answers (2)

alexhughesking
alexhughesking

Reputation: 2007

Take a look at A Tale of Frankenstein and Binding to Service Values in Angular.js.

Here's the quick summary of what is happening:

  1. displayController executes self.keyOne = dataService.getKey1(). This assigns an empty string to $scope.keyOne.
  2. In the view, the expression {{ keyOne }} watches $scope.keyOne for changes. It initially evaluates to "", so an empty string is present in the view.
  3. inputController changes dataService.data.keyOne to some value, such as hello world.
  4. In the view, the expression {{ keyOne }} once again evaluates $scope.keyOne for changes as part of the digest cycle. Problem: $scope.keyOne is still an empty string! displayController doesn't know to fetch the latest value from dataService.data.keyOne.

Luckily, the fix is simple. You just need to get a reference to the data object so that you can correctly evaluate data.keyOne when watching for changes:

app.service('dataService', function() {
  let data = {
    key1: ""
  }

  //Expose the data object
  this.getData = function() {
    return data;
  }
}

app.component('display', {
  controller: displayController,
  //Evaluate the current value of keyOne in the data object
  template: '<p>{{ data.keyOne }}</p>'
});

function displayController($scope, dataService) {
  const vm = this;
  const self = $scope;
  vm.$onInit = onInit;
  function onInit() {
    //Get a static reference to the data object.
    //The data object itself shouldn't change, but its properties will.
    self.data = dataService.getData();
  }
}

Upvotes: 1

adutu
adutu

Reputation: 401

You are updating a string data.key1 = str; in the

let data = { key1: "", key2: "", key3: { key4: 0, key5: 0 } }

Angular doesn't bind strings between components, only binds objects.

see here https://plnkr.co/edit/wfwyyGpcMKOSHGFdg2DS?p=preview

var app = angular.module('plunker', ['ngRoute', 'ngAnimate', 'ngSanitize']);

app.service('dataService', function() {
  let data = {
    key1: "",
    key2: "",
    key3: {
      key4: 0,
      key5: 0
    }
  }

  this.getData = function() {
    return data;
  }

  this.updateKey1 = function(str) {
    data.key1 = str;
  }
});



app.component('inputc', {
  controller: inputController,
  template: '<input ng-model="$ctrl.key1" ng-change="sendKey1()">'
});

app.component('display', {
  controller: displayController,
  template: '<p>{{ data.key1 }}</p>'
});

function displayController($scope, dataService) {
  const vm = this;
  const self = $scope;
  vm.$onInit = onInit;

  function onInit() {
    self.data = dataService.getData();
  }
}

function inputController($scope, dataService) {
  const vm = this;
  const self = $scope;
  vm.$onInit = onInit;

  function onInit() {
    self.sendKey1 = function() {
      dataService.updateKey1(vm.key1)
    }
  }
}
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link href="style.css" rel="stylesheet" />
    <script src="https://code.angularjs.org/1.6.2/angular.js"></script>
    <!-- By setting the version to snapshot (available for all modules), you can test with the latest master version -->
    <!--<script src="https://code.angularjs.org/snapshot/angular.js"></script>-->
    <script src="https://code.angularjs.org/1.6.2/angular-route.js"></script>
    <script src="https://code.angularjs.org/1.6.2/angular-animate.js"></script>
    <script src="https://code.angularjs.org/1.6.2/angular-sanitize.js"></script>    
    <script src="app.js"></script>
  </head>

  <body>
    <inputc></inputc>
    <display></display>
  </body>
  
  

</html>

Upvotes: 2

Related Questions