3

According to the below image:

enter image description here

I want to improve components communication method....I think this way is not efficient.

When clicking tabsetComponent to emit event, then parent controller catch this event, changing rootScope variable. Using $watch rootScope variable in tableComponent to trigger http fetch data function...

Could anyone has better and efficient way to communicate sibling component?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Seven Lee
  • 597
  • 4
  • 9
  • 21

1 Answers1

5

The accepted AngularJS method for communication between components is using component attributes for communication.

<div ng-controller="rootCtrl as vm">

    <tab-set-component tsc-click="vm.fn($event, data)">
    </tab-set-component>

    <table-component="vm.tableData">
    </table-component>

</div>

For more information on defining component attributes, see AngularJS Comprehensive Directive API -- isolate scope

Best practices

Only use .$broadcast(), .$emit() and .$on() for atomic events

Events that are relevant globally across the entire app (such as a user authenticating or the app closing). If you want events specific to modules, services or widgets you should consider Services, Directive Controllers, or 3rd Party Libs

  • $scope.$watch() should replace the need for events
  • Injecting services and calling methods directly is also useful for direct communication
  • Directives are able to directly communicate with each other through directive-controllers

-- AngularJS Wiki Best Practices


Controller Example

In your html, you use vm.fn that came from root controller right? So your advice is it should call the click method defined root controller, the click method will trigger http request function defined on the rootScope, then get table component datas, then bind the datas on table component attribute.

As example:

angular.module("myApp", []);

angular.module("myApp").controller("rootCtrl", function($http) {
    var vm = this;

    vm.tableData = { /* initial data */ };

    //click handler
    vm.fn = function(event, url) {
        $http.get(url).then (function onFulfilled(response) {
            vm.tableData = response.data;
        }).catch (function onRejected(response) {
            console.log(response.status);
        });
    };
});

The above example avoids cluttering $rootScope. All the business logic and data is contained in the controller.

The controller sets the initial data for the table-component, receives click events from the tab-set-component, makes HTTP requests, handles errors, and updates the data to the table-component.


UPDATE -- Using Expression Binding

Another approach is using expression binding to communicate events:

<header-component view="root.view" on-view-change="root.view = $event.view">
</header-component>

<main-component view="root.view"></main-component>

For more information, see SO: How to pass data between sibling components in angular, not using $scope

With version 1.5.3, AngularJS added the $onChanges life-cycle hook to the $compile service.

app.component("mainComponent",  {
      template: "<p>{{$ctrl.count}}",
      bindings: {view: '<'},
      controller: function() {
        this.count = 0;
        this.$onChanges = function(changesObj) {
            if (changesObj.view) {
                this.count++;
                console.log(changesObj.view.currentValue);
                console.log(changesObj.view.previousValue);
                console.log(changes)bj.view.isFirstChanged());
            };
        };    
      }
});

For more information, see AngularJS Comprehensive Directive API Reference -- Life-cycle hooks

See also SO: AngularJs 1.5 - Component does not support Watchers, what is the work around?

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • in your html, you use vm.fn that came from root controller right? So your advice is it should call the click method defined on root controller, the click method will trigger http request function defined on the rootScope, then get table component datas, then bind the datas on table component attribute. Do you think http fetch function is defined in the root controller is more efficient? And with these way, we didn't need to used any event method, is that correct? – Seven Lee Feb 20 '16 at 19:37
  • See added controller example – georgeawg Feb 20 '16 at 20:11
  • Thank of your detail explain:) Another question, if there are many components in root scope, we will defined many function in root controller, is correct way? The root controller will be too heavy to execute efficiently? – Seven Lee Feb 21 '16 at 03:40
  • The controller example above doesn't use `$rootScope`. It uses a child scope of `$rootScope`. You can check that by putting `{{'scope-id'=+$id}}` in its template. `$rootScope` has an ID of 1. Child scopes have numbers incremented from one. – georgeawg Feb 21 '16 at 18:34
  • I am sorry I key wrong word...Actually I want to say if I defined many function that components in root controller needed in root controller, the root controller will be too heavy? – Seven Lee Feb 21 '16 at 18:41
  • Common functions are generally re-factored out of controllers into provider services. But that's another question. Your question is about communication between components. – georgeawg Feb 21 '16 at 18:49
  • I agree with you. thanks a lot, I will refactor my code now:) – Seven Lee Feb 21 '16 at 18:55
  • Please check out http://stackoverflow.com/questions/36033940/how-to-pass-data-between-child-components-in-angular-1-5-not-using-scope/40241970#40241970 for an alternative yet simple approach. Thanks! – Andrea Puddu Oct 25 '16 at 14:00