How can we prevent AngularJS from continuously triggering a controller function with every input change?

Here is a simple TODO app created using angularjs.

The functionality is working well, but I am facing an issue where angularjs calls the 'remaining' function on every keystroke in the input field!! Can you spot any errors in the code below?

<!doctype html>
<html ng-app>

<head>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
    <link href="style.css" rel="stylesheet">
</head>

<body>
    <div>
        <h2>TODO:</h2>
        <div ng-controller="TodoCtrl">
            <span>{{remaining()}} of {{todos.length}} remaining</span> [ <a href="" ng-click="archive()">archive</a> ]
            <ul class="list-unstyled">
                <li ng-repeat="todo in todos">
                    <input type="checkbox" ng-model="todo.done" >
                    <span>{{todo.text}}</span>
                </li>
            </ul>
            <form ng-submit="addTodo()">
                <input type="text" ng-model="todo" placeholder="Enter your task here">
            <form>
        </div>
    </div>

    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
    <!--script src="app.js"></script-->
    <script>
    function TodoCtrl($scope) {
        $scope.todos = [
            {text: 'learn angular', done:true},
            {text: 'build an angular app', done:false}
        ];

        $scope.remaining = function() {
            console.log('calling remaining()');
            var count = 0;
            angular.forEach($scope.todos, function(todo) {
                count += todo.done? 0:1;
            });

            return count;
        };

        $scope.addTodo =  function() {
            if($scope.todo)  {
                $scope.todos.push({text: $scope.todo, done:false});
            }
            $scope.todo = '';
        };
    }
    </script>
</body>
</html>

Answer №1

Angular follows a standard behavior where it executes all watches on the scope whenever there is a change in the model or any other binding. This process, known as a digest cycle, is initiated by a $scope.$apply(). It's crucial to avoid performing heavy calculations within functions called from binding expressions in the view.

Performance issues may arise when dealing with functions that involve complex calculations or filtering on large lists. To address this, one can set up a watch on the collection and update the calculated property as a separate variable in the scope only when changes occur in the collection. By doing so, unnecessary recalculations can be avoided, especially when unrelated inputs are modified.

Answer №2

In addition to @dtabuenc's response, here is an illustration of how to avoid the function triggering with every slight modification:

Substitute:

<span>{{remaining()}} of {{todos.length}} remaining</span> [ <a href="" ng-click="archive()">archive</a> ]

With:

<span>{{remaining}} of {{todos.length}} remaining</span> [ <a href="" ng-click="archive()">archive</a> ]

And change the definition of $scope.remaining = function()... to:

