The angular controller function is failing to set $scope.value

I've been facing an issue with setting an Angular variable value in a controller function that is created by a directive. For some reason, it doesn't seem to work when I try to assign the value within the controller function, even though it displays correctly when set independently.

Below is my code snippet:

<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>

</head>
<body>
    <div ng-app="mainApp">
        <div ng-controller="MyController">
            <div id="Details" class="Details">{{Details}}</div></br>
             <div id="Test" class="Test">
             <collection collection='testdata'></collection>             
             </div>          
        </div>              
    </div>
</body>
<script>    
var mainApp = angular.module("mainApp", [])

mainApp.directive('collection', function () {
    return {
        restrict: "E",
        replace: true,
        scope: {collection: '=', showFn : '&'},
        template: "<ul><member ng-repeat='member in collection' member='member'></member></ul>"         
    }
})

mainApp.directive('member', function ($compile) {
    var NewChild = "<li><span ng-click=ShowDetailsFunc()>{{member.TagName}}</span></li>";

    return {
        restrict: "E",
        replace: true,
        scope: {member: '=', ShowHideCtrlFunc : '&', ShowDetailsCtrlFunc : '&'},
        template: NewChild,
        controller: 'MyController',
        link: function (scope, element, attrs) {            
            var collectionSt = '<collection collection="member.children"></collection>';
            if (angular.isArray(scope.member.children)) {               
                    $compile(collectionSt)(scope, function(cloned, scope)   {                                           
                        element.attr('xml-path', scope.member.TagPath);
                        element.append(cloned);                         
                     });                      
                    scope.ShowDetailsFunc = function() {
                        scope.ShowDetailsCtrlFunc(element,event);                     
                    }

            }
        }   

    }
})

