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

Node.js - Hitting maximum call stack size limit despite using process.nextTick()

I am currently developing a module for creating "chainable" validation in Express.js: const validatePost = (req, res, next) => { validator.validate(req.body) .expect('name.first') .present('This parameter is required') ...

Choosing options from Bootstrap dropdown using Protractor

I am working with a Bootstrap single-button dropdown and I need to programmatically click on one of the options in the dropdown using Protractor. How can I achieve this? <div class="btn-group" ng-if="!persist.edit" dropdown> ...

Obtain data from an ajax request to be included in the form submission

Seeking assistance with my code to retrieve the selected value from ajax_details.php for submission in the form action process_details.php. Here is the code snippet: injury_details.php <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jqu ...

What is the best way to display HTML code using Vue syntax that is retrieved from an Axios GET request

I am currently working on a project that involves a Symfony 5 and Vue 3 application. In this setup, a Symfony controller creates a form and provides its HTML through a JSON response. The code snippet below shows how the form HTML is returned as a string: i ...

What steps should I take to incorporate this feature into my Meteor project?

After successfully installing the type.js package, I discovered that the code works fine when directly executed in the console. Here is the code snippet: $(function(){ $(".typedelement").typed({ strings: ["You don&apo ...

Create a generic function that retrieves a specific property from an array of objects using the select method

Currently, I have implemented four functions that select entries from an array based on different properties. if ($scope.filters.filter1) $scope.filteredEntries = $scope.filteredEntries.filter(function (o) { return o.field1 === $scope.filt ...

Using CKEditor in an AngularJS web application: Tips and tricks

I'm having trouble integrating the ckeditor into an HTML page that's built with angularjs. Despite trying out numerous examples, such as the ng-ckeditor and ckeditor directives, I haven't found a solution that works for me. What I need is ...

Prevent title flickering in Android using Ionic

I am attempting to create a tab content page using the "standard" method recommended by the ionic template example. However, I have noticed that when switching between tabs on Android, the view title flickers. This issue is not present on iOS or desktop b ...

What is the method for eliminating PHP $_SESSION using AJAX?

I am facing an issue with removing an array within a PHP Session variable using AJAX. Here is the process I follow: HTML: <a href="#" onclick="delete_pix(false, '1', false, '1.jpg');">remove</a> JavaScript: functio ...

Using JavaScript to implement responsive design through media queries

The code seems to be having some issues as it only works when I reload the page. I am looking to display certain code if "size < 700" and other code if "size > 699". I also tried this code from here: <script> function myFunction(x) { if ( ...

The server has sent cookies headers, however, the browser did not store the cookies

I need assistance in understanding why browsers such as Chrome are not setting cookies, even though the Set-Cookie header is present in the Response Headers: Access-Control-Allow-Origin: * Connection: keep-alive Content-Length: 345 Content-Type: applicati ...

Incorporate an icon into a text input field using Material UI and React

I have a form with a text input element: <FormControl className='searchOrder'> <input className='form-control' type='text' placeholder='Search order' aria-label='Search&ap ...

I need help using i18N to translate the SELECT option in my VUE3 project. Can someone guide me

<n-select v-model:value="value" :options="options" /> options: [ { label: "Every Person", value: 'file', }, { label: 'Drive My Vehicle', ...

Using jQuery to select all child elements based on a specified condition

Is there a way to locate all instances of .post-comment-replies that have a nested '.post-comment-reply' within them, rather than being at the first level? Currently, the code provided retrieves not only those .post-comment-replies with nested . ...

The function is not being called as expected when using `onclick` or `eventListener('click')`

I'm in the process of developing a login form for my website that will offer 2 options - "login" and "signup". The concept is similar to mini tabs and iframe windows. Essentially, I have two divs side by side for "login" and "signup". When the user cl ...

What occurs when multiple HTML tags are assigned the same string as their ID attribute value?

While browsing a html page, I stumbled upon several tags that I thought had the same id. It turns out they were unique, but it got me thinking - what would happen if multiple tags actually shared the same id? I've always heard that the id attribute o ...

React Router's nested route causes a full page reload when navigating

I have been working on setting up nested routing in React Router and here is my code: import React from 'react'; import DefaultSwitch from './components/DefaultSwitch/DefaultSwitch'; import './scss/App.scss'; const App = () ...

How to set up objects with accessors and mutators in ES5 JavaScript?

creating a new object, obj1, with an enumerable property of value 42 > changing the value of obj1.property to 56 > output: 56 > accessing obj1.property returns: 42 When using use strict, an error occurs. To merge multiple objects: using jQuery ...

Iterate through a JSON object using JavaScript, with distinct keys but the same object structure

Hello, I'm currently in the process of creating a slider using images sourced from a json file. Here is the json structure that I am working with: { "info":[ { "slide1":[ { "title" ...

Decomposing a Vuex module into distinct files with Nuxt: A step-by-step guide

In the official Nuxt documentation (here), it is mentioned that 'You can choose to divide a module file into separate files: state.js, actions.js, mutations.js, and getters.js'. While there are examples of breaking down the Vuex store at the roo ...