问题
I have two services, each of which takes a different WebSocket as a constructor parameter. I'd like to use AngularDart's dependency injection to pass in the WebSocket connection, but I can't rely on types alone (as I have two WebSockets).
How can I annotate or specify which specific WebSocket connection should to each service?
Let's pretend I have:
class ServiceOne {
WebSocket socketOne;
ServiceOne(this.socketOne);
}
and
class ServiceTwo {
WebSocket socketTwo; // different connection
ServiceTwo(this.socketTwo);
}
Thanks!
回答1:
This problem is well understood if you look at GUICE. (https://code.google.com/p/google-guice/) The issue is that when you are retrieving an instance you need to have some sort of Key
(this is what GUICE calls it). There are several ways you can get a Key
.
In AngularJS, we simplified the issue and said that the parameter name is the Key
. Since JS has no types in source code, that was really the only choice we had for a Key
.
In AngualDart we have improved it and used the Type
as the Key
. This has the benefit that the parameter name is irrelevant. But it creates an issue that you can only ever inject one of each Type
. For custom types it is not a big deal, but to have only one String
configuration type becomes an issue.
The solution to this problem is to have annotations on top of types. So that Annotation
+ Type
is the Key
. Here is what that may look like:
NOTE: None of this exist yet, it is only a proposal, how it will be solved.
class MyClass {
MyClass(@Url String url, @Secret String secret) { ... }
}
Module module = new Module();
module.value(key(String, [Url]), 'http://server.com/...');
module.value(key(String, [Secret]), 'A89B42C');
REQUEST: Since none of this is yet implemented, if you are passionate about helping AngularDart and would like to help make this a reality please contact me.
回答2:
I haven't seen anything about injection by type names. It's 'the' big improvment over JS that there aren't names anymore in Dart. You could embed the services or sockets in two different classes to distinuish them.
Without seeing some code it's somewhat hard to make suggestions.
Example
library main;
import 'dart:html';
import 'package:angular/angular.dart';
import 'package:di/di.dart';
/**
* usage examples
*/
class ServiceOne {
WebSocketWrapper1 socketOne;
ServiceOne(this.socketOne);
void doSomething() {
socketOne.ws.xxx();
}
}
class ServiceTwo {
WebSocketWrapper2 socketTwo; // different connection
ServiceTwo(this.socketTwo);
void doSomething() {
socketTwo.ws.xxx();
}
}
}
@NgController(
selector: '[ng-controller=alert-demo-ctrl]',
publishAs: 'ctrl')
class AlertDemoController {
WebSocketOnDemandWrapper1 _wsodw1;
AlertDemoController(this._wsodw1) {
}
String sendData() {
_wsodw1.ws.send("somedata");
}
}
@NgController(
selector: '[ng-controller=accordion-demo-ctrl]',
publishAs: 'ctrl')
class AccordionDemoController {
WebSocketOnDemandWrapper2 _wsodw2;
AccordionDemoController(this._wsodw2) {
}
String sendData() {
_wsodw2.ws.send("somedata");
}
}
/**
* injectable WebSockets
*/
class WebSocketWrapper1 {
WebSocket ws;
WebSocketWrapper1(this.ws);
}
class WebSocketWrapper2 {
WebSocket ws;
WebSocketWrapper2(this.ws);
}
class WebSocketOnDemandWrapper1 {
WebSocket ws;
WebSocketOnDemandWrapper1(){
ws = new WebSocket('ws://127.0.0.1:1337/ws');
}
}
class WebSocketOnDemandWrapper2 {
WebSocket ws;
WebSocketOnDemandWrapper2(){
ws = new WebSocket('ws://127.0.0.1:3173/ws');
}
}
class MyAppModule extends Module {
MyAppModule() {
type(ServiceOne);
type(ServiceTwo);
type(AlertDemoController);
type(AccordionDemoController);
type(WebSocketOnDemandWrapper1); // connect on demand
type(WebSocketOnDemandWrapper2); // connect on demand
// start connection on app startup and provide this connection when requested
value(WebSocketWrapper1, new WebSocketWrapper1(new WebSocket('ws://127.0.0.1:1337/ws')));
value(WebSocketWrapper2, new WebSocketWrapper2(new WebSocket('ws://127.0.0.1:3173/ws')));
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}
回答3:
DI 0.0.34 got special support for this use case using annotations
Annotation with parameters is not supported. I'm not sure if this is still planned (https://github.com/angular/di.dart/issues/46)
I have also added an example how primitives can now be used with DI. (not sure if this is of great use yet)
import 'package:di/di.dart';
import 'package:di/dynamic_injector.dart';
/**
* Annotation used to mark classes for which static type factory must be
* generated. For testing purposes not all classes are marked with this
* annotation, some classes are included in @Injectables at the top.
*/
class Injectable {
const Injectable();
}
/**
* Some dummy WebSocket class (just for demonstration)
*/
@Injectable()
class WebSocket {
String url;
WebSocket(this.url);
}
/**
* Allows to mark an injectable as 'one'
*/
class One {
const One();
}
/**
* Allows to mark an injectable as 'two'
*/
class Two {
const Two();
}
/**
* A class that implements updates.
* It needs a websocket marked as 'one'
*/
class Updates {
WebSocket ws;
Updates(@One() this.ws);
}
/**
* A class that implements chats.
* It needs a websocket marked as 'two'
*/
class Chat {
WebSocket ws;
Chat(@Two() this.ws);
}
/**
* The application module
*/
class AppModule extends Module {
AppModule() {
value(String, 'http://www.google.com', withAnnotation: AjaxUrl);
value(int, 8080, withAnnotation: ServerPort);
value(int, 1000);
factory(WebSocket, (Injector i) => new WebSocket('ws://game.example.com:12010/updates'), withAnnotation: One);
factory(WebSocket, (Injector i) => new WebSocket('ws://chat.example.com/games'), withAnnotation: Two);
type(Chat);
type(Updates);
}
Injector _injector;
Injector get injector {
if (_injector == null) {
_injector = new DynamicInjector(modules: [this]);
// Static injector => comment in and comment out above
// _injector = new StaticInjector(modules: [this],
// typeFactories: type_factories_gen.typeFactories);
}
return _injector;
}
}
/**
* Allows to mark a String as ajax url
* Just to demonstrate how to use primitive types with DI
*/
class AjaxUrl {
const AjaxUrl();
}
/**
* Allows to mark an int as server port
* Just to demonstrate how to use primitive types with DI
*/
class ServerPort {
const ServerPort();
}
void main(List<String> args) {
var module = new AppModule();
print('AjaxUrl: ${module.injector.get(String, AjaxUrl)}');
print('ServerPort: ${module.injector.get(int, ServerPort)}');
// primitives without annotation are not supported and throw an exception
// print('int: ${module.injector.get(int)}');
print('Chat: ${module.injector.get(Chat).ws.url}');
print('Updates: ${module.injector.get(Updates).ws.url}');
}
来源:https://stackoverflow.com/questions/21244644/how-can-i-dependency-inject-based-on-type-and-name-with-angulardart