Search code examples
angularjsangularjs-ng-repeatangular-ngmodel

How to calculate a dynamic value with ng-repeat for a nested object property in AngularJs


I want to calculate taxes in relation with a user-input that correspond to the price.

I have no problem when I don't use ng-repeat. But I can't figure out how to make it works with ng-repeat. For information, the code below refers to an invoice controller where I add items to the invoice.

Here is what I have:

Controller

App.controller('FacturesSoloController', function($scope, $stateParams, Facture ) {


    $scope.factureId = $stateParams.factureId;


    Facture.get({ id: $stateParams.factureId }, function(data) {
        $scope.facture = data;

        $scope.ajouterItem = function(){
         $scope.facture.items.push({});
        }

    });

    $scope.calculTps = function() {
        $scope.item.tps = $scope.item.prix*5/100;
    };
    $scope.calculTvq = function() {
        $scope.item.tvq = $scope.item.prix*9.975/100;
    };
    $scope.calculGrandTotal = function() {
        $scope.item.grandtotal = parseFloat($scope.item.prix)+parseFloat($scope.item.tps)+parseFloat($scope.item.tvq);
    };
});

Here is my HTML file

<table class="table">
<thead>
    <tr>
        <th><strong>Description</strong></th>
        <th class="text-right"><strong>Prix</strong></th>
        <th class="text-right"><strong>TPS</strong></th>
        <th class="text-right"><strong>TVQ</strong></th>
        <th class="text-right"><strong>Grand Total</strong></th>
    </tr>
</thead>
<tbody>
    <tr ng-repeat="item in facture.items">
        <td class="bold" width="50%">
            <input type="text" class="form-control" name="description" id="description" ng-model="item.description"></td>
        <td class="text-right">
            <input type="text" class="form-control" name="prix" id="prix" ng-model="item.prix"></td>
        <td class="text-right">
            <input type="text" class="form-control" name="tps" id="tps" ng-model="item.tps" value="{{calculTps()}}"></td>
        <td class="text-right">
            <input type="text" class="form-control" name="tvq" id="tvq" ng-model="item.tvq" value="{{calculTvq()}}"></td>
        <td class="text-right">
            <input type="text" class="form-control" name="grandtotal" id="grandtotal" ng-model="item.grandtotal" value="{{calculGrandTotal()}}">
        </td>
    </tr>
    <tr>
        <td class="bold"></td>
        <td class="text-right"></td>
        <td class="text-right"></td>
        <td class="text-right"></td>
        <td class="text-right">Grand Total</td>
    </tr>
</tbody>
</table>

And here is what {{facture.items | json}} returns

[
  {
    "id": 1,
    "facture_id": 10200,
    "description": "Item numéro 1",
    "prix": "15.00",
    "tps": "0.75",
    "tvq": "1.50",
    "grandtotal": "17.25",
    "created_at": "2015-02-21 15:07:18",
    "updated_at": "2015-02-21 15:07:18"
  },
  {
    "id": 2,
    "facture_id": 10200,
    "description": "Deuxième item quoi",
    "prix": "135.00",
    "tps": "6.75",
    "tvq": "13.47",
    "grandtotal": "155.22",
    "created_at": "2015-02-21 15:07:18",
    "updated_at": "2015-02-21 15:07:18"
  }
]

So, I would like the "tps" and the "tvq" to calculate automaticly when I enter a number in the "prix" input. I wonder what is wrong.


Solution

  • In your javascript you still need to refer to 'item' as part of the 'facture' array. The controller doesn't know what '$scope.item' is. You can use '$index' to call a function which will do your calculations for each element in the array.

        App.controller('FacturesSoloController', function($scope, $stateParams, Facture ) {
    
    
        $scope.factureId = $stateParams.factureId;
    
    
        Facture.get({ id: $stateParams.factureId }, function(data) {
            $scope.facture = data;
    
            $scope.ajouterItem = function(){
             $scope.facture.items.push({});
            }
    
        });
    
        $scope.calculTps = function(i) {
            $scope.facture.items[i].tps = $scope.facture.items[i].prix*5/100;
        };
    
        $scope.calculTvq = function(i) {
            $scope.facture.items[i].tvq = $scope.facture.items[i].prix*9.975/100;
        };
    
        $scope.calculGrandTotal = function(i) {
            $scope.facture.items[i].grandtotal = parseFloat($scope.facture.items[i].prix)+parseFloat($scope.facture.items[i].tps)+parseFloat($scope.facture.items[i].tvq);
        };
    
        $scope.doCalculations = function(i) {
            $scope.calculTvq(i);
            $scope.calculTps(i);
            $scope.calculGrandTotal(i);
        }
    });
    

    and your HTML

    <table class="table">
    <thead>
        <tr>
            <th><strong>Description</strong></th>
            <th class="text-right"><strong>Prix</strong></th>
            <th class="text-right"><strong>TPS</strong></th>
            <th class="text-right"><strong>TVQ</strong></th>
            <th class="text-right"><strong>Grand Total</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="item in facture.items">
            <td class="bold" width="50%">
                <input type="text" class="form-control" name="description" id="description" ng-model="item.description"></td>
            <td class="text-right">
                <input type="text" class="form-control" name="prix" id="prix" ng-model="item.prix" ng-change="doCalculations($index)></td>
            <td class="text-right">
                <input type="text" class="form-control" name="tps" id="tps" ng-model="item.tps"></td>
            <td class="text-right">
                <input type="text" class="form-control" name="tvq" id="tvq" ng-model="item.tvq"></td>
            <td class="text-right">
                <input type="text" class="form-control" name="grandtotal" id="grandtotal" ng-model="item.grandtotal">
            </td>
        </tr>
        <tr>
            <td class="bold"></td>
            <td class="text-right"></td>
            <td class="text-right"></td>
            <td class="text-right"></td>
            <td class="text-right">Grand Total</td>
        </tr>
    </tbody>
    </table>
    

    https://docs.angularjs.org/api/ng/directive/ngRepeat https://docs.angularjs.org/api/ng/directive/ngChange

    UPDATE:

    You should also use ngChange to call both calculation functions each time the 'prix' is updated