A variable that can be changed is available within a closure in Angular services

Currently, I am working with the Angular UI grid library and facing an issue while populating a custom dropdown filter. This filter is only meant to be applied to specific columns, which is why I have implemented a for loop to dynamically access these columns. For each specific column, I make a request to the API using my Angular services to fetch values for the filter. If the response is successful, I compare the property id of each column with the returned data id and then populate the filter value.

The problem arises within the then function, as my IDE displays a warning

mutable variable is accessible from closure

and nothing seems to work inside the then function.

I have come across various discussions on this platform and learned that I need to execute a self-invoking function, but even that solution doesn't seem to work for me.

So, where exactly am I making a mistake? Your help is greatly appreciated.

Here is the code snippet in question:

 for (var i = 0; i <  $scope.columns.length; i++) {

                        // ===== rebuild columns tree =====

                        var execute_ids = [];

                        angular.forEach($scope.columns[i].property, function(){                             
                           if ($scope.columns[i].property.strict === true){

                                if(execute_ids.indexOf($scope.columns[i].property.propertyTypeId) === -1){
                                    execute_ids.push($scope.columns[i].property.propertyTypeId);

                                    lovServices.customPropFilterValue(execute_ids)
                                        .then(function (response) {
                                            (function () {
                                            if (response.id == $scope.columns[i].property.propertyTypeId){

                                                $scope.filter.push({
                                                    value: data.value,
                                                    label: data.value
                                                });

                                                 $scope.columns[i].filter = {
                                                    type: uiGridConstants.filter.SELECT,
                                                    condition: uiGridConstants.filter.EXACT,
                                                    selectOptions: $scope.filter
                                                };
                                            }
                                            })()
                                        });
                                }
                            }
                        });

                    }

Here is the original part of the code for reference:

lovServices.customPropFilterValue(execute_ids)
                                        .then(function (response) {
                                            if (response.id == $scope.columns[i].property.propertyTypeId){

                                                $scope.filter.push({
                                                    value: response.value,
                                                    label: response.value
                                                });

                                                $scope.columns[i].filter = {
                                                    type: uiGridConstants.filter.SELECT,
                                                    condition: uiGridConstants.filter.EXACT,
                                                    selectOptions: $scope.filter
                                                };
                                            }
                                        });

EDIT

After implementing the solution provided by @JuanTonina, the warning message disappeared, but a new problem surfaced. The i variable within the then function returns an incorrect value.

 (function (i) { /* note that the lovServices call is INSIDE the IIFE*/

            console.log($scope.columns[i].property.propertyTypeId)

 // this console log returns correct ids (120, 123, 194)

            lovServices.customPropFilterValue(execute_ids)
                .then(function (response) {

                    console.log($scope.columns[i].property.propertyTypeId)

 // this console log returns wrong ids (120, undefined, 114)

                    if (response.id == $scope.columns[i].property.propertyTypeId) {

                        $scope.filter.push({
                            value: data.value,
                            label: data.value
                        });

                        $scope.columns[i].filter = {
                            type: uiGridConstants.filter.SELECT,
                            condition: uiGridConstants.filter.EXACT,
                            selectOptions: $scope.filter
                        };
                    }
                });
        })(i)

Answer №1

In order to provide a more clear explanation, I have decided to include this as an answer rather than relying solely on comments.

It is important that your code is structured in the following manner:

for (var i = 0; i < $scope.columns.length; i++) {

// ===== rebuild columns tree =====

var execute_ids = [];

angular.forEach($scope.columns[i].property, function () {
    if ($scope.columns[i].property.strict === true) {

        if (execute_ids.indexOf($scope.columns[i].property.propertyTypeId) === -1) {
            execute_ids.push($scope.columns[i].property.propertyTypeId);

            (function (i) { /* please note the lovServices call is WITHIN the IIFE*/
                lovServices.customPropFilterValue(execute_ids)
                    .then(function (response) {
                        if (response.id == $scope.columns[i].property.propertyTypeId) {

                            $scope.filter.push({
                                value: data.value,
                                label: data.value
                            });

                            $scope.columns[i].filter = {
                                type: uiGridConstants.filter.SELECT,
                                condition: uiGridConstants.filter.EXACT,
                                selectOptions: $scope.filter
                            };
                        }
                    });
            })(i)
        }
    }
});

}

The key issue to address here is the changing value of your variable i before it is utilized in the callback. Additionally, if you are working with es6, using let i = 0;... can also resolve this problem.

Answer №2

The issue may be connected to the presence of an Immediately-Invoked Function Expression (IIFE). This type of function is executed immediately after it is created.

To resolve this problem, try removing the IIFE from inside your .then() method. This should help fix the issue.

(function () {
    // Your code here
})();

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

What is the procedure for adding a URL path in jQuery?

When using $(this).attr("href"); in the jQuery Ajax url field, it correctly retrieves the URL path. However, if I try to use a prefix in front of it like this: $.ajax({ type: 'GET' url: 'api/' + $(this).attr("href"); }) the co ...

