Updating shared data between controllers

69 views Asked by At

I'm trying to implement data sharing between AngularJS controllers, I've followed an approach to share a service instance between two controllers and there's no problem with it, the data from a variable shows up. But, now I need to update a variable from a service so the change is going to be replicated by various controllers, so any controller that injects that same service has the same data.

Here's my attempt to accomplish this...

HTML template

<h1>Services data sharing</h1>
<div ng-controller="OrderListController as vm">
  <h3>Orders list</h3>
  <table>
    <tr>
      <th>Item name</th>
      <th>Total</th>
    </tr>
    <tr ng-repeat="order in vm.orders" ng-click="vm.selectOrder(order.id)">
      <td ng-bind="order.item"></td>
      <td ng-bind="order.total | currency"></td>
    </tr>
  </table>
  <p><i>Click an order to see it's details.</i></p>
</div>
<div ng-controller="OrderShowController as vm">
  <h3>Order detail</h3>
  <p>
    <b>ID: </b><span ng-bind="vm.order.id"></span>
  </p>
  <p>
    <b>Item name: </b><span ng-bind="vm.order.item"></span>
  </p>
  <p>
    <b>Total: </b><span ng-bind="vm.order.total | currency"></span>
  </p>
</div>

JavaScript file

'use strict';

angular
    .module('orderCrud', [])
    .service('OrderService', function() {

        this.selected = {};

        var orders = [{
            id: 1,
            item: 'Hamburger',
            total: 5.15
        }, {
            id: 2,
            item: 'Nachos',
            total: 2.75
        }, {
            id: 3,
            item: 'Hot Dog',
            total: 3.50
        }];

        function getOrders() {

            return orders;
        }

        function setSelected(id) {

            for (var i = orders.length - 1; i >= 0; i--) {

                if (orders[i].id === id) {
                    this.selected = orders[i];
                    console.log(this.selected);
                    break;
                }
            }
        }

        return {
            get: getOrders,
            setSelected: setSelected
        };
    })
    .controller('OrderListController', function(OrderService) {

        var vm = this;

        vm.orders = OrderService.get();

        vm.selectOrder = function(id) {

            OrderService.setSelected(id);
        };

        vm.selectOrder(angular.copy(vm.orders).shift().id);
    })
    .controller('OrderShowController', function(OrderService) {

        var vm = this;

        vm.order = OrderService.selected;
    });

I've created a Plunker if you need it.

The goal is to show the selected order at the detail side of the template, that order should be changed if another order is clicked in the table at the list. And as you may notice, I'm not a fan of $scope or $rootScope in the controllers, if it isn't any way to implement this without injecting those scopes, please let me know.

Any help is going to be highly appreciated, thank you!

1

There are 1 answers

2
Rohìt Jíndal On

I did some code changes :

  • Used javaScript filter() method instead of for loop in setOrders method of the service.
  • use $scope.$watch function to reflect the service data into another controller based on the old value and new value changes.

Working demo :

angular
  .module('orderCrud', [])
  .service('OrderService', function() {
    
    this.selected = {};
    
    var orders = [{
      id: 1,
      item: 'Hamburger',
      total: 5.15
    }, {
      id: 2,
      item: 'Nachos',
      total: 2.75
    }, {
      id: 3,
      item: 'Hot Dog',
      total: 3.50
    }];
    
    var selectedOrder = [];
    
    this.getOrders = function() {
      
      return orders;
    }
    
    this.getSelectedOrder = function() {
      return selectedOrder;
    }
    
    this.setOrders = function(id) {
          this.order = orders.filter(function(item) {
            return item.id === id;
          });
          selectedOrder = this.order[0];
    }
    
  })
  .controller('OrderListController', function($scope, OrderService) {
    
    $scope.orderServ = OrderService;
    var vm = this;
    
    vm.orders = $scope.orderServ.getOrders();
    
    vm.selectOrder = function(id) {
      $scope.orderServ.setOrders(id);
    };
    
  }).controller('OrderShowController', function($scope, OrderService) {
    
    var vm = this;
    
    $scope.orderServ = OrderService;
    
    $scope.$watch(function () { return $scope.orderServ.getSelectedOrder(); }, function (newValue, oldValue) {
        if (newValue !== oldValue) vm.order = newValue;
    });
    
  });
table {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
}

td, th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 8px;
}

tr:nth-child(even) {
    background-color: #dddddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="orderCrud">
    <h1>Services data sharing</h1>
    <div ng-controller="OrderListController as vm">
      <h3>Orders list</h3>
      <table>
        <tr>
          <th>Item name</th>
          <th>Total</th>
        </tr>
        <tr ng-repeat="order in vm.orders" ng-click="vm.selectOrder(order.id)">
          <td ng-bind="order.item"></td>
          <td ng-bind="order.total | currency"></td>
        </tr>
      </table>
      <p><i>Click an order to select it.</i></p>
      </div>
<div ng-controller="OrderShowController as vm">
      <h3>Order detail</h3>
      <p>
        <b>ID: </b><span ng-bind="vm.order.id"></span>
      </p>
      <p>
        <b>Item name: </b><span ng-bind="vm.order.item"></span>
      </p>
      <p>
        <b>Total: </b><span ng-bind="vm.order.total | currency"></span>
      </p>
          </div>
  </body>