young-steveo / bottlejs

A powerful dependency injection micro container for JavaScript applications
MIT License
1.29k stars 67 forks source link

Access services in a decorator #67

Open robations opened 8 years ago

robations commented 8 years ago

I've not thought this through fully, but is there a good reason not to allow access to dependencies in a decorator? I've had a few situations where I want to alter/augment some behaviour in a given service, but from the decorator I'm very limited in what I have access to.

Something like

bottle.decorator(
    "glass",
    function (original, beer) {
        return function () {
            original.fill(beer.pour());
            return original;
        }
    },
    "Beer"
);
young-steveo commented 8 years ago

If I follow you correctly, you want to be able to inject additional services into a decorator; basically the decorator would have it's own dependencies. Is that correct?

robations commented 8 years ago

That is correct!

young-steveo commented 8 years ago

This makes sense to me; sounds like a good addition. Probably should add the feature to middleware as well. Possibly also add injection to factories, but not sure.

robations commented 8 years ago

In case it helps, the use case is an npm module where I export a bottle container. Other modules can then consume these services but in some cases may either override or augment existing functionality.

It's a flexible architecture but, at the moment, decorating an existing service is constrained to services you can access using require() (or otherwise outside the realms of bottle).

mwillbanks commented 7 years ago

@young-steveo yes, injection into factories, decorators and middleware is very nice...

For decorators think of something more or less that might want to hook an event on something else...

bottle.service('MyClassEmitsEvent', MyClassEmitsEvent);
bottle.service('MyEventWatcher', MyEventWatcher);

// assuming injectables would be an object of the injected parameters... otherwise container would be file also
bottle.decorator('MyClassEmitsEvent', 'MyEventWatcher', (service, injectables) => {
  service.on('event', injectables.MyEventWatcher.notify);
});
young-steveo commented 7 years ago

@mwillbanks Yeah, I'm going to get started on the new Bottle version soon; I'll include this feature. I hadn't considered putting the injectables before the final function, but I like the idea, thanks!

ghost commented 4 years ago

Just as an FYI to any poor soul needing this solution, I've implemented the following which works well. It isn't pretty but beauty is skin deep.

function getDecorator(decoratorDependency,) {
  function decorate(someService) {
    // have much fun with decoratorDependency
    return someService;
  }
  return decorate;
}

module.exports = (container) => {
  container.decorator('someService', getDecorator(container.container.decoratorDependency);
};