alexeieleusis/greencat

How to inject a service into an AsyncAction

sturmf opened this issue · 6 comments

Hi, thanks for the greencat library!

So far it looks really nice and I now want to add async actions to my application. With that I mean I want to create an action that makes an ajax call which returns with some new event which would trigger another action.

For this I tried to use the ThunkMiddleware. But for now I am stuck with where to get my service from. Usually I let inject the service into the Angular2 components but now the actions need to have access to it.

How would you pass it in? I don't really like to have the component that creates the action to pass the service as a parameter to the action. My component should not need to know which service to use.

Tanks a lot for any ideas!

In the place you bootstrap the app I would register the async action, similar to how is done in https://github.com/alexeieleusis/greencat/blob/master/examples/todo_list_angular/lib/app_component.dart

But I am not sure it will fit you needs, probably a minimal example to reproduce your use case would better help me understand.

Besides that cc @yjbanov who has more experience with this architecture type.

Hmm I am not sure what you mean by registering the async action?

In case of your todolist example. I would write one service which handles all the async rest api calls for adding, removing and resolving todo entries. I can inject this service easily into any component. But I can't inject it into the action itself where I would need a reference to the service in the "call" method. Or am I missing something obvious here?

Ok I now could come up with a working solution. In my AppCommponent I now have a static field for the global Injector. I fill it like this:

  bootstrap(AppComponent, <Provider>[provide(Client, useFactory: () => new BrowserClient(), deps: <Object>[])]).then((ComponentRef ref) =>
    AppComponent.myinjector = ref.injector
  );

And my AsyncAction now looks like this:

/// Action to request the add of a new Event.
class AddNewEventAction extends RegattaAction<String> implements AsyncAction {
  ///
  AddNewEventAction(String payload) : super(payload);

  @override
  ActionType get type => ActionType.addNewEvent;

  @override
  Future call(MiddlewareApi api) {
    EventService _event_service = AppComponent.myinjector.get(EventService);
    return _event_service.addEvent(payload).then(
        (event) { api.dispatch(addEvent(event)); }
    );
  }
}

Maybe there is something more elegant? But for me this seems to work fine.

I think ideally AddNewEventAction would be created by the injector too, and EventService would be injected via a constructor parameter. Unfortunately, Angular injectors can only vend singletons, and the workaround is not pretty.

Therefore, I think your solution is fine. One small suggestion: if EventService is itself a singleton (or more generally, if it's lifecycle encompasses that of AddNewEventAction), you can make it a private final field:

class AddNewEventAction extends RegattaAction<String> implements AsyncAction {
  AddNewEventAction(String payload) :
    _event_service = AppComponent.myinjector.get(EventService),
    super(payload);

  final EventService _event_service;

  @override
  ActionType get type => ActionType.addNewEvent;

  @override
  Future call(MiddlewareApi api) {
    return _event_service.addEvent(payload).then(
        (event) { api.dispatch(addEvent(event)); }
    );
  }
}

Thank you very much for your answers. After some more twiddeling I could now also get my tests working again! So I am closing this here. I would have more questions about greencat but I don't know where to aks them, since they are not really issues with the library itself but more with the usage of it.

Feel free to ask them here or in stack overflow and ping the link here. I do not have that much experience with the flux/redux architecture, actually I implemented greencat because I was learning it, still I am happy to help with whatever I can.