3

By using & binding in child component we can run function that is part of parent components controller. I am strugling to do the same in oposite direction - when something happens in parent component, child component runs function.

I have mapComponent which is direct child of mainComponent. mapComponent displays fullscreen Google Map(using NgMap) with some markers. mainComponent handles login and registration. If users start registration I need to run function in mapComponent that removes all markers and attaches map event so that user could click on map to show were he lives and those coordinates could be saved on registration.

Just for example that wouldnt be to complex, here is code from plunker I found while searching stackoverflow:

var app = angular.module('plunker', []);

app.controller('RootController', function() {});

app.component('parentComponent', {
  template: `
    <h3>Parent component</h3>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Child</a>
    <span data-ng-bind="$ctrl.childMessage"></span>
    <child-component on-change="$ctrl.notifiedFromChild(count)" message="$ctrl.message"></child-component>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.notifiedFromChild = function(count) {
      ctrl.childMessage = "From child " + count;
    }
    ctrl.click = function() {
      ctrl.message = Math.random()
    }
  },
  bindings: {}
});

app.component('childComponent', {
  template: `
    <h4>Child component</h4>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Parent</a>
    <span>{{$ctrl.parentMessage}}</span>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.counter = 0;
    ctrl.click = function() {
      ctrl.onChange({
        count: ++ctrl.counter
      });
    }
  },
  bindings: {
    onChange: '&',
    parentMessage: '<message'
  }
});

In this example by clicking button in parent component I can change parentMessage value in child component through data binding. However I would want to find a way for child component change this value byself when parent component asks to do it.

3 Answers 3

4

One possible solution is to create a register function in the parent like this:

ctrl.register = function(child){
    ctrl.childCtrl = child;
}

And then use regular callback binding to pass this register function to the child, and have the child call it. Like this:

app.component('childComponent', {
    bindings: {
        registerParent: '&'
    }
    controller: function() {
        var ctrl = this;

        // Register the child controller with the parent.
        ctrl.registerParent({childRef: ctrl});

        ctrl.childFunction = function(message) {
            console.log('Child function: ' + message);
        }
    },
    // ...and so on
});

Then pass it through the template

<child-component register-parent="$ctrl.register(childRef)" />

After this you can use the childRef variable to call any function of the child controller from the parent. So for instance in your parent you could do this:

ctrl.childRef.childFunction("called from parent")

and that should log the following into the console: Child function: called from parent

I've created a Plunker to show this working.

Sign up to request clarification or add additional context in comments.

4 Comments

I am not sure that I completely understand your answer. Wouldnt that mean that I would call that child function on init? Could you give example or explaine how to use childRef?
You basically pass a reference to the ChildComponent's controller object instance, to the parent controller. I've updated my answer with a link to a working example.
Hard to understand at beggining... but this is genius, exactly what I was looking for, thank you! Quick question thou - could you compare this technique with more common broadcast/on performance-wise?
It's hard to compare really. I don't think this method should have any real performance implication. But neither would the other method I would assume. For me it comes down to what I'm comfortable with. I dislike using the broadcast-system in Angular, so I try to stay from it. Some people dislikes exposing the child to the parent like this (and I can also relate to this) so for me it's down to the lesser of two "evils". I mostly try to avoid this situation altogether. When I use it, I usually change it up a bit so I only register the child function, not the entire child controller.
1

You can use BroadCast and on mechanism as Below

$scope.startScanner = function() {

    $rootScope.$broadcast('scanner-started');
}

And then to receive, use the $scope of your controller:

$scope.$on('scanner-started', function(event, args) {

    // do what you want to do
});

If you want you can pass arguments when you $broadcast:

$rootScope.$broadcast('scanner-started', { any: {} });

And then receive them:

$scope.$on('scanner-started', function(event, args) {

    var anyThing = args.any;
    // do what you want to do
});

Comments

0

You could try a two way binding.

Your child component will have a new binding:

bindings: {
    onChange: '&',
    parentMessage: '<message',
    callChild: '='
  }

and in your child component you fill that binding up with a function

ctrl.callChild = function() {
    ctrl.parentCalled = true;
}

and in your parent you call that function. I've updated your plunker to do what I just explained.

plunker

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.