I am experiencing difficulty in detecting variable changes within my $scope function

I'm encountering an issue where a variable isn't being updated in a $scope function when there's a state change event. Despite seeing the variable update in the event listener, it doesn't reflect in the function.

The code snippet in question is as follows:

angular.module('testapp')
.controller('AnotherCtrl',
['$scope', '$rootScope', '$state',
    function ($scope, $rootScope, $state) {
        'use strict';
        console.log("Init prevState.");
        var prevState = 'null_state';
        $scope.state = prevState;


        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + prevState);
                    prevState = fromState.name;
                    console.log("New State:" + prevState);


                }
            })

        $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + prevState);
                    prevState = fromState.name;
                    console.log("New State:" + prevState);

                }
            })


        $scope.goBack = function () {
            //$scope.state = 'anotherState';
            console.log("goBack:" + prevState);
            $state.transitionTo(prevState, {arg: 'Robert'});
        };
    }]);

Below is the HTML template:

<div>
<h1>Another view</h1>
<br>
<br>
State:  <span ng-model="state">{{state}}</span>
<br>
<br>
<button ng-click="goBack()">Back</button>
</div>

The console output shows that, even after pressing the button invoking the goBack() function, the prevState variable remains 'null_state'.

If anyone could shed light on this issue, I would greatly appreciate it.

UPDATE following a review of the answers: After carefully reviewing and testing all proposed solutions, it appears that the core problem was not related to the immutability of string types. From my perspective, the most promising solution came from @Khanh TO. The issue stemmed from the event listeners not being initialized when the controller was first created, which was remedied by bootstrapping (angular.module.run). Another factor was that the controller was being re-initialized each time the state/view loaded, leading to the prevState variable also being re-initialized. Storing prevState on the rootScope resolved this conflict.

Answer №1

Every time there is a change in state, Angular will reset the controller linked to that state, resulting in the clearing and re-initialization of the previously stored state.

The solution is to save the previous state in a shared service, such as using $rootScope.

$rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {
                    $rootScope.prevState = fromState.name;
                }
 });

 $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {
                    $rootScope.prevState = fromState.name;
                }
  });

  $scope.goBack = function () {
    $state.transitionTo($rootScope.prevState, {arg: 'Robert'});
    //or $state.transitionTo($scope.prevState, {arg: 'Robert'}); //due to scope inheritance
  };

However, your code faces a significant issue of adding more event handlers to $rootScope each time the AnotherCtrl is re-initialized. Additionally, the $stateChangeSuccess event does not trigger when loading a page for the first time here.

To address these issues, it is important to only register event handlers once within the .run block:

angular.module('testapp')
       .run(["$rootScope",function ($rootScope){
                $rootScope.$on('$stateChangeError',
                   function (event, toState, toParams, fromState, fromParams, error) {
                      console.log("Error");
                      if (toState.name === 'anotherState') {
                        $rootScope.prevState = fromState.name;
                      }
                   });

               $rootScope.$on('$stateChangeSuccess',
                  function (event, toState, toParams, fromState, fromParams) {
                    console.log("Success");
                    if (toState.name === 'anotherState') {
                        $rootScope.prevState = fromState.name;
                    }
                });  
            }]);

Finally, ensure that only the necessary code remains in your controller:

$scope.goBack = function () {
    $state.transitionTo($rootScope.prevState, {arg: 'Robert'});
    //or $state.transitionTo($scope.prevState, {arg: 'Robert'}); //due to scope inheritance
 };

Answer №2

The issue you are encountering is a result of the functionality of JavaScript closures. Within the scope of the functions goBack() and the function tied to stateChangeSuccess, accessing prevState maintains a connection to the parent's prevState. However, since strings in JavaScript are immutable, assigning a value to prevState within goBack creates a new string, causing prevState in goBack() to point to the new string while the prevState associated with stateChangeSuccess remains pointing to "null_state".

To resolve this, consider wrapping prevState in an object like this:

var prevData = {prevState: 'null_state'};

Then, use prevData.prevState to update prevState. By using an object instead of a string, all closures will reference the same object, allowing changes to be reflected across different functions.

For further insights, please carefully review the following resources:

How do JavaScript closures work?

Pass a string by reference in Javascript

Answer №3

Every time you alter the route, the current controller is terminated (the $destroy event is triggered) and a new controller is instantiated.

To monitor when a controller is terminated, insert the following code within the controller:

$scope.$on('$destroy',function () {
   console.log("The controller is destroyed.");
});

The best way to preserve data in the application is by using Services (like $state) or $rootScope.

So, when creating the controller, you can retrieve data from the Service. Implement a getter function for this purpose.

Alternatively, if you wish to bind data with the controller, remember to update $scope.state, not prevState. The template only reflects changes made to $scope values.

Outcome:

angular.module('testapp')
.controller('AnotherCtrl',
['$scope', '$rootScope', '$state',
    function ($scope, $rootScope, $state) {
        'use strict';
        console.log("Initializing prevState.");
        $scope.state = $state.getState(); //creating the getter


        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {

                    console.log("Previous State:" + $scope.state);
                    $scope.state = fromState.name;
                    console.log("New State:" + $scope.state);


                }
            })

        $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {

                    console.log("Previous State:" + $scope.state);
                    $scope.state = fromState.name;
                    console.log("New State:" + $scope.state);

                }
            })


        $scope.goBack = function () {
            //$scope.state = 'anotherState';
            console.log("Going back to:" + $scope.state);
            $state.transitionTo($scope.state, {arg: 'Robert'});
        };
    }]);

