2

I'm fairly new to Angular. I have two calculators that are functioning, and I'm trying to add the two $scope values, GrandTotal and GrandTotalTwo, to get a total of both sums TotalSum.

Codepen

HTML

<div ng-app="myApp" ng-controller="myController">
    <table>
        <tr>
            <th>Camp Fees</th>
            <th>Number of Campers</th>
            <th>Rates</th>
            <th>Totals</th>
        </tr>
        <tr ng-repeat="event in events">
            <td>{{event.name}}</td>
            <td><input placeholder="0" class="qty" type="number"
                       ng-model="quantity" min="0"
                       ng-change="recalc(event, quantity )" 
                       restrict-to="[0-9]" />
               </div>
            </td>
            <td>{{event.cost | currency : $}}</td>
            <td class="subTotal" placeholder="$0.00">
                {{ quantity*event.cost | currency : $}}
            </td>
        </tr>
    </table>
    <div>
        <h2>Camp Fee Total : {{GrandTotal | currency : $}}</h2>
    </div>
    <br>

    <div ng-controller="myControllerTwo">
        <table>
            <tr>
                <th>Accommodation Fees</th>
                <th>Rooms Needed</th>
                <th>Rates</th>
                <th>Totals</th>
            </tr>
            <tr ng-repeat="eventTwo in eventsTwo">
                <td>{{eventTwo.nameTwo}}</td>
                <td><input placeholder="0" class="qty" type="number"
                           ng-model="quantityTwo" min="0"
                           ng-change="recalcTwo(eventTwo, quantityTwo )"
                           restrict-to="[0-9]" />
                </td>
                <td>{{eventTwo.costTwo | currency : $}}</td>
                <td class="subTotal">{{ quantityTwo*eventTwo.costTwo | currency : $}}</td>
            </tr>
        </table>
        <div>
            <h2>Accommodation Fee Total : {{GrandTotalTwo | currency : $}}</h2>
        </div>
    </div>
</div>
<div ng-controller="ToAddTotal">
    <h2>Sum = {{TotalSum | currency : $}}</h2>
</div>

JS

(function() {
    var myApp = angular.module("myApp", []);
    myApp.controller("myController", ["$scope", myController]);
    var events = [{
        name: "Adults",
        cost: 485,
        itemTotal: 0
    }, {
        name: "Kids 13-18",
        cost: 394,
        itemTotal: 0
    }, {
        name: "Kids 4-12",
        cost: 307,
        itemTotal: 0
    }, {
        name: "Kids 0-3",
        cost: 100,
        itemTotal: 0
    }];

    function myController($scope) {
        $scope.events = events;
        $scope.recalc = function(item, quantity) {
            item.itemTotal = quantity * item.cost;
            $scope.GrandTotal = 0;
            var sum = 0;
            angular.forEach($scope.events, function(event) {
                $scope.GrandTotal = $scope.GrandTotal + event.itemTotal;

                $scope.GrandTotal == NaN ? "custom msg" : $scope.GrandTotal;
                // console.log($scope.GrandTotal);
            })

        };
    }
    myApp.controller("myControllerTwo", ["$scope", myControllerTwo]);
    var eventsTwo = [{
        nameTwo: "Hotel Style Rooms:",
        costTwo: 535, //Not relevant info (display: none in css - line:745)
        itemTotalTwo: 0 //Not relevant info (display: none in css - line:745)

    }, {
        nameTwo: "First Room",
        costTwo: 535,
        itemTotalTwo: 0
    }, {
        nameTwo: "Additional Room(s)",
        costTwo: 445,
        itemTotalTwo: 0
    }, {
        nameTwo: "RV Sites",
        costTwo: 175,
        itemTotalTwo: 0
    }];

    function myControllerTwo($scope) {
        $scope.eventsTwo = eventsTwo;
        $scope.recalcTwo = function(item, quantityTwo) {
            item.itemTotalTwo = quantityTwo * item.costTwo;
            $scope.GrandTotalTwo = 0;
            var sumTwo = 0;
            angular.forEach($scope.eventsTwo, function(eventTwo) {
                $scope.GrandTotalTwo = $scope.GrandTotalTwo + eventTwo.itemTotalTwo;

                $scope.GrandTotalTwo == NaN ? "custom msg" : $scope.GrandTotalTwo;
                // console.log($scope.GrandTotal);
            })

        };
    }
    function ToAddTotal($scope) {
        $scope.TotalSum = function() {
            return $scope.GrandTotal * 1 + $scope.GrandTotalTwo * 1;
        };
    }
})();
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Aeveritt84
  • 33
  • 8
  • Read [AngularJS Developer Guide - Scope Hierarchies](https://docs.angularjs.org/guide/scope#scope-hierarchies). – georgeawg Jun 13 '18 at 18:41
  • It looks like the third controller is outside of the `
    ` which encloses the `ng-app` directive. That is why the AngularJS compiler does not evaluate the `{{` expressions for the grand total sum.
    – georgeawg Jun 13 '18 at 19:06
  • New AngularJS developers often do not realize that `ng-repeat`, `ng-switch`, `ng-view`, `ng-include` and `ng-if` all create new child scopes, so problems often show up when these directives are involved. Issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models. For more information, see [What are the nuances of scope prototypal / prototypical inheritance in AngularJS?](https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs). – georgeawg Jun 13 '18 at 19:14

2 Answers2

1

You can do everything with the ng-change and ng-model directives -- no need for custom code:

  1. Put everything in the same controller
  2. Set the ng-change attribute on the inputs to set the total: [simplifying for clarity] ng-change="$parent.TotalSum = $parent.GrandTotal + $parent.GrandTotalTwo"
  3. Set the ng-model attribute of the output to TotalSum
Stand__Sure
  • 253
  • 1
  • 13
  • The key to this answer is keeping everything on the same controller. Each view/template (HTML File) should be bound to only one controller. This is how you keep things in-line and readable. Angular is great because of it's dependency injection. So you bind your controller to you template, but within your controller you inject your needs (services). – Dylan Wright Jun 13 '18 at 17:15
  • Something also to note I think to help distinguish AngularJS features is the ng-model and ng-change are both directives. Directives or components are smaller isolated (Key word) functions that you can share throughout the app. – Dylan Wright Jun 13 '18 at 17:16
  • This answer neglects the fact that inputs with `ng-model` are inside an `ng-repeat` and the code is not following the "best practice" of always have a '.' in your ng-models. – georgeawg Jun 13 '18 at 19:20
  • @georgeawg good eye -- updated the answer -- I agree: the OP code needs heavily refactored. – Stand__Sure Jun 13 '18 at 19:41
  • The use of `$parent` is a [code smell](https://en.wikipedia.org/wiki/Code_smell), a symptom of a deeper problem. – georgeawg Jun 13 '18 at 19:47
  • @georgeawg agreed. The OP is relatively new here: some answer is better than none -- isn't it? – Stand__Sure Jun 13 '18 at 20:04
0

Turns out I noticed I had an extra </div>, which was throwing off my code.

I removed:

function ToAddTotal($scope) {
    $scope.TotalSum = function() {
        return $scope.GrandTotal * 1 + $scope.GrandTotalTwo * 1;
    };
}

and,

<div ng-controller="ToAddTotal">
    <h2>Sum = {{TotalSum | currency : $}}</h2>
</div>

and instead wrote this in the HTML:

<h2>Total Sum : {{GrandTotal*1 + GrandTotalTwo*1 | currency : $}}</h2>

My final product is here if anyone is interested to see all of the code.

Aeveritt84
  • 33
  • 8