Gaining access to the isolated scope of a sibling through the same Angular directive led to a valuable discovery

I am currently working on an angularjs directive that creates a multi-select dropdown with a complex template. The directives have isolated scopes and there is a variable called open in the dropdown that toggles its visibility based on clicks. Currently, the dropdown only closes when the DOM is clicked. However, if a user clicks on two dropdowns consecutively, both remain open. To close the sibling dropdown, I need to access its scope and set the value for open. How can I access the isolated scope of a sibling originating from the same directive?

var directiveModule = angular.module('angular-drp-multiselect', []);
directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse',
function ($filter, $document, $compile, $parse) {

    return {
        restrict: 'AE',
        scope: {
            selectedModel: '=',
            options: '=',
            extraSettings: '=',
            events: '=',
            searchFilter: '=?',
            translationTexts: '=',
            groupBy: '@'                
        },
        template: function (element, attrs) {
            var checkboxes = attrs.checkboxes ? true : false;
            var groups = attrs.groupBy ? true : false;

            var template = '<div class="multiselect-parent btn-group dropdown-multiselect btn-block ">';
            template += '<button type="button" ng-disabled="getDisableStatus()" class="dropdown-toggle btn-block btn-leftAlign" ng-class="settings.buttonClasses" ng-click="HideAllOpenDropDowns();toggleDropdown($event)" ng-attr-title="{{getButtonTitle()}}">{{getButtonText()}}&nbsp;<span class="caret"></span></button>';
            template += '<ul class="dropdown-menu dropdown-menu-form" ng-data="{{open}}"  ng-style="{display: open ? \'block\' : \'none\', height : settings.scrollable ? settings.scrollableHeight : \'auto\' }" style="overflow: scroll" >';
            template += '<li ng-hide="!settings.showCheckAll || settings.selectionLimit > 0"><a data-ng-click="selectAll()">  {{texts.checkAll}}</a>';
           .....
            element.html(template);
        },
        link: function ($scope, $element, $attrs) {
            var $dropdownTrigger = $element.children()[0];

            $scope.toggleDropdown = function () {
            //here I need to access all siblings(generated from the same directive) scope , to control the visibility, by setting value for $scope.open
            //I tried the following things
            --------------------------------------
            //Attempt 1- not working
            angular.forEach(angular.element($element.parent().parent().children()), function (value, key) {
                var x = angular.element(value).scope();
                    x.open = false;
                     //x.$apply(function () {
                        // x.open = false;
                     //});
                     }
            //Attempt  2- not working
            angular.forEach(angular.element($(".multiselect-parent")), function (value, key) {

                var menuElem = angular.element(value);
                var menuElemScope = menuElem.scope();
                menuElemScope.$apply(function () {
                    menuElemScope.open = false;
                });
            });


            --------------------------------------
              $scope.open = !$scope.open;
            };
            ...
            ...

The HTML structure is as follows:

<div ng-app="multiSelectApp">
        <div ng-controller="MultiSelect">

        <div ng-dropdown-multiselect="" 
            extra-settings="DropDownSettings1" >
        </div>

        <div ng-dropdown-multiselect="" 
            extra-settings="DropDownSettings2" >
        </div>

        <div ng-dropdown-multiselect="" 
            extra-settings="DropDownSettings3" >
        </div>

        </div>

        </div>

Answer №1

When it comes to manipulating DOM elements that affect a sibling directive, some may argue that it's not the most Angular way to go about it.

One possible alternative approach is shown below:

link: function(element, attrs){
    ....
    $rootScope.$on('openChanged', function(event, open){
        scope.isOpen = open;
    });
    scope.toggleOpen = function(){
        var wasOpen = scope.isOpen;
        $rootScope.$broadcast('openChanged', false);
        scope.isOpen = !wasOpen;
    }

For larger applications, it might be recommended to create a service for communication instead of relying on $rootScope.

Here's a Fiddle showcasing an example implementation.

UPDATE
In terms of implementing a service,
It's worth considering using this package to avoid reinventing the wheel.

However, if you prefer, here's a quick solution snippet that demonstrates something similar:

.factory('broadcastService', function () {
    var handlers = {};
    this.on = function (eventName, callback) {
        var callbacks = handlers[eventName];
        if (!callbacks) {
            handlers[eventName] = callbacks = [];
        }
        if (typeof callback === 'function' && callbacks.indexOf(callback) < 0) {
            callbacks.push(callback);
        }
    };
    this.broadcast = function (eventName, args) {
        var callbacks = handlers[eventName];
        if (callbacks) {
            callbacks.map(function (handler) {
                try {
                    handler({
                        name: eventName
                    }, args);
                } catch (ex) {
                    console.error(ex);
                }
            });
        }
    }
})

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

Display the value of a shortened string

My Goal I aim to develop a method for determining the amount of a string visible before it gets cut off. Motivation In my project, there is a collection of items that can be chosen. A panel presents a concatenated string of the selected item names separa ...

Show various Bootstrap Alert Messages based on the type of error detected?

Trying to figure out how best to explain this, here it goes! I am working on a website and I want to incorporate alert messages. Check out some examples here. Depending on the form inputs, I need different alerts to be displayed. PHP will validate the in ...

Creating an animated link with a dynamic underline featuring a trio of vibrant colors

I'm having trouble figuring out how to animate my links on hover with 3 different colors. I attempted using the linear-gradient property but it doesn't seem to be working. Any suggestions on how to proceed? Below is an example of what I'm a ...

Error encountered: `unexpected token within ES6 map() function`

Do you see any issues with this code snippet? render(){ return ( var users= this.state.users.map(user => <li key={user.id}>{user.name}</li> ) <ul>{users}</ul> ) } I am receiving an error mes ...

The upload directory in KCFinder can be accessed and selected using a URL

Issue: While using the link to open kcfinder as defined in kcfinder, I encountered a problem. The link is intended to open kcfinder with the provided parameters: window.open('/kcfinder/browse.php?type=files&dir=files/public&subDi ...

When Electron's WebView.loadURL is called, it initiates a reloaded page

According to the documentation provided by Electron, it is necessary to wait until the webview element is available in order to utilize its respective methods. While most methods function properly, I am facing challenges in understanding how to programmati ...

Troubleshooting TextField malfunctioning after button click within Dialog on Material UI

My main objective is to focus on a Material UI TextField after closing a Dialog by clicking a button inside the Dialog. The code snippet below successfully accomplishes this task when triggered from a button that is not within a dialog component: focusOn ...

Forwarding from a user interface element in Next.JS

I am currently working on a project utilizing Next.js 13, and I have encountered a situation where I need to invoke a client-side component from a server-side page. The specific component in question is the DeleteAddressAlertDialog which interacts with my ...

Error in Node.js: Cannot listen on socket.io object as it does not have a 'listen' method

I am currently developing a Node application using socket.io version 0.9.13 alongside Express 3.0.6. I am encountering an issue where the app fails to run due to an error stating that socket.io does not have a method called listen(). Here is the code snipp ...

After updating the INNERHTML, the NAV tag content is no longer functional

I am facing an issue with replacing the contents of a NAV tag that contains UL list items. The original HTML within the NAV tag works perfectly fine, but when I replace it with my own HTML - which matches the original word for word - the dropdown functiona ...

What strategies can be employed to preserve certain fields while dynamically populating others using JSON in a form?

Currently, I am utilizing jquery Populate to dynamically fill a field with the information from the previous Firstname and Surname fields within the same form. However, an issue arises when using the Populate javascript function: $(formname).populate(newfi ...

Generate interactive tables using data from an XML file

Hello, I'm in the process of generating tables from an XML file using Node.js with the Express framework. I am utilizing npm modules xmldom and xmldoc for this task. The objective is to present these data tables on an ejs page. Here is the structure ...

Guide on retrieving the innerHTML of all <a> tags within a specific span class

<span class="mgen"> <a rel="tag">Action</a> <a rel="tag">Adventure</a> <a rel="tag">Apocalypse</a> <a rel="tag">Fantasy</a> <a rel="tag" ...

Exploring the capabilities of window opener in AngularJS applications

I am facing an issue with a function that utilizes an API to redirect me to a callback URL. After this redirection, I need to execute a function in my core JavaScript. If I keep the function separate from AngularJS and use window.opener, it works fine. How ...

display a visual element within a function using reasoning

I am trying to display an image based on a specific condition in my code, for example: if(x==1) { showImage }. However, I am facing an issue where the web content is not displayed until the image is fully loaded. What I want is for the image to appear smoo ...

simulate angular service/promise in a karma/jasmine test

As I work on writing a karma/jasmine test, I'm seeking clarification on how mocks function in a service that returns a promise. Here's my scenario: Within a controller, I make the following call: mapService.getMapByUuid(mapUUID, isEditor).then( ...

It appears that protractor-flake is programmed to re-run all tests instead of just the ones that have failed

Running tests using the latest version of "[email protected]", encountering failures during testing but the tests are rerunning again. No custom reporter used except Allure reporting. Below is the command used for running: protractor-flake --max-at ...

What is the best approach to retrieve all items from DynamoDB using NodeJS?

I am trying to retrieve all the data from a DynamoDB table using Node.js. Here is my current code: const READ = async (payload) => { const params = { TableName: payload.TableName, }; let scanResults = []; let items; do { items = await ...

react-navigation hook for navigating

Currently, I am utilizing the react-navigation hook and instead of repeating the hook in various components, my goal is to pass navigation as a prop. const navigation = useNavigation(); ... <MyButton resetLocation={resetLocation} navigation= ...

Is it possible to conceal an error message while another error message is being shown?

<form name="form"> <input type="password" placeholder="Password" ng-model="password" name="password" minlength="6" maxlength="15" ng-pattern="/^\S*$/" required> <span ng-show="form.password.$dirty && form.password.$in ...