$scope.$watch('todos', function(todos) {
  var count = 0;
  angular.forEach(todos, function(todo) {
    count += todo.done ? 0 : 1;
  }
  $scope.remaining = count;
}

This approach will prevent angular from constantly re-evaluating the remaining() angular expression in your view whenever the scope is altered.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing Next.js on Vercel with Sentry and opting out of source maps in the production environment

Currently setting up a Next.js application on Vercel with Sentry configuration thanks to the @sentry/next.js module. You can find an example repo here - https://github.com/voronianski/test-next-sentry-app This setup is based on the official example from N ...

"Execution of the console.log statement occurs following the completion of the request handling

When I have a piece of middleware that responds if no token is found, why does the console.log line still run after the request is responded to? I always believed that the res.json call would "end" the middleware. Any insights on this behavior would be g ...

Unable to access attributes of null (reading 'title) and other information

When the parameter 'edit=true' is present, I expect a form with pre-filled data to render; otherwise, redirect me. However, I am encountering an issue where I keep getting redirected and receiving a console error stating 'Cannot read propert ...

Dealing with blank values in jQuery DataTables

I am currently utilizing jQuery DataTable to display data in table format; within the table, there is a button that triggers a Bootstrap Modal for editing two of these values, and I utilize Ajax to send the modified values to a Spring Controller. The init ...

Utilizing a vertex shader to rotate a geometry within threejs

Having trouble rotating a box using vertex shader, it's getting sheared but I can't pinpoint the issue. Can someone help me troubleshoot? Check out the code snippet below: uniform float delta; void main() { vec4 modelViewPosition = modelView ...

The React Router Dom V6 triggers loaders as soon as the router is initialized

I'm currently working on implementing routing into my application using react-router-dom V6, specifically utilizing the new createBrowserRouter function and RouterProvider component. The issue I'm facing is that when attempting to integrate a lo ...

Using the push method on a global object array will replace all existing values within the array

After researching various solutions to this issue, I have not been successful in implementing the commonly suggested fix. The variable evtList holds a collection of global objects labeled as "Event". Previous discussions advised creating a local object ( ...

Empower your presentations with slick cloning technology

I am facing an issue with my slider that I cannot seem to resolve. Currently, I am using slick to display 3 slides, but only the center one shows all the content. To achieve a continuous infinite slider effect where all slides appear in the center, I need ...

Node.js tutorial: Fetching all pages of playlists from Spotify API

Currently, I am attempting to retrieve all of a user's playlists from the spotify API and display them in a selection list. The challenge lies in only being able to access 20 playlists at a time, which prompted me to create a while loop utilizing the ...

Mobile phone carousel glitch

I am encountering an issue with the Bootstrap v4.0 Carousel that I can't seem to resolve. Despite searching online for similar problems, I have not been able to find a solution, which is quite perplexing. Whenever I load a page on an iPhone, the caro ...

Employing ngModel within an (click) event in Angular 4

I have this html code snippet: <div class="workflow-row"> <input type="checkbox" id="new-workflow" [(ngModel)]="new_checkbox"> <label>New Workflow</label> <input type="text" *ngIf="new_checkbox" placeholder="Enter ...

The KeyboardAvoidingView disrupts the structure of the flexbox layout

Check out this code snippet: return ( <KeyboardAvoidingView style={{ flex: 1 }} behavior="padding" enabled> <View style={style.flex1}> <View style={style.imageContainer}> <Image style={style.image} ...

"Is there a way to transfer a value from one list box to another without needing to refresh the page, by utilizing AJAX,

Is there a way to automatically load Related Course levels in the Course Level Listbox when selecting a Course from the Course list? <script type="text/javascript" src="js/jquery.js"></script> <div>Course:<select name="course_id" id= ...

Adjust the href link value when a new option is selected

I currently have a select element displayed below. Next to it, there is a link that sets the eID value to a session value. However, I want this value to be updated dynamically whenever a different eID value is selected from the dropdown. Although I can r ...

Using HTML5 video with cue points and stop points

I've noticed that there are various options available for implementing cuepoints in HTML5 videos, such as using PopcornJS or CuepointsJS to trigger play events at specific times within the video track. However, I am wondering if there is a solution t ...

AngularJS: Modifying values in one div updates data in all other divs

The webpage appears as shown below: https://i.sstatic.net/IxcnK.png HTML <li class="list-group-item" ng-repeat="eachData in lstRepositoryData"> <div class="ember-view"> <div class="github-connection overflow-hidden shadow-oute ...

Error in D3: stream_layers function is not defined

Utilizing nvd3.js to construct a basic stacked bar chart as detailed here I inserted the code provided in the link into an Angular directive like this: app.directive('stackBar', function() { return { restrict: 'A', ...

The custom directive in Vue utilizes the refreshed DOM element (also known as $el)

I am looking to create a custom directive that will replace all occurrences of 'cx' with <strong>cx</strong> in the Dom Tree. Here is my current approach: Vue.config.productionTip = false function removeKeywords(el, keyword){ i ...

Sharing styles between ReactJS and Material-UI - best practices

I am currently facing an issue where I need to share styles across my entire web application. Here is the Problem: I have been using makeStyles in a repetitive manner as shown below: In component_A.js const useStyles = makeStyles({ specific_style: { ...

If I change the request mode to 'no-cors' in my Firebase cloud function, how will it impact the outcome?

After encountering an issue with the Firebase inbuilt password reset functionality, I created a Firebase function to handle OTP verification and password changes based on the correctness of the OTP. The function is designed to validate the OTP provided, ch ...