I'd like to pop a toastr message to display $log messages. I.e.:
$log.info('test'));
It seems like in AngularJS 1.2.19 and greater, $provide.decorator is a great way to extend the functionality of $log. Of course, I am using 1.2.18. Is there a way to do this in 1.2.18?
Ideally I'd like to extend the existing functionality, as opposed to overriding it completely.
I don't want to modify the Angular source.
The decorator mechanism is a shorthand for creating a provider that obtains the previous definition and returns a new version of it.
Therefore you can mimic this functionality as follows:
module.provider('$log', function ($logProvider) {
// $logProvider here is the one previously defined, from 'ng'
// unless someone else is overriding too.
this.$get = function ($injector) {
// Invoke the original provider.
var $log = $injector.invoke($logProvider.$get);
var oldInfo = $log.info;
// Override the method.
$log.info = function info() {
oldInfo.apply(this, arguments);
// (not actually useful to alert arguments; just an example)
alert(arguments);
}
return $log;
}
});
Of course, this modifies the object for all dependents, even if they were quite happy with the standard $log
interface, and means that you have no direct way to see in your application which services depend on the standard interface and which depend on your augmented interface. This could be useful if you want to just change the behavior of the existing methods through wrapping (e.g. to make logs get sent to your server, or to display a toaster as you mentioned in your question) but may be a risky choice for adding additional methods that existing callers aren't expecting, where you may accidentally break compatibility with the standard interface.
Instead, it may be better to provide a new service which you can use in places where you know you need your extension, and leave the built-in $log
interface as standard. This way it will be easier to tell the difference and will avoid unintended behavior changes in standard callers. This looks similar to the above, but is a little different in the details:
module.provider('anotherLog', function ($logProvider) {
this.$get = function ($injector) {
var $log = $injector.invoke($logProvider.$get);
// Create a new object rather than extending the existing one in-place.
// This way the new method is only visible to callers that request
// 'anotherLog' instead of just '$log'.
var anotherLog = angular.extend(Object.create($log), {
sayHello: function sayHello() {
console.log('Hello!');
}
})
return anotherLog;
}
});
Both of these approaches exploit the fact that during app initialization a separate "provider injector" is used to handle dependencies between providers. This is distinct from the main $injector
that is used once the app is created. The provider injector contains all of the providers that have been defined so far, but won't contain the resulting services such as $log
. The provider function itself is injected with the provider injector, while its $get
method is (as shown in these examples) injected with the main $injector
.
Because of this distinction, if you depend on any other services to display your toaster it is necessary to depend on them for the $get
method itself and not the provider as a whole. This will allow you access to services in the main $injector
, rather than just providers from the provider injector.