Executing a method from the parent controller within nested directives by utilizing isolated scope

I need help with a treeview directive in my project. I am having trouble invoking a function from the parent controller and can't seem to figure out why it's not working. It could be due to the structure of the treeview and nesting of child elements.

In my HTML, I have declared the directive as:

<div ng-controller="treeController as vm">
    <tree src="myList" filter="doSomething()"></tree>
    <a ng-click="clicked()"> link</a>
</div>

I have defined an attribute/parameter filter in the directive that should call the doSomething() function in the main controller.

The main controller code includes:

app.controller("treeController", ['$scope', function($scope) {

    var vm = this;

   $scope.doSomething = function () {
        var item = data;
   }

   $scope.clicked = function () {
       alert('clicked');
   }

        $scope.myList = {
            children: [
              {
                  name: "Event",
                  children: [
                    {
                        name: "Event Date",
                        children: [
                          {
                              name: "2008",
                              FilterType: '_eventStartDate',
                              Parent: '_event'
                          },
                          {
                              name: "2009",
                              FilterType: '_eventStartDate',
                              Parent: '_event'
                          }
                        ]
                    },
                    {
                        name: "Event Attendee",
                        children: [
                          {
                              name: "Person 1",
                              FilterType: '_eventAttenddeeName',
                              Parent: '_Event'
                          },
                          {
                              name: "Person 2",
                              FilterType: '_eventAttenddeeName',
                              Parent: '_Event'
                          }
                        ]
                    }
                  ]
              }]
        };
}]);

In the directive, I declare the isolated scope and the parameter filter using model binding prefix '&'. I then use ng-click within the template to invoke the doSomething() function in the main controller. However, the function is not being called.

app.directive('tree', function() {
//builds the tree
    return {
        restrict: 'E', 
        replace: true,
        scope: {
            t: '=src'
        },
        template: '<ul><branch ng-repeat="c in t.children" src="c"></branch></ul>'
    };
});

app.directive('branch', function($compile) {
//directive that builds the children/branches
    return {
        restrict: 'E', 
        replace: true, 
        scope: {
            b: '=src',
            filter: '&'
        },
        template: '<li><input type="checkbox" ng-click="filter()" ng-hide="visible" /><a>{{ b.name }}</a></li>',
        link: function (scope, element, attrs) {

           var has_children = angular.isArray(scope.b.children);
            scope.visible = has_children;
            if (has_children) {
                element.append('<tree src="b"></tree>');

                $compile(element.contents())(scope);
            }

            element.on('click', function(event) {
                event.stopPropagation();

                if (has_children) {
                    element.toggleClass('collapsed');
                }
            });
            //test to call function within directive
            //scope.doSomething = function(b) {
            //    alert('test');
            //}
        }
    };
});

A demo of the code sample can be found on jsFiddle.

If you have any suggestions or solutions to why the function in my controller is not getting called, please let me know.

Currently, I am focused on invoking the method, but eventually, I will need to pass the selected item back to the controller as well.

Update: It was recommended to move the declaration of the filter from the branch to the tree directive.

I made changes locally to the tree directive as follows:

app.directive('tree', function() {
    return {
        restrict: 'E', 
        replace: true,
        scope: {
            t: '=src',
            filter: '&'
        },
        template: '<ul><branch ng-repeat="c in t.children" src="c"></branch></ul>'
    };
});

Note: the filter parameter was removed from the secondary directive, but there was no change in the output. The function in the controller still wasn't being called.

Answer №1

It appears that your tree directive is missing a filter method, as only your branch directive contains that property.

<div ng-controller="treeController as vm">
    <tree src="myList" filter="doSomething()"></tree>
    <a ng-click="clicked()"> link</a>
</div>

app.directive('tree', function() {
//builds the tree
    return {
        restrict: 'E', 
        replace: true,
        scope: {
            t: '=src',
            filter: '&'
        },
        template: '<ul><branch ng-repeat="c in t.children" src="c" filter="doSomething()"></branch></ul>'
    };
});

app.directive('branch', function($compile) {
//directive that builds the children/branches
    return {
        restrict: 'E', 
        replace: true, 
        scope: {
            b: '=src',
            filter: '&'
        },
        template: '<li><input type="checkbox" ng-click="filter()" ng-hide="visible" /><a>{{ b.name }}</a></li>',
        link: function (scope, element, attrs) {

           var has_children = angular.isArray(scope.b.children);
            scope.visible = has_children;
            if (has_children) {
                element.append('<tree src="b"></tree>');

                $compile(element.contents())(scope);
            }

            element.on('click', function(event) {
                event.stopPropagation();

                if (has_children) {
                    element.toggleClass('collapsed');
                }
            });
            //test to call function within directive
            //scope.doSomething = function(b) {
            //    alert('test');
            //}
        }
    };
});

Answer №2

Latest Update: After receiving advice from Sundar, I was able to make progress in the right direction with my directive. The issue that I faced involved working with nested directives, where the nested item making the function call was out of scope for the controller. To resolve this, I implemented Sundar's suggestions and explicitly defined the controller at the parent directive level to ensure functionality within the nested directive. While I acknowledge that this solution may not be optimal for situations requiring the directive to be used in multiple areas of an application, it suits my current needs as the directive is only utilized in one specific location. Any alternative suggestions or improvements are welcome.

app.directive('tree', function() {
    return {
        restrict: 'E', 
        replace: true,
        scope: {
            t: '=src',
            filter: '&'
        },
        controller:'treeController', //explicitly set the controller of the parent directive
        template: '<ul><branch ng-repeat="c in t.children" src="c" filter="doSomething(data)"></branch></ul>'
    };
});

