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 the possibilities of using the typeof operator within an Event

I want to log some information if the input value is a number. However, I am facing an issue where it's not working and no bugs are appearing. Here is a snippet of code from CodePen (https://codepen.io/matoung/pen/KBNmPP) let button = document.que ...

Issue detected: Props that are of type Object/Array must utilize a factory function in order to provide the default value

I recently started using Vue-Cli3.0 and came across this interesting module for Vue.js called https://github.com/holiber/sl-vue-tree It's a customizable draggable tree component for Vue.js, but I encountered an issue where it couldn't copy funct ...

Having trouble showing the material-ui icon on my navigation menu

How can I use Material-UI icons like <AddOutlinedIcon /> in my nav menu without displaying the actual code before the menu name? Do I need to escape the icon code somehow to make it appear correctly? The intended result is to have a + icon displaye ...

Hiding Modal Box Upon User Login: A Step-by-Step Guide

After a user clicks the login button in my navigation, a modal box pops up. However, once the user logs in, the modal box does not disappear. How can I hide or remove the modal box when users click on the login button? This code snippet is from Home.vue: ...

Utilizing Laravel 5.3 and Vue.js for Dynamic AJAX Calls Based on Select Box Selections

I am looking to display a newly added record in a select box once it has been inserted into the table. Below is my Laravel HTML code: <div class="form gorup"> <select class="form-control" > <option @click="called" ...

Using JavaScript's FOR loop in combination with AJAX

I'm encountering an issue with using AJAX and a FOR loop. Within my PHP file, there are several if statements that return different prices based on a number range (1-9). For example: 1 -> echo "15.20"; 2 -> echo "11.10"; 3 -> echo "13.65"; ...

Convert items to an array utilizing lodash

I need assistance converting an object into an array format. Here is the input object: { "index": { "0": 40, "1": 242 }, "TID": { "0": "11", "1": "22" }, "DepartureCity": { "0": "MCI", "1": "CVG" }, "ArrivalCity": { ...

When using the android phonegap application, the screen interface may not render properly and display a patch on the keypad area when the keypad is

Whenever I tap on a textbox, the system keypad pops up. But after entering text, if I click on any link instead of pressing the Go button on the keypad, the keypad disappears but leaves behind a random area or displays the previous screen. When navigating ...

Encountering the error message "Uncaught Error: [vuex] Getters must be functions, but 'getters.getters' is {}. This occurred while splitting the Vuex store into modules in Vue.js."

As a beginner in VUEX, I am experimenting with building a test application to dive deeper into the world of VUEX. I have organized my VUEX store into modules, where each module has its own getter.js file. Getters, actions, and mutations are imported into i ...

Having trouble with the import of the directory's index file?

The code snippet in application.js indicates that the "home" imported from "./routes/index" is undefined. import {home} from "./routes/index" console.log(JSON.stringify(home, null, 4)) This is what index.js contains: export * from "./home.js" And here ...

What sets apart the browser/tab close event from the refresh event?

Can you help me understand the difference between a browser/tab close event and a refresh event? I've been researching this on Stack Overflow, but I'm still having trouble with it. My goal is to be able to log out a user through a server call whe ...

establishing status within enclosed reaction

In the process of developing a react application, I am encountering difficulties in correctly setting the state with the nested response data received from an api. The state is not aligning as desired. Here is the sample response obtained from the api: [ ...

Enhancing Performance with Web Workers in Angular CLI

Lately, I've been tackling a challenging issue with my Angular app - heavy computation on the client side leading to UI blockage. My solution? Utilizing Web Workers in my Angular CLI project to separate UI tasks into one thread and heavy processing in ...

The design of RESTful web applications with a client-server architecture

I need clarification on how client-server architecture should function for a modern web application with a RESTful backend. For a web app, the client is typically the browser while the server is the web server. Programatically speaking, there are componen ...

A guide on removing a database row using Angular and PHP

I am currently working on a project that involves the combination of Angular and PHP. One issue I have encountered is when trying to delete a row from a list of customers displayed on an HTML page. Despite clicking the "delete" button, no deletion occurs, ...

The functionality of data-ng-trim="false" seems ineffective within an Angular context

Need assistance with my HTML form: <form name="signInForm" novalidate=""> <div class="form-group has-feedback has-feedback-left"> <label class="control-label sr-only">Email address</label> <input type="email" class="form-control ...

I am having trouble scrolling through the main content when the side-drawer is open. How can I fix this issue?

When the sidebar is opened, I am facing issues with the main content scroll and certain fields such as select options and search bar not functioning properly. I have included the main content in the routes from which it is being loaded. However, the scroll ...

Using the ternary operator in React to implement inline styles

Within my React/Typescript project, I aim to dynamically exhibit a color based on the presence or absence of a value in payload[1]. In the code snippet below, note the usage of an inline style tag. <li className="recharts-tooltip-item" style={ ...

Exploring the potential of VSCode's RegEx search and replace

I am working on an Angular translation file and need to perform a search and replace operation in VScode for the translate key. The goal is to extract only the final key and use it in the replacement. The keys are structured with a maximum depth of 3 level ...

Converting JavaScript code to React requires excluding jQuery

I have been working on updating my old JavaScript code to make it compatible with React. The main goal is to integrate external data into a 3rd party form. Here is the original JS code: <script charset="utf-8" type="text/javascript" ...