mainApp.controller('MyController', function ($scope) {
    $scope.testdata = [{"TagName":"MyRootNode","TagPath":">MyRootNode","children":[{"TagName":"LandXML","TagPath":">MyRootNode>ChildItems>LandXML","children":[{"TagName":"Units","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Units","children":[{"TagName":"Imperial","TagPath":">MyRootNode>ChildItems>...

    $scope.Details = "defalut value"
    $scope.ShowDetailsCtrlFunc = function(element,event) {
            console.log("in function ShowDetailsCtrlFunc");                     
            var myxmlpath = $(element).attr("xml-path")
            $scope.Details = getObjects($scope.testdata, 'TagPath', myxmlpath)[0].TagName; 
            console.log($scope.Details)                         
            //event.stopImmediatePropagation();
      };    
});
function getObjects(obj, key, val) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val));
        } else if (i == key && obj[key] == val) {
            objects.push(obj);
        }
    }
    return objects;
}   
</script>   
</html>

I would really appreciate any guidance on where I might be going wrong and how I can correct this issue. Thank you so much in advance for your help!

Answer №1

The issue arises from nesting the MyController-controller when setting up the directives in a circular manner. As a result, $scope.Details will be assigned to the specific nested scope instead of the one intended for displaying the value in the view.

To resolve this, you can use $emit to propagate the change upwards, eventually reaching the desired scope responsible for presenting the value.

Here's an example:

// Angular module creation
var mainApp = angular.module("mainApp", [])

// Custom directive 'collection'
mainApp.directive('collection', function () {
    return {
        restrict: "E",
        replace: true,
        scope: {collection: '=', showFn : '&'},
        template: "<ul><member ng-repeat='member in collection' member='member'></member></ul>"         
    }
})

// Custom directive 'member'
mainApp.directive('member', function ($compile) {
    var NewChild = "<li><span ng-click=ShowDetailsFunc()>{{member.TagName}}</span></li>";

    return {
        restrict: "E",
        replace: true,
        scope: {member: '=', ShowHideCtrlFunc : '&', ShowDetailsCtrlFunc : '&'},
        template: NewChild,
        controller: 'MyController',
        link: function (scope, element, attrs) {            
            var collectionSt = '<collection collection="member.children"></collection>';
            if (angular.isArray(scope.member.children)) {               
                    $compile(collectionSt)(scope, function(cloned, scope)   {                                           
                        element.attr('xml-path', scope.member.TagPath);
                        element.append(cloned);                         
                     });                      
                    scope.ShowDetailsFunc = function() {
                        scope.ShowDetailsCtrlFunc(element);                     
                    }

            }
        }   

    }
})

// Controller definition 'MyController'
mainApp.controller('MyController', function ($scope) {
    // Test data provided for demonstration
    $scope.testdata = [{"TagName":"MyRootNode","TagPath":">MyRootNode","children":[{"TagName":"LandXML","TagPath":">MyRootNode>ChildItems>LandXML","children":[{"TagName":"Units","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Units","children":[{"TagName":"Imperial","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[1]>Imperial","children":[]},{"TagName":"Project","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Project","children":[]},{"TagName":"Application","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Application","children":[{"TagName":"Author","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[2]>Author","children":[]},{"TagName":"Alignments","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Alignments","children":[]},{"TagName":"Roadways","TagPath":">MyRootNode>ChildItems>ChildItems[1]>Roadways","children":[{"TagName":"Roadway","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[3]>Roadway[1]","children":[]},{"TagName":"Roadway","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[3]>Roadway[2]","children":[]},{"TagName":"Roadway","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[3]>Roadway[3]","children":[]},{"TagName":"Roadway","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[3]>Roadway[4]","children":[]},{"TagName":"Roadway","TagPath":">MyRootNode>ChildItems>ChildItems[1]>ChildItems[3]>Roadway[5]","children":[]}]}]}]}]},{"TagName":"Surfaces","TagPath":">MyRootNode>ChildItems>Surfaces","children":[{"TagName":"Surface1","TagPath":">MyRootNode>ChildItems>ChildItems[2]>Surface1","children":[]},{"TagName":"Surface2","TagPath":">MyRootNode>ChildItems>ChildItems[2]>Surface2","children":[]}]}]}]

    // Default value for Details
    $scope.Details = "default value";
    
    // Function to handle showing details
    $scope.ShowDetailsCtrlFunc = function(element) {
            console.log("in function ShowDetailsCtrlFunc");                     
            var myxmlpath = angular.element(element).attr("xml-path")
            var detail = getObjects($scope.testdata, 'TagPath', myxmlpath)[0].TagName; 
            console.log(detail);
            $scope.$emit('detailSelected',detail);
            // event.stopImmediatePropagation();
      };
      
      // Listening for 'detailSelected' event
      $scope.$on('detailSelected',function($event, message){
          $scope.Details = message;
    });
});

// Recursive function to retrieve objects based on key and value
function getObjects(obj, key, val) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val));
        } else if (i == key && obj[key] == val) {
            objects.push(obj);
        }
    }
    return objects;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<script data-require="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bcddd2dbc9d0ddced6cffc8d928a89285853945e92949572cc4c41" rel="noopener noreferrer">[email protected]</a>" data-semver="1.6.4" src="https://code.angularjs.org/1.6.4/angular.min.js"></script>

</head>
<body>
    <div ng-app="mainApp">
        <div ng-controller="MyController">
            <div id="Details" class="Details">{{Details}}</div><br/>
             <div id="Test" class="Test">
             <collection collection='testdata'></collection>             
             </div>          
        </div>              
    </div>
</body>
  
</html>

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

Exploring issues with jQuery

I'm encountering an issue with my code. I have multiple divs with the same classes, and when I click on (toggle#1) with the .comments-toggle class, all divs below toggle-container expand. What I actually want is for only the div directly below .commen ...

"Troubleshooting IE-specific problem: Loading dropdown options dynamically with AJAX on change

Struggling with a particular issue lately. I'm attempting to populate a second dropdown menu based on the selection made in the first one using jquery ajax. Surprisingly, it works flawlessly on all browsers except for IE 11. Below is the ajax functio ...

Creating a seamless and interactive online platform

I am in the process of designing a website that has a sleek and dynamic layout, rather than just a static homepage. Let me explain further: Here is my current setup so you can understand what I am trying to achieve. By dynamic, I mean that when the page ...

Using the base tag in Angular HTML5 mode can cause script links to break

Issue Following the configuration of a base tag to enable Angular 1.x in HTML5 mode, encountering an error where the browser (Chrome) sends requests to an incorrect path for scripts when directly accessing the app via localhost:3000/resource/id. Specific ...

Expressing the assignment of arrays inside the req.body object to separate variables