app.directive('branch', function($compile) {
    return {
        restrict: 'E', 
        replace: true, 
        scope: {
            b: '=src',
            filter: '&'
        },

        template: '<li><input type="checkbox" ng-click="innerCall()" ng-hide="visible" /><a>{{ b.name }}</a></li>',
        link: function (scope, element, attrs) {

           var has_children = angular.isArray(scope.b.children);
            scope.visible = has_children;
            if (has_children) {
                element.append('<tree src="b"></tree>');

                $compile(element.contents())(scope);
            }

            element.on('click', function(event) {
                event.stopPropagation();

                if (has_children) {
                    element.toggleClass('collapsed');
                }
            });
            scope.innerCall = function() {
                scope.filter(scope.b);
            }
        }
    };
});

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

Consolidate radio group in Vuetify platform

I'm having trouble centering the v-radio-group. Here's my current setup: <v-container grid-list-md text-xs-center> <v-form ref="form"> <div v-if="question.question_type == 'YESNO' "> <v-radio-group ...

A guide on implementing nested child routes in AngularJS 2

I have successfully completed routing for two children, but now I want to display nested routes for those children. For example: home child1 child2 | grand child | grand child(1) ...

Breaking apart web addresses and their attached parameters

I am attempting to extract the part of a URL that comes after the last forward slash (/) and before the querystring. While I have been successful in obtaining the last segment of the URL, I have encountered difficulty in removing the querystring from it. ...

What causes fs to produce an error when routing to a new page, yet refreshing the page resolves the issue?

Concern: I have developed a NextJs application with 4 input fields, each connected to a predefined options list read in as a json file within the project. The user can select two fields and then proceed to a search page by clicking a button. From the sear ...

The display function in Javascript has a tendency to work sporadically

I’ve been tasked with creating my own tic tac toe game through coding. While I am relatively new to programming, I have a strong passion for it. At the moment, I've set up a basic function to hide only the "O", leaving only the "X" visible on the gr ...

Sticky sidebar panel featuring a stationary content block

I have a sidebar that is set to position:fixed; and overflow:auto;, causing the scrolling to occur within the fixed element. When the sidebar is activated, the element remains static on the page without any movement. My goal: I am looking to keep the .su ...

Embarking on a New Project with Cutting-Edge Technologies: Angular, Node.js/Express, Webpack, and Types

Recently, I've been following tutorials by Maximilian on Udemy for guidance. However, I have encountered a roadblock while trying to set up a new project from scratch involving a Node/Express and Angular 4 application. The issue seems to stem from the ...

When integrating string variables into JavaScript regular expressions in Qualtrics, they seem to mysteriously vanish

I have been working on a project to analyze survey responses in Qualtrics by counting the number of matches to specific regular expressions. For example, whenever phrases like "I think...", "In my opinion," are used, the count increases by one. Below is t ...

Chatting with a Discord bot

I am currently working on a Discord bot that will execute specific functions based on the questions asked, most of which are yes or no queries. Upon responding with "yes," a particular function should be performed, while answering "no" would terminate t ...

Notifying a Child Component in React When a Props-Using Function Resolves a REST Call

When I submit an item or problem, I trigger a function in the parent component that has been passed down to the child component as props. After sending my information, I want to clear the input fields. The issue is that I am clearing the input fields immed ...

When applying a cell formatter to change the color of a Tabulator cell, the text displayed is being

I am attempting to dynamically change the color of a tabulator cell based on its input. My initial approach was to simply try changing the cell's color. After running the following code, here is what I observed: function testFormatter(cell, formatt ...

The PHP script is not receiving the post parameters sent in the ajax request

Help Needed jQuery.ajax({ url: 'PHPdocs/appsearch.php', data: {term:'blub'}, type: "POST", async: true, data: "text", succes ...

Ways to ensure that ng-click is triggered exclusively on the click event

I am a beginner in Angular and attempting to toggle a class on click only for the current link. However, when I click, it is affecting all links instead of just the current one. I would like it to work only on the current element, similar to how we use "(t ...

Show or hide the expand/collapse button based on the height of the container

Looking for a way to hide content in a Div if it's taller than 68px and display an expand option? The challenge lies in detecting the height of the responsive Div, especially since character count varies. I attempted using PHP to count characters bu ...

Showing information from the data text option

I'm having trouble displaying text below the cube. The code works fine in a standalone fiddle, but it doesn't work when I incorporate it into my project. Can someone help me figure out how to show the value of data-text="Cube1"? Code t ...

Unable to retrieve object element in angular

weatherApp.controller('forecastController', ['$scope','weatherService','$resource','$log', function($scope,weatherService,$resource,$log){ var cnto =3; $scope.forecastholder = weatherService.holder; $scope ...

Guide on how to showcase the chosen option of a dropdown menu in a table by clicking an arrow icon

I am looking to modify the code below so that instead of pushing data to the selected panel, it pushes data to a table inside the panel. The new data should be added to a new row every time the arrow is clicked. <html> <head> <title>Bo ...

Dividing the logic from the Express router while retaining the ability to utilize Express functionalities

As I embark on my journey of developing my first app using Node.js and Express, I have noticed that my router file is starting to get overcrowded with logic. It seems like there is too much going on in there. My solution to this issue is to pass a functio ...

Learn how to cycle through three different texts that appear in the same spot using smooth transitions

I am working with three different rows that contain the Typography component. My goal is to display the first row, followed by the second, then the third, and back to the first one in a continuous loop. All three rows should be shown in the same location, ...

Toggling with Jquery when an image is clicked

I'm trying to wrap my head around the functionality of jquery toggle. My goal is to toggle to the next anchor element with the class plr-anchor when an image with the class go_down is clicked. The information is being populated using maps. Javascript ...