Answer №4

If you're considering changing:

var previousState = 'null_state';

to

$scope.previousState = 'null_state';

Make sure to adjust other elements as needed.

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

AngularJS $scope variable can be initialized only once using an HTTP GET request

I'm facing an issue with fetching data from an API in a separate AngularJS application. There's a button that triggers the retrieval of data from the API. Upon clicking, it executes the $scope.getApiData function, which is connected to $scope.pr ...

While Ajax POST is functional on desktop, it does not seem to work on Phonegap applications or Android

I am facing an issue with a global function that does not seem to work properly in the PhoneGap Desktop app or Chrome Mobile on Android. Surprisingly, it works perfectly fine only in the Chrome PC version. The function is called using an onClick event, a ...

An effective way to pass a value using a variable in res.setHeader within express.js

Attempting to transmit a file on the frontend while including its name and extension. var fileReadStream = fs.createReadStream(filePath); res.setHeader("Content-disposition", `attachment; filename=${fileName}`); fileReadStream.pipe(res); Encount ...

Differences between JSX and creating instances of component classes

Could someone please clarify the distinction between the following two statements? let instance1 = new CustomComponent(); and let instance2 = <CustomComponent /> When checking in the Chrome debugger, I see the following: for instance1 CustomComp ...

Is today within the current week? Utilizing Moment JS for time tracking

There is a problem that I am facing. Can you assist me in determining whether the day falls within the current week? I am currently developing a weather forecast service and need to validate if a given day is within the current week. The only clue I have ...

"Sequelize will pause and wait for the loop to finish before executing the

As someone with a background in PHP, I'm finding the concept of callbacks a bit challenging to grasp. Essentially, I need to retrieve some rows and then iterate through them to compare against another model (in a different database). However, I want ...

The Challenge of Upgrading from AngularJS 1 to Angular 2: An Adapter Solution

<html lang="en"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="app.css" type="text/css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.js"></script> <script src="https:/ ...

I continue to encounter the following error message: A system error occurred while attempting to convert 'AngularTest' into an Angular project

I encountered an issue while attempting to integrate Karma into my Eclipse project. The error occurred during the conversion process to AngularJS: While converting 'AngularTest' to an angular project, I faced the following internal error: loader ...

Using Webdriver to dynamically enable or disable JavaScript popups in Firefox profiles

I am currently working on a test case that involves closing a JavaScript popup. The code functions correctly in a Windows environment, but when I try to deploy it on a CentOS based server, I encounter the following error: Element is not clickable at point ...

Is there a way to load a URL within a DIV element using AJAX?

These two PHP files are set up to capture a user's input from a form and then display that text. Below you'll find the code for each file: file1.php: <form action="output.php" method="post"> Paste text document: <br> ...

What is the best way to implement vuelidate when dealing with an array of objects?

In my form questionnaire, I am looping through an array of objects. Each object has five properties, but only one property needs validation. The validation setup in the component looks like this: specificGifts: { $each: { passThrough: { ...

PlateJS: Transforming HTML into data

Trying to implement a functionality where clicking on a button retrieves an HTML string. The objective is to have this rich text editor within React-Hook-Form, and upon form submission, the value will be saved as HTML for database storage. Below is the pr ...

What could be causing the malfunction of client components in NextJS 13.3.0 with experimental features?

My understanding of the beta documentation suggests that I need to include "use client" at the top of my client component in order for it to be defined as such. This allows me to use it within server components, leaves, or layouts. With that in ...

Inserting a Specific Iframe into a Designated Location in HTML with the Help of Jquery

Currently, I am encountering an issue with placing a dynamically created iframe inside a specific section of my webpage. The iframe is supposed to be contained within a div element named "maps", but instead it is appearing at the bottom of the page.This ma ...

How can you specify the environment in Google Tag Manager?

I have integrated google tag manager and google analytics using the vue-gtm plugin found at vue-gtm Within google tag manager, I have set up 3 environments under the same container. In a user-defined variable called GA from environment, I have created a ...

When using jquery.hide() and show(), the content within the div disappears momentarily before reappearing

Hello! I am experiencing an issue where the content of my div disappears after using a jQuery hide() and show() function. It seems to be gone. <li class="gg3"><a class="link" href="#" data-rel="content3">Link1</a></li> <div clas ...

Generate a configuration file that allows for the reading and storage of modifications

Is there a way to create a configuration file (JSON) on the local file system using JavaScript where I can write and modify data without losing it when the application is restarted? Any suggestions or solutions for this problem? Thank you for your assista ...

How can I iterate through multiple rows in JavaScript?

Feeling stuck, the simple yet dreaded for loop has become my nemesis and I could really use some guidance. Currently, I have a Google sheet with 3 rows (excluding headers) and 8 columns. As users input data via a web app, the number of rows will dynamicall ...

How can JavaScript determine if this is a legitimate JS class?

I'm currently in the process of converting a React.js project to a next.js project. In my project, there's a file named udf-compatible-datafeed.js. import * as tslib_1 from "tslib"; import { UDFCompatibleDatafeedBase } from "./udf-compatibl ...

The AngularJS Cross-Origin Resource Sharing (CORS) Policy

Our team is currently working on a web application using AngularJS that requires calling REST API services from another application. However, due to it being a cross domain request, we are facing difficulties in making the calls and receiving the following ...