mtmoran mtmoran - 1 month ago 21
AngularJS Question

How to communicate between sibling directives/components in angular 1.5

What is the best way to communicate between angular 1.5 sibling components and/or directives.
I am using angular material.
I am using ui-router.

I am trying to keep my components and directives separate and not dependent on each other.

I would like to refactor, where appropriate, my directives into .component() modules.

I have a navbar that I have separated into a directive (navBar). In that navbar, I have a search bar that I would like to filter a list. The list is in a sibling directive.

Originally I had the navbar directive(and tried to use it as .compontent()) outside of the scope of MainCtrl as defined by ui-router. This seemed to make sense to me as the navbar would be relatively consistent throughout the application.

I deferred to putting it inside the scope of MainCtrl where I can then bind properties from MainCtrl to elements in my navBar directive. This seems wrong as now the navBar and fileBrowser are coupled with the MainCtrl.

Other options I was looking into:

Using and scope.$watch() to define properties on the parent component from the child component navBar. Then in the other child component, fileBrowser, using scope.$watch() to watch for these changes in the parent component and respond accordingly.

Using a service to store data and pass data.

Using $emit, $broadcast events.

What is the best solution in this situation to keep my directive/components separate? What is the best/cleanest/recommended way to communicate between sibling directive/components?

This state is initiated by ui-router

main.component.js

angular.module('glossa')
.component('mainComponent', {
controller: MainCtrl,
controllerAs: 'vm',
transclude: true,
templateUrl: 'app/main/main.html'
});

function MainCtrl($scope, nodeSrvc, fileSrvc) {
var vm = this;

vm.selectedFile = {};
vm.fileList = [];
vm.searchText = '';
vm.filteredFiles = [];

activate();

function activate() {
buildFileList();
}

/**
* Queries for all files in db.
*/
function buildFileList() {
fileSrvc.queryAllFiles().then(function(docs) {
vm.fileList = docs;
});
}
}


main.html

//where the input where I filter the list
<navbar-directive></navbar-directive>

<div flex layout="row" >
//where the list is located
<file-browser layout="column"></file-browser>
<tabbar></tabbar>
</div>

<drawer-directive></drawer-directive>


I would like navbar to filter a list located in the sibling directive or component filebrowser

navbar.directive.js

angular.module('glossa')
.directive('navbarDirective', navBarDirective);

function navBarDirective(fileSrvc) {
var directive = {
restrict: 'E',
replace: true,
controller: NavbarCtrl,
controllerAs: 'navVm',
templateUrl: 'components/navbar/navbar.html',
bindToController: true
};
return directive;
}


navbar.html

<md-toolbar
layout="row"
class="nav-content primary-bg"
md-whiteframe="1"
md-selected-nav-item="currentNavItem"
nav-bar-aria-label="navigation links">
<span flex></span>
<div class="md-toolbar-tools">
<md-input-container md-no-float flex >
<form ng-submit="vm.searchSubmit()">
<input ng-model="vm.searchText" placeholder="Search...">
</form>
</md-input-container>
</div>
</md-toolbar>


This is where the list I'd like to filter is located.

filebrowser.js

angular.module('glossa')
.directive('fileBrowser', fileBrowser);

function fileBrowser() {
var directive = {
restrict: 'E',
templateUrl: 'components/filebrowser/filebrowser.html'
};
return directive;
}


filebrowser.html

<md-sidenav
md-component-id="left"
md-is-locked-open="true"
layout="column">
<md-content>
<md-list flex>
<md-list-item ng-repeat="file in vm.filteredFiles = (vm.fileList | filter: vm.searchText)" class="md-2-line">
<md-item-content md-ink-ripple layout="row" layout-align="start center">
<div class="md-list-item-text" layout="column">
<h3>{{file.name}}</h3>
<p>Preview of first few lines of a baseline</p>
</div>
</md-item-content>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>

Answer

To communicate berween sibling components use bidirectional binding:

angular.module('glossa')
    .directive('navbarDirective', navBarDirective);

function navBarDirective(fileSrvc) {
    var directive = {
        //Use bi-directional binding
        scope: { 
            searchText: '='
        },
        restrict: 'E',
        replace: true,
        controller: NavbarCtrl,
        controllerAs: 'navVm',
        templateUrl: 'components/navbar/navbar.html',
        bindToController: true
    };
    return directive;
}

HTML

<nav-bar-directive search-text="main.searchText">
</nav-bar-directive>

<sibling-component search-text="main.searchText">
</sibling-component>