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

Utilizing JSON data as a variable for handling in a Handlebars view within a Node.js/Express application

I am currently seeking a solution to display a view that includes a variable with data fetched from an API. My technology stack involves express, handlebars, and request. Here is the code for the web server's router: const express = require('ex ...

Modify the button text when it is hovered over

I am attempting to modify the text displayed on a button when hovering over it in a React component from Ant Design. However, I have not been successful so far. Button <div className={ status == "verified" ? `${styles.btn1} ${styles.btn1C ...

The resize function fails to trigger when it is required

Struggling to get this code working properly. If the window width is greater than 800, I want 6 images with a red background. If the window width is less than 800, I want 4 images with a blue background. I need this functionality to work both on r ...

Encountering a "Unable to use import statement outside a module" issue when trying to import react-hook-mousetrap within a Next.js project

Currently experimenting with Next.js but encountering some challenges. Recently attempted to add react-hook-mousetrap and imported it as per usual: import useMousetrap from "react-hook-mousetrap"; However, this resulted in the following error: S ...

Discover the step-by-step process of combining an array with JSON to create the desired outcome

I am working with a JSON array that looks like this: var row={ shopId: 3, shopName: '1', address: 'abc', contactNumber: 1234 } Alongside, I have another array: var data= [ { imageId: 1, shopId: 3, imageUrl: 'aaa' }, ...

Get a reference to pass as an injection into a child component using Vue js

Is there a way to pass a reference to child components? For example: The Parent component provides the ref: <template> <div ref="myRef" /> </template> <script> export default { name: 'SearchContainer', pr ...

Tips for customizing the AjaxComplete function for individual ajax calls

I need help figuring out how to display various loading symbols depending on the ajax call on my website. Currently, I only have a default loading symbol that appears in a fixed window at the center of the screen. The issue arises because I have multiple ...

Perform DOM manipulation prior to triggering the AJAX event to prevent CSRF Error

Currently, I am faced with a challenge while working on Django. My goal is to implement a chained AJAX call - meaning that once one call returns, it triggers additional AJAX calls. Despite thorough research and referencing the suggested methods in the Djan ...

Only a fragment of the .attr() method

I am trying to work with an image HTML block <img src="folder1/folder2/folder3/logo1.png"> situated inside a large div similar to this structure <div id="editorial"> <div id="img_editorial"><img src="folder1/folder2/folder3/logo1. ...

I am looking for a custom script for a splash page that will automatically redirect users to one of three designated pages based on the information stored

As a programmer, I'm well-versed in coding but lack knowledge about creating and utilizing cookies. If anyone could provide guidance on this matter, it would be highly appreciated. I believe I require two concise scripts for this task. 1st Script: T ...

Data object constructor is not triggered during JSON parsing

Currently, I am retrieving data from a server and then parsing it into TypeScript classes. To incorporate inheritance in my classes, each class must be capable of reporting its type. Let me explain the process: Starting with the base class import { PageE ...

When making an HTTP GET request followed by another GET request in Express, it results in an error with undefined parameters on the

When I open a form that has a link to a list and try to access the list, I encounter an "id" undefined error for the form we came from, which was already functional. The issue arises when I have a GET page where I present a form to modify a record at /loc ...

xhttp.load path for server-side module

I'm currently working on developing a node package and in my JavaScript code, I have the following: const calcHtml = './calc.html'; const xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4) { ...

I encountered an issue while attempting to fetch table data, receiving the following error message: "Uncaught TypeError: result.rows.product is not a function at products.html:134."

https://i.sstatic.net/WZ5CC.pngHere is the HTML I have written <form> <br/> <label for="products1">Product Name:</label> <input type="text" name="pdt" id="pr ...

Why does Googlebot need to retrieve HTML from JSON-exclusive URLs?

Link to a page like this: The following code is found: $.getJSON("/newsfeeds/61?order=activity&amp;type=discussion", function(response) { $(".discussion-post-stream").replaceWith($(response.newsfeed_html)); $(".stream-posts").before($("<div cl ...

When an unrelated value is changed, the Vue 2 dynamic component experiences a loss of its values during the refresh

I've been grappling with this issue for quite some time now and I'm beginning to suspect it might be a bug. I'm currently utilizing a dynamic vue component to substitute markers in a body of text with input fields. The functionality seems t ...

Customize date filtering in KendoUI grid

I am trying to modify the date format in the filter of my kendo grid. For example, I would like to change 1/30/2015 to Jan 30, 2015 I have successfully changed the date format for Start Date field: "StartDate", title: " ...

Unable to reach elements that have been loaded through ajax with jQuery

I am facing an issue where I cannot access content loaded via ajax for modification. Unfortunately, I do not have access to the js file responsible for the initial load. Therefore, I need to create a separate function to alter the content. The required mo ...

What is preventing the control from being passed back from the PHP file to the AJAX success function?

My website is built using PHP, Javascript, and AJAX. Below is the essential code snippet: JS code (AJAX function): $("#btn_add_event").click(function(){ var strSeriaze = $( "#formAddEvent" ).serialize(); url = $( "#formAddEvent" ).attr('act ...

Creating a dynamic input box that appears when another input box is being filled

I have a form set up like this: <FORM method="post"> <TABLE> <TR> <TD>Username</TD> <TD><INPUT type="text" value="" name="username" title="Enter Username"/><TD> </TR> <TR> <TD>A ...