Various methods for communicating between nested directives

There are numerous methods for communication between directives. For instance, in the case of nested directives where inner directives need to communicate something to the outer directive (e.g., user selection).

<outer>
  <inner></inner>
  <inner></inner>
</outer>

I have identified 5 different ways to achieve this:

require: parent directive

The inner directive can require the outer directive and access a method on its controller. In the inner definition:

require: '^outer',
link: function(scope, iElement, iAttrs, outerController) {
   // This method can be called within the template using ng-click
   $scope.chosen = function() {
     outerController.chosen(something);
   }
}

And in the outer directive's controller:

controller: function($scope) {
   this.chosen = function(something) {
   }
}

$emit event

The inner directive can send an event using $emit, which the outer directive can then respond to using $on. In the inner directive's controller:

controller: function($scope) {
  $scope.chosen = function() {
    $scope.$emit('inner::chosen', something);
  }
}

And in the outer directive's controller:

controller: function($scope) {
  $scope.$on('inner::chosen', function(e, data) {
  }
}

Execute expression in parent scope, via &

The item can bind to an expression in the parent scope and execute it when needed. The HTML structure would look like:

<outer>
  <inner inner-choose="functionOnOuter(item)"></inner>
  <inner inner-choose="functionOnOuter(item)"></inner>
</outer>

Inside the inner controller, there is an 'innerChoose' function that can be triggered:

scope: {
  'innerChoose': '&'
},
controller: function() {
  $scope.click = function() {
    $scope.innerChoose({item:something});
  }
}

This will call the 'functionOnOuter' function on the outer directive's scope:

controller: function($scope) {
  $scope.functionOnOuter = function(item) {
  }
}

Scope inheritance on non-isolated scope

In cases of nested controllers with non-isolated scopes, inner directives can directly call functions in the scope chain. Within the inner directive:

// scope: anything but a hash {}
controller: function() {
  $scope.click = function() {
    $scope.functionOnOuter(something);
  }
}

And in the outer directive:

controller: function($scope) {
  $scope.functionOnOuter = function(item) {
  }
}

By service injected into both inner and outer

A service can be injected into both directives, allowing them to access the same object or notify the service by calling functions. They can also register themselves to be notified in a pub/sub system, even if the directives are not nested.

Question: What are the potential advantages and disadvantages of each of these methods over the others?

Credit/Disclaimer: This question was originally found on programmers.SE and was authored by Michal Charemza. The original question can be accessed here.

Answer №1

Thank you for bringing this to the attention of the original author.

Here are my thoughts on when each approach should be preferred:

Opt for the require parent directive if

  • the inner element is always nested within the outer element
  • the inner element consistently calls the same API from the outer element
  • the inner element is private and interacts with the outer element

Choose to use $emit event if

  • the two directives have nothing in common, particularly in terms of hierarchy
  • communication between the two relies on an event trigger
  • you're feeling a bit lazy to create a service for this purpose

Execute expression in the parent scope using & if

  • the inner element doesn't need to always be inside the outer element
  • the inner element doesn't always call the same API from the outer element, or with the same parameters

Consider utilizing scope inheritance on non-isolated scope if

No, I would not recommend this. It's similar to using require, but without the guarantee that the inner element will always be inside the outer element, making it confusing for developers to use the directive.

Use a service injected into both the inner and outer elements if

  • You find yourself in a situation similar to the one described for using $emit
  • Plus, you're just a good person

That's essentially it. A service is a more effective choice compared to broadcasting events because it explicitly informs the programmer which events impact the directive. Using $emit and similar methods is usually the least favorable option, as it mimics the problematic nature of the infamous goto statement that many developers despise: debugging becomes challenging when there are too many events in play.

If the hierarchy and API usage are assured, I strongly suggest opting for the require approach, as it alleviates a developer's concerns while working with directives.

Answer №2

Just wanted to share a different approach for directives to communicate - using objects:

<outer model='model'>
  <inner model='model[0]'></inner>
  <inner model='model[1]'></inner>
</outer>

Alternatively, in a more common scenario:

<input type="checkbox" ng-model="smth"/>
<div ng-show="smth"></div>

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

Code for Custom Controllers in Strapi Beta version 3.0

I have encountered some discrepancies in the beta version of Strapi's controllers compared to the previous version. The new version includes multipart/sanitization boilerplate, and I am having trouble integrating my order object and Stripe charge. Be ...

Get the JSON array by decoding the JSON data in PHP

I have a JavaScript function that creates a JSON array and uses an AJAX post method. How should this JSON array be decoded on the PHP side? Here is my JavaScript function: var invoices = {invoice: [{ "customerName" : "John" ,"reciptionName" : "Doe" ,"dat ...

The jQuery .each function is malfunctioning specifically on Google Chrome browsers

I developed a web application that utilizes jQuery to automatically submit all forms on the page. The code snippet is as follows: $('form').each(function() { $(this).submit(); }); While this functionality works perfectly in Internet Explore ...

Determining the Right Version of a Framework in npm: A Guide

One common issue I encounter is the uncertainty of which versions of npm, Ionic, and other tools I should have installed. It often goes like this: "Oh, there's a new version of the Ionic CLI out. Let's update." I install CLI v3.9.0. ...

What is the process for creating a selector that links to an element that has been dynamically generated?

Can anyone help me figure out how to create a selector that links to a dynamically created element without using an event on the dynamic element? By dynamically created element, I mean an element that is not present in the HTML at the beginning. I know t ...

Eliminate any leading or trailing spaces around a string contained within a tag within a contenteditable div

I am working on a project to eliminate extra spaces before and after text within a bold tag. Functionalities: I developed a text editor where users can type text, select it, and apply bold formatting. Additionally, there is an HTML button that displays th ...

Using Javascript to make an AJAX request and appending "?=####" to the end of the call

When I make an ajax call to a URL on my server, the response from my logs shows as "/action?_=1423024004825". Is there a way to remove this extra information? $.ajax({ type: "GET", url: "/action" }); ...

Tips for implementing and utilizing onclick functions in EJS

My goal is to develop a trivia game with interactive features. I aim to enable users to click on an answer, which will trigger a border effect and increase the points variable. Below is the layout of the entire page: <% include ../partials/boilerp ...

Is there a way to activate a click event on a particular child element within a parent element?

When attempting to click on a specific descendant of an element, I located the ancestor using ng-class since it lacked an id. yes = document.querySelectorAll('[ng-controller = "inventoryController"]') Having found the desired ancestor, ...

Create a semicircle progress bar with rounded edges and a shadow using JavaScript and CSS

Despite my extensive search efforts, I have been unable to find a solution. My goal is to create a progress bar with rounded corners that includes a shadow. So far, this is what I have managed: $(".progress-bar").each(function(){ var bar = $(this). ...

When running grunt-bower, I am encountering an error stating that _.object is not a function

I am attempting to execute the grunt-bower task in order to copy all of my bower components. Encountered an error while running "bower:dev" (bower) task TypeError: _.object is not a function at Object.exports.getDests (/Users/wonoh/cocApp/node_modules/g ...

Display or conceal section based on selected checkboxes

I'm currently working on a JavaScript script that involves showing/hiding elements based on radio button and checkbox selections. The goal is to reveal a hidden section when certain checkboxes are checked, as illustrated in the screenshot below: http ...

Error message in Mysql connector for NodeJS: Pool has been closed

We have developed a NodeJS application that functions as an API for a frontend Angular project we are currently working on. So far, both components have been operating smoothly. However, occasionally we encounter a strange error with the API stating "Pool ...

Is it possible to further simplify this piece of JavaScript using jQuery?

In my journey to learn and grow with Javascript, I have come across a snippet of code that simply attaches an onclick event to a button and updates the text of an em tag below it. As I delve deeper into jQuery, I am beginning to appreciate its syntax and u ...

How can I effectively use gulp-sourcemaps in an Angular application using the IIFE pattern? Can someone confirm if my understanding of sourcem

Today marks my first attempt at working with exporting source maps through my gulp builds. I am currently using angular 1.4.x and we have implemented the following pattern: (function() { 'use strict'; var thing = (function() { func ...

Navigating the integration of internal Bootsrap 5.2 with Laravel 7

Hi there, I am new to the Laravel framework and I am attempting to link my Bootstrap v5.2 with my Laravel v7 project. However, I seem to be having trouble connecting the two. I have stored the CSS and JS folders within a "bootstrap" folder at the same leve ...

Detecting empty or default form fields in Angular using Reactive Forms to disable buttons

Is it possible to disable the submit button when the initial form is empty, and enable it if any value is not empty? <form [formGroup]="registerForm" (ngSubmit)="onSubmit()"> <div class="form-row"> ...

Removing an item from an array in Mongoose

I'm encountering an issue with removing an Object from an Array and I'm unsure of the correct way to use $pull for this action. Any help would be greatly appreciated! JSON data "accountId": "62efd8c008f424af397b415d", "l ...

Exporting a SkinnedMesh with position using Three.js

I am attempting to export a model from a Three.js scene using the OBJExporter.js found at https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/OBJExporter.js Within the scene, I have both base meshes and SkinnedMeshes. To account for this ...

Create a personalized compilation process that eliminates the two-way binding

In my Angular 1.5.8 application, I have created an attribute directive called my-directive. I am trying to apply this directive to an element while passing two additional parameters - one with one-way binding (@) and the other with two-way binding (=). Ev ...