Error message: Unknown scope- unable to process 'files-path' Android File provider issue

My current project involves implementing file system access in react native Android. However, when attempting to add 'files-path' in XML, an error stating "Error: Unsupported type 'files-path'" is encountered. I am utilizing the react n ...

The navigation bar does not display the CSS when in the active state

I created a navigation bar in ReactJS and I need the tabs to stay highlighted when clicked, but the styles are not applying correctly. I have double-checked that the stylesheet is linked properly based on the Reactjs file structure. For reference, I used ...

Hold on for the useState object to contain a value

Created a custom hook that fetches the user's location and determines the nearest marker on a map based on that information. Initially, it returns the default value of useState. The response looks like this: { coordinates: { lat: '', lng: ...

"The text() or json() methods in Javascript's fetch function never seem to resolve, leaving the operation in a perpetual

In my new nextjs 13 project, I'm attempting to perform a fetch operation (unsure if it's related to JavaScript or nextjs) and using console.logs to monitor the code execution. let url = `${BASE}/${module}/${path}`; url += "?" + ne ...

Angular directive - observing changes in dynamically grouped models

How can I make similar models in a group watch each other for changes dynamically, with an unspecified number of members in each group? For example, if one input is filled, it should auto-fill any other empty inputs in the same group. The goal is to have c ...

Developing pledges in AngularJS

I am currently working on implementing a promise in Angular using the $q service to retrieve an object from a web service. The unique aspect of this implementation is that if the object is already cached, it should be returned without making a call to the ...

Using Express to Authenticate with the Yelp API

I am currently trying to make a GET request to Yelp's API for a simple search using Express and Node.js, but I am struggling with setting the request header with the provided API key. Despite following the documentation by passing basic authentication ...

What is the process for removing a specific row from a datatable?

I have implemented a data-table using Vue and Vuetify. When I click on the delete icon in a specific row, a snackbar pops up with two buttons - yes and no. If I click on the yes button, I want to delete that specific row. How can I achieve this functionali ...

Using JavaScript, enter the value into the input field and then redirect the

I'm encountering a minor issue with my contact form and jQuery redirection based on input fields. Everything was working smoothly until I attempted to integrate the redirection into the form validation code. Redirection snippet: $("#signup").submit ...

Issue encountered while attempting to load several google charts simultaneously

What do I need? I am in need of two Google charts, one on each tab of the website as shown in the screenshot below: Tabs What seems to be the issue? The second chart is not loading properly and showing mixed information. This can be observed in the scre ...

Error encountered when attempting to have multiple chrome.runtime.onMessage listeners - port disconnection issue

I am currently developing a chrome-extension that utilizes the Dexie indexedDB Wrapper, various jQuery Libraries, and syncfusion's eGrid to manage and display data. While I know this issue has been addressed multiple times in the past, I have encounte ...

SQL message comparison error within mysql module is malfunctioning

Currently, I am utilizing the nodejs MySQL module to automatically create a table every month. To ensure this process runs smoothly, I need to check if the table already exists using the condition err.sqlMessage == Table 'tableName' alre ...

Attempting to showcase a button element within a popover, although it is failing to appear

I have implemented a feature where a popover appears when hovering over an inbox icon (font-awesome). However, I am facing an issue with displaying a button on the second row within the popover. The title is showing up fine, but the button is not appearing ...

Exposing a factory JS variable globally within an AngularJS environment

I am trying to access a variable created within a factory in another function in my AngularJS controllers. How can I achieve this and make the new calculated value available? The variable I want to use is result.data.bkor_payamount = result.data.bkor_paya ...

How should values be properly stored in a constant using mongoose?

Within my user model, I have included timestamps. I am seeking a way to retrieve the createdAt date and store it in a variable. My initial attempt was: const date = await User.find({ serial: serialId, }).select('-_id createdAt'); The result re ...

Issue encountered when attempting to set a default value in Material-UI Text Field while interacting with Redux state?

Struggling to assign a defaultValue property for a Text Field using data from Redux state, but the updates are not reflecting as expected. The value is passed down as a prop from the container component to the edit component like this: render() { const ...

Unlock the ability to retrieve the current Ember route directly within a component

I have a unique custom Ember component embedded within a controller. Currently, I am attempting to dynamically retrieve the name of the current route. In order to access the controller, I use: this.get('parentView') However, I am facing diffi ...

Modifying Copyright Feature in Footer

I am attempting to implement this function within my dark-colored footer: import Typography from '@material-ui/core/Typography'; function Copyright() { return ( <Typography variant="body2" color="textSecondary" align="center"> ...

A guide on scheduling automatic data insertion in mongoose.js

I'm working on a website with Node.js and Mongoose, and I could use some guidance on how to automatically insert data into Mongoose with Node.js at specific times. For example, at the end of each month, I want my current data to be stored in the Mongo ...