1

In the code below you can see I have a counter component that fires events on increment and decrement and those are bound to an output that is displayed. These events are on $rootScope so they can be heard on another component which works too. What doesn't work is the data binding on my watchercomponent. If you look at the console, it's receiving the event and its data properly and is even updating the message property but it isn't being update in the view. What gives? Below is my code:

Script.js

angular.module('testApp', []).component('counter', {
bindings: {
    input: '<',
    output: '&'
},
controller: function ($rootScope, $attrs) {
    this.output = $attrs.input;

    this.increment = function increment() {
        this.output++;
        console.log("event from counter fired");
        $rootScope.$emit('eventfromcounter', 'increment');

    }

    this.decrement = function decrement() {
        this.output--;
        console.log("event from counter fired");
        $rootScope.$emit('eventfromcounter', 'decrement');

    }

},
template: function ($element, $attrs) {
    return [
    '<input type="text" ng-model="$ctrl.output">',
    '<button type="button" ng-click="$ctrl.decrement();">-</button>',
    '<button type="button" ng-click="$ctrl.increment();">+</button>',
    ].join('');
}
}).component('watchercomponent', {
    bindings: {
        message : '&'
    },
    controller: function ($rootScope, $attrs)
    {
        this.message = 'waiting for event';
        $rootScope.$on('eventfromcounter', function (event, args)
        {
            console.log("recieved at anothercomponent : " + event.name + " : " + args);
            this.message = args;
            console.log("message : " + this.message);
        });
    },
    template: function($element, $attrs)
    {
        return [
            '<br/><span>watchercomponent : {{$ctrl.message}}</span>'
        ].join('');
    }
});

index.html

<html>

  <head>
    <script src="angular.min.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <div ng-app = "testApp">
      <counter input="3"></counter>
      <watchercomponent></watchercomponent>
    </div>
  </body>

</html>

Plunker here.

Also are $rootScope events good practice to share events/data between components? If not could anyone point me into the right direction?

Thanks

2
  • 2
    Use $broadcast instead of $emit plnkr.co/edit/f1jL6HtDyXrt0O0KpD9W?p=preview. Use a service rather than $rootScope to share data between directives. Commented Mar 10, 2016 at 11:39
  • Isn't broadcast just a worse (design wise) emit? Also that doesn't solve the problem of data binding not working on the watchercomponent. Commented Mar 10, 2016 at 22:09

1 Answer 1

3

Your problem relies on the use of this. In JavaScript, context inside each function is different, thus the value of this references something different. This is why you want to keep a reference to your controller this value by using var ctrl = this;.

JS

    angular.module('testApp', []).component('counter', {
    bindings: {
        input: '<',
        output: '&'
    },
    controller: function ($rootScope, $attrs) {
        this.output = $attrs.input;

        this.increment = function increment() {
            this.output++;
            console.log("event from counter fired");
            $rootScope.$broadcast('eventfromcounter', 'increment');

        }

        this.decrement = function decrement() {
            this.output--;
            console.log("event from counter fired");
            $rootScope.$broadcast('eventfromcounter', 'decrement');

        }

    },
    template: function ($element, $attrs) {
        return [
        '<input type="text" ng-model="$ctrl.output">',
        '<button type="button" ng-click="$ctrl.decrement();">-</button>',
        '<button type="button" ng-click="$ctrl.increment();">+</button>',
        ].join('');
    }
}).component('watchercomponent', {
    controller: function ($rootScope, $attrs)
    {
        var ctrl = this; //Keep a reference to controller's context

        ctrl.message = 'waiting for event';
        $rootScope.$on('eventfromcounter', function (event, args)
        {
            console.log("recieved at anothercomponent : " + event.name + " : " + args);
            ctrl.message = args; //Use controller's context, not function context
            console.log("message : " + ctrl.message);
        });
    },
    template: function($element, $attrs)
    {
        return [
            '<br/><span>watchercomponent : {{$ctrl.message}}</span>'
        ].join('');
    }
});

Working plunkr: https://plnkr.co/edit/v4VgggsOx1xWsdRnI8hU?p=preview

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

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.