I've been facing some challenges trying to get Express and body-parser to properly interpret the JSON object sent from the Angular side of my app. It seems like there might be an issue with how I'm assigning variables in my syntax. Despite trying ...

Create a div element within the parent window of the iFrame

I'm trying to figure out how I can click a button within an iFrame that contains the following code: <td class="id-center"> <div class="bs-example"> <a id="comments" href="comments.php?id=$id" name="commen ...

execute field function prior to sorting

Currently, I am building a graphql server in express and using a resolver to modify my fields based on user input from the query. The issue arises from the transformer function returning a function. My goal is to sort the results by a field determined by ...

Is there a way to conceal JavaScript code?

I am trying to keep my AJAX code hidden from users who view the source of my PHP page. How can I achieve this? Below is the AJAX code that I currently have: <script type="text/javascript"> function Ajax(){ var xmlHttp; try{ xmlHttp=n ...

How can I prevent the same JavaScript from loading twice in PHP, JavaScript, and HTML when using `<script>'?

Is there a PHP equivalent of require_once or include_once for JavaScript within the <script> tag? While I understand that <script> is part of HTML, I'm curious if such functionality exists in either PHP or HTML. I am looking to avoid load ...

Error: The combination of 0 and .... is invalid and cannot be used as a function

I am currently in the process of developing a next.js application using Material-ui. I have been attempting to integrate material-ui into my project. Following guidance from the official GitHub page, I have copied the _app.js , _document.js , theme.js fil ...

What is the best way to control the overflow length and display only a specific amount of content at once?

I am currently designing the homepage for a social media platform. I have around 45 dummy posts and I am looking for a way to limit the overflow in CSS so that only 5 posts are displayed at a time. Once the user reaches the bottom of those 5 posts, another ...

Generating random numbers within specified character limits using Javascript and Selenium

Just starting to learn javascript - I am working on creating random user IDs for my selenium test scenarios. The line of code I am using for the value is as follows: javascript{Math.floor(Math.random()*11111)} However, there is a specific field that need ...

Unable to integrate npm package into Nuxt.js, encountering issues with [vue-star-rating] plugin

Just starting with nuxt js and running into issues when trying to add npm packages. Below are my attempts. star-raing.js import Vue from 'vue' import StarsRatings from 'vue-star-rating' Vue.use(StarsRatings) nuxt.config.js plugi ...

Dealing with multiple POST requests at once in Node Express JS - Best Practices

I've been working on a Node project with Express to handle incoming GET/POST requests. I have set up routes to manage various types of requests. One specific route, /api/twitter/search, calls a function that uses promises to retrieve Twitter feeds and ...

Preserve the chosen option in a dropdown menu even after a postback using JavaScript

Seeking Help in Retaining Dropdownlist Selected Value After Postback In my efforts to retain a dropdownlist selected value after postback, I have been exploring various methods. I extract the selected values from the dropdownlist and store them in local ...

Using Ionic to create a master-detail layout with a side menu

Trying to wrap my head around the master-detail (MD) pattern in Ionic with a side menu. The example code uses 'Playlists' as the master and 'Playlist' as the detail. The states are set up like this: .config(function($stateProvider, $ur ...

What is the best way to maintain a Tampermonkey script within Selenium?

I'm trying to execute a JavaScript script before the page loads, so I've placed it in Tampermonkey. However, the script doesn't persist after closing the driver. Whenever I run the code again, the saved script is no longer there. This code u ...

Experimenting with an Angular controller while maintaining its dependency integrity without the use

After spending 48 hours scouring the internet for answers to my question without success, I find myself seeking help here. The controller I am trying to test is as follows: (function () { "use strict"; angular .module("productManagement" ...

Issue: Anticipated the primary reducer to be a function, but was presented with: 'undefined' in Vanilla JS with Redux

Exploring the world of Redux, I decided to try out some code from a tutorial on YouTube. Building a simple Vanilla JS App, I integrated Redux into it. However, upon running the app in the terminal, an error message popped up saying: "Error: Expected the ro ...

JavaScript Algorithm for Pyramids

Experimenting with simple algorithms brought me to this code. I'm curious if anyone has a more concise version in JavaScript for displaying a pyramid: const max= 30; let row = 1; for (let i = 1; i < max; i += 2) { console.log(' '.r ...