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

Is Angular File API Support Compatible with HTML5?

When checking for HTML5 File API browser support in my code: private hasHtml5FileApiSupport; constructor(@Optional() @Inject(DOCUMENT) document: Document) { const w = document.defaultView; this.hasHtml5FileApiSupport = w.File && w.FileReader & ...

What steps do I need to take to modify the text in the ionic navbar

Here is a snippet of my code... <ion-side-menu-content> <ion-nav-bar class="bar-stable"> <ion-nav-back-button> </ion-nav-back-button> <ion-nav-buttons side="left"> <button class="button b ...

Turn off the scrolling bars and only allow scrolling using the mouse wheel or touch scrolling

Is there a way to only enable scrolling through a webpage using the mouse wheel or touch scrolling on mobile devices, while disabling browser scroll bars? This would allow users to navigate up and down through div elements. Here is the concept: HTML: &l ...

Grails 3.1.9 does not support displaying JavaScript

Having trouble getting the datepicker to display using JavaScript <script> $( "#invoiceDate" ).datepicker({ inline: true, dateFormat: "yy-mm-dd", onSelect: function(datetext){ datetext = datetext+" 00:00:00.0" ...

Disabling prefetch in next.config.js: A comprehensive guide on eliminating data prefetching in Next.js

Typically, in many cases, the disabling of prefetching is achieved by specifically setting a link to not preload. An example of this can be seen below: <Link href="/about" prefetch={false}> <a>About us</a> </Link> My go ...

Error: The function cannot be performed on _nextProps.children

I'm having trouble implementing react context with nextJS and I keep encountering this error: Server Error TypeError: _nextProps.children is not a function This is my code for _App.js: import Head from "next/head"; import Router from &q ...

Utilizing the Twitter API 1.1 to retrieve a list of tweets

As I work on updating my CMS component, I am incorporating integration with the Twitter API to fetch and showcase a list of tweets related to a user or search query. I have chosen to utilize the Twitter Restful API v1.1 as the 1.0 version is set to be disc ...

The error message "ReferenceError: express is not defined" indicates that Node.js is

Recently, I started using Nodejs to develop web servers, utilizing the express module. To install it, I used the command: "sudo npm install -g express". However, upon running the program, an error occurred: "ReferenceError: express is not defined ...

Adjust the dimensions of the dropdown menu

Objective: How can I adjust the width of a select dropdownlist that is utilizing bootstrap v2? Challenge: I am uncertain about how to modify the width in the context of bootstrap. Additional Information: Keep in mind that there are three dropdownli ...

Updating the model does not reflect changes made to AGM polygons' binding

<div *ngFor="let p of polys"> <agm-polygon #cmp [paths]="$any(p.getPath()).i" [fillColor]="'blue'" [draggable]="true" [editable]="true" [polyDraggable]="true" (p ...

"Shopping just got easier with our new drag and drop feature! Simply drag items

I want to develop a Virtual Shopping Cart system. Items will be retrieved from a database. A virtual basket will be available. Users can drag items and drop them into the basket, similar to shopping in a mall. Once the user clicks the save button, the s ...

Failure to call the controller function when submitting the form

After clicking the submit button, I noticed that the auth function is not being triggered. Unfortunately, I haven't been able to identify the root cause of this issue. Here is the HTML template for all pages: <!DOCTYPE html> <html ng-app="m ...

Present the value of an object within an array in an HTML format

I have organized an array containing information about different video games: let games = [{ title: 'Fortnite', price: 20, img: "./assets/images/Fortnite.jpg" }, { title: 'Valorant', price: 0, img: "./asse ...

Obtaining the client's IP address using socket.io 2.0.3: a comprehensive guide

Currently, I am facing a challenge using socket.io v2.0.3 in my node.js server as I am unable to retrieve the client's IP address. Although there are several suggestions and techniques on platforms like stackoverflow, most of them are outdated and no ...

Error encountered in jQuery validation: Uncaught TypeError - $(...) ""valid is not a function" is displayed with proper references and order

When I try to call a function, I keep getting an error message that says "Uncaught TypeError: $(...).valid is not a function"... JS $('input[data-val=true]').on('blur', function() { $(this).valid(); }); HTML <div class="col-x ...

Transferring Variables from WordPress PHP to JavaScript

I have implemented two WordPress plugins - Snippets for PHP code insertion and Scripts n Styles for JavaScript. My objective is to automatically populate a form with the email address of a logged-in user. Here is the PHP snippet used in Snippets: <?p ...

Tips for modifying and refreshing data in a live table with JQuery

One challenge I'm facing is figuring out how to transfer the data from a dynamic table back into input fields when clicking on the edit button of a specific row. Additionally, I need to update that particular row based on any changes made to the value ...

Adjusting the X-axis in Highstock: Tips and Tricks

Is there a way to adjust the X axis on this chart? My goal is to shift it so that it only covers half of the width, leaving the other half blank for future plotlines. Any suggestions on how to achieve this? Thanks! :) ...

Can anyone please guide me on how to extract the IP address of a specific individual using Node.js?

There's an individual running some kind of exploit scanner on my server. I'm receiving strange requests like: IP ADDRESS: ::ffff:127.0.0.1 www-0 (out): POST /cgi-bin/php5?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64 ...

class-validator: ensures the correct number of digits are present in numeric values

Seeking assistance on validating the number of digits for numeric values using class-validator. Specifically, I want my entity to only accept numbers with 6 digits for a certain property. For example: const user1 = new User(); user1.code = 123456 // should ...