In the previous post, we created a simple service called deregister to make event listeners deregistration easier. But sometimes you don’t even need to listen to an event more than once, and then the deregistration task is boring. No more!

Got the data? Move on, soldier.

Sometimes, you just want to listen to an event, but just one time. Your code usually ends up like this:

1

2

3

4

5

6

7

8

9

10

function ( $rootScope ) {

var dereg = $rootScope.$on( 'myEvent' , doStuffOnce);



function doStuffOnce ( event, data ) {



dereg();



doStuff(data);

}

}



This is boring. jQuery’s got .one for this kind of stuff (even jqLite does!). Why didn’t they implement Scope.$one , man I will never now. But thanks to AngularJS 1.4 decorators, we can fix this!

Enter Scope.$one

Turns out it’s quite easy to implement Scope.$one by decorating the $rootScope service. The Scope class itself isn’t exposed so we can’t alter its prototype, but we can add a hook to the $rootScope.$new method (which is used to create every single scope in the application) to inject our $one method afterwards.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

angular

.module( 'app' )

.decorator( '$rootScope' , $rootScopeDecorator);





function $rootScopeDecorator ( $delegate ) {







$delegate._$ new = $delegate.$ new ;

$delegate.$ new = createNewScope;









$delegate.$one = one;



return $delegate;

}



function createNewScope ( isolate, parent ) {



var newScope = this ._$ new (isolate, parent);





newScope.$one = one;



return newScope;

}



function one ( eventName, callback ) {





var dereg = this .$on(eventName, function ( ) {



dereg();





callback.apply( null , arguments );

});







return dereg;

}



Conclusion

Now we can update our original use case:

1

2

3

4

5

6

7

8

9

function ( $rootScope ) {



$rootScope.$one( 'myEvent' , doStuffOnce);



function doStuffOnce ( event, data ) {



doStuff(data);

}

}



Sidenotes

Note that this tip doesn’t exempt you from deregistering manually if the event is never fired. Using our deregister service, the example looks like this: