30

Is there a built in way to stop $broadcast events from going down the scope chain?

The event object passed by a $broadcast event does not have a stopPropagation method (as the docs on $rootScope mention.) However this merged pull request suggest that $broadcast events can have stopPropagation called on them.

Noah Freitas
  • 17,240
  • 10
  • 50
  • 67

3 Answers3

43

Snippets from angularJS 1.1.2 source code:

$emit: function(name, args) {
    // ....
    event = {
        name: name,
        targetScope: scope,
        stopPropagation: function() {
            stopPropagation = true;
        },
        preventDefault: function() {
            event.defaultPrevented = true;
        },
        defaultPrevented: false
    },
    // ....
}

$broadcast: function(name, args) {
    // ...
    event = {
        name: name,
        targetScope: target,
        preventDefault: function() {
            event.defaultPrevented = true;
        },
        defaultPrevented: false
    },
    // ...
}

As you can see event object in $broadcast not have "stopPropagation".

Instead of stopPropagation you can use preventDefault in order to mark event as "not need to handle this event". This not stop event propagation but this will tell the children scopes: "not need to handle this event"

Example: http://jsfiddle.net/C8EqT/1/

kostik
  • 1,179
  • 10
  • 11
  • Using `defaultPrevented` as a flag to signal ignored events makes sense. – Noah Freitas Mar 15 '13 at 14:28
  • Real Crux of this example are these lines: ' "not need to handle this event" :- This not stop event propagation but this will tell the children scopes: "not need to handle this event" ' As the Default Behaviour of preventDefault "The preventDefault() method does not prevent further propagation of an event through the DOM. Use the stopPropagation() method to handle this" stopPropagation: The event will complete dispatch to all listeners on the current EventTarget before event flow stops. – Abhijeet Apr 14 '15 at 08:00
7

Since broadcast does not have the stopPropagation method,you need to use the defaultPrevented property and this will make sense in recursive directives.

Have a look at this plunker here:Plunkr

$scope.$on('test', function(event) { if (!event.defaultPrevented) { event.defaultPrevented = true; console.log('Handle event here for the root node only.'); } });

Mahendra Singh
  • 298
  • 2
  • 9
1

I implemented an event thief for this purpose:

.factory("stealEvent", [function () {

  /**
   * If event is already "default prevented", noop.
   * If event isn't "default prevented", executes callback.
   * If callback returns a truthy value or undefined,
   * stops event propagation if possible, and flags event as "default prevented".
   */
  return function (callback) {
    return function (event) {
      if (!event.defaultPrevented) {
        var stopEvent = callback.apply(null, arguments);
        if (typeof stopEvent === "undefined" || stopEvent) {
          event.stopPropagation && event.stopPropagation();
          event.preventDefault();
        }
      }
    };
  };

}]);

To use:

$scope.$on("AnyEvent", stealEvent(function (event, anyOtherParameter) {
  if ($scope.keepEvent) {
    // do some stuff with anyOtherParameter
    return true; // steal event
  } else {
    return false; // let event available for other listeners
  }
}));

$scope.$on("AnyOtherEvent", stealEvent(function (event, anyOtherParameter) {
  // do some stuff with anyOtherParameter, event stolen by default
}));
sp00m
  • 47,968
  • 31
  • 142
  • 252