0

Hi I've been developing my first AngularJS application and studying Angular for about 4 months using my free time, so I am far for being an expert.

I just notice in one of my Controllers that there's a function defined to calculate the effort between two dates that's being called for every single click that happens in that page. I mean even if I have a simple button that just shows or hides parts of the view or even using angular-ui calendar component button to show the calendar it triggers that function. I have no clue why is this happening. Here's some fragment of code:

My Controller definition:

'use strict';
(function () {
    var byallApp = angular.module('byallApp');
    byallApp.controller('ActivitieController', ['$scope', '$log', 'httpGetService',       '$rootScope', 'httpPostService', '$moment',
        function ($scope, $log, httpGetService, $rootScope, httpPostService, $moment) {

        $scope.activities = [];//array that holds the objects to be displayed in table.
        ....//a lot of normal code here. No code at all that updates the $scope.activities array is ever called outside some other function.

        //function that calculates the effort, uses momentjs
        this.calculateEffortFromValues = function (finalDate, initialDate) {
            $log.info('Executing calculateEffortFromValues');
            var initial = $moment(new Date(initialDate));
            var final = $moment(new Date(finalDate));
            var duration = $moment.utc(final.diff(initial)).format("HH:mm");
            $log.info('duration: ' + duration);
            return (duration);
        }

    }]);
})();

Than in my view I use the controller and angular directives to render the table using the $scope.activities array:

<div ng-controller="ActivitiesController as activitiesCtrl">
....
<tbody>
        <tr ng-repeat="activityList in activities">
            <td>{{activityList.initialDate | date : 'dd/MM/yyyy'}}</td>
            <td>{{activityList.initialDate| date : 'hh:mm a'}}</td>
            <td>{{activityList.endDate | date : 'hh:mm a'}}</td>
            **<td>{{activitiesCtrl.calculateEffortFromValues(activityList.endDate, activityList.initialDate)}}</td>**
            <td>{{activityList.codContract}}</td>
            <td>{{activityList.description}}</td>
            <td>
                <button class="btn btn-danger btn-mini" ng-click="deleteRow(row)" ng-hide="isTemp($index)"><img
                        width="25px" height="25px" title="Delete Activity!" src="img/trash.ico"/></button>
            </td>
        </tr>
        </tbody>   

I than call that function while building the table to calculate the effort based on 2 other fields of the table as showed above.

All works perfeclty as expected. But than reviewing the code and with the open debugger console in Chrome I noticed that for every single click I have no this page, this function is called again. I start thinking that somehow my $scope.activities array would probably being updated but I double checked and this doesn's seem to be the case as it's also only updated inside functions where I also log to console and that functions are never called.

Any clues about what could be causing this strange behavior?

groo
  • 4,213
  • 6
  • 45
  • 69
  • This link could help you understand a bit what is happening : http://stackoverflow.com/questions/12463902/how-does-the-binding-and-digesting-work-in-angularjs . You should read about what is digest in AngularJS – Julien Oct 31 '14 at 10:53
  • @Julien - Thanks, I think I got it. From the suggested link I got to this docs: https://docs.angularjs.org/guide/concepts#runtime those were perfect to clarify my mind about this. Regards – groo Oct 31 '14 at 12:33

1 Answers1

1

When you bind values in your HTML code, with {{ }}, you basically ask Angular to keep the HTML snippet up to date with the data it is bound to. In order to accomplish that, Angular has to check at certain points in time if data has changed. If you bind your HTML to the result of a function call, Angular has to execute the function to be sure that the HTML is up to date.

Now, these certain points in time are when Angular finishes $applying some code (the end of the $digest cycle).

Framework events, such as ng-click, causes Angular to $apply code.

If you are concerned with the non-relevant re-evaluation of your Effort, you should bound it to a $scope variable like $scope.effort.

EDIT:

You mentionned not to be using any events, so assuming initialDate and endDate won't be updated, the resulting effort won't need to be recomputed. You should then compute only once.

//ActivitieController
//code to call after $scope.activities gets filled (not quoted in your question)
$scope.activities.map(function(a){
    a.effort = calculateEffortFromValues(a.initialDate,a.initialDate);
});


//HTML
 <td>{{activityList.effort}}</td>

I don't really get why you would be using Angular or any client framework/library for an HTML page that doesn't require interactivity (== events).

zrz
  • 340
  • 2
  • 10
  • Thanks for the answer, but after reading the official documentation here as suggested in my question comments above: https://docs.angularjs.org/guide/concepts#runtime I think the best solution would be a factory for that. Also I have to confess that I am not a big fan of using events and I avoid them whenever I have a chance of course I use them in specific cases. So I consider a factory as suggested @ official docs a better solution. What do you think? – groo Oct 31 '14 at 12:35
  • Updated my answer, taking into account that you won't be using events. – zrz Oct 31 '14 at 13:05
  • Hi, hum..I will try that during the weekend. About the events and using angular: Well, there's much more than just events to angular like DI, modularization, directives, 2 way data binding, among other very nice stuff angular provides. What I don't like is to wire up my application using the event mechanism in Angular. Have you noticed they are removing much of the event mechanism from next version in Angular 2? – groo Oct 31 '14 at 13:25
  • I haven't noticed. If you have any source, I would be happy to read about how removing event mechanism could push Angular forward. Regarding your factory idea, I don't really see what problem you are trying to solve now. Regarding your code, I would maybe use an ActivityService to abstract away the HTTP calls to your server, and augment received data (for example with the `effort` property). – zrz Oct 31 '14 at 14:05
  • Hi, this solution worked perfectly thanks. About Angular2 maybe check this. jQLite will be dropped(lot of events with it) and mostly I've seen are comments over the internet. I am not a JS expert. Started deepening in it a while ago, so I don't have the skills(yet) to argument with you, but I am pretty sure you'll find ppl who has. Thanks again for the solution, this type of solution looks much better to me(from the backgound I came from C++, C#, Java) than using events. http://goo.gl/aAz67q http://goo.gl/Hy1GSo – groo Oct 31 '14 at 21:35