gpt4 book ai didi

angular - 通过 Angular 中的工厂打开通用注入(inject)

转载 作者:行者123 更新时间:2023-12-03 23:47:27 24 4
gpt4 key购买 nike

我希望能够使用通用工厂注入(inject)具有约束到 Angular 中的接口(interface)的泛型服务......如果我声明每种注入(inject)类型的提供者,我可以做到这一点,但这违背了我为什么要这样做的目的想要这样。

我想要的是这样的:

interface WorkerServiceContract {
doWork(): void;
}

class MyService<T extends WorkerServiceContract> {
constructor(private worker: T) {}

doWorkWrapper() { this.worker.doWork(); }
}

所以在任何时候我都可以做一个:

@Injectable({ providedIn: 'root' })
class FooWorkerService implements WorkerServiceContract {
doWork() { console.log('foo'); }
}

@Injectable({ providedIn: 'root' })
class BarWorkerService implements WorkerServiceContract {
doWork() { console.log('bar'); }
}

@Component(/*blabla*/)
class MyComponent {
constructor(private fooWorker: MyService<FooWorkerService>, private barWorker: MyService<BarWorkerService>) {}
}

我知道我可以为每个 WorkerServiceContract 使用特定 token 声明注入(inject)。可能性,但我想知道是否有办法(我已经查看了文档但无法弄清楚)让它“打开”......所以我可以做 类似于 (这显然行不通):

providers: [
{
provide: MyService<T extends ServiceWorker>
useFactory: (worker: T) => new MyService<T>(worker);
deps: [T]
}
]

我知道这在提供者定义中是不可能的(因为它不知道 T ),但是有没有任何机制可以允许像这样的“通用”工作?这可能是显而易见的事情,但我似乎可以开始做这件事

我正在使用 Angular 9

真实场景(附录)

我们为什么想要这个(现实生活场景)的全部理由是:

我有一个工具生成的服务类(来自 Swagger/OpenApi)。对于该服务,我创建了一个代理/包装服务,它拦截所有对 API 的 http(不是 http,而是方法,这些方法不仅仅是调用 http 客户端)调用,并通过管道返回的 observables 来处理错误(和成功,实际上)显示 UI 通知(并创建对诊断日志服务器的其他调用等)。

这些处理程序有时是通用的,但每个 View (取决于调用的 API)可能希望拥有自己的处理程序(例如,一个可能显示一个 toast,一个可能显示一个弹出窗口,一个可能希望将调用转换为一些方法,或其他)。

我可以在每次调用 API 时都这样做,但在我的团队中,我们发现将这些问题(处理成功调用与从 API 接收到的正常数据以及错误处理)分开有助于提高可读性和组件代码的大小(以及每个代码的责任)。我们已经通过对构造函数的“简单”调用解决了这个问题,例如:

constructor(private apiService: MyApiService) {
this.apiService = apiService.getProxyService<MyErrorHandler>();
}

返回 Proxy处理所有这些。 这工作得很好 ,但我们正在讨论使它“更清洁”的想法,如下所示:

constructor(private apiService: MyApiService<MyErrorHandler>) {}

并让 DI 容器上的工厂为我们创建该代理,这将带来以下好处:a) 不必记住对构造函数进行调用,b) 清楚地了解所有依赖项(包括错误处理程序)直接在构造函数参数上(无需深入构造函数代码,根据实际组件可能还有其他内容)

不,一个 HttpClient拦截器在这里不起作用,因为自动生成的服务比 HttpClient 更多。调用(我们想要处理从该服务返回的内容,而不是直接处理 HttpResponse 对象)

最佳答案

更新

也许尝试使用 token 的注入(inject)器

const services = new Map();
const myService = <T>(service: T): InjectionToken<MyService<T>> => {
if (services.has(service)) {
return services.get(service);
}

const token = new InjectionToken<<T>(t: T) => MyService<T>>(`${Math.random()}`, {
providedIn: 'root',
factory: injector => new MyService(injector.get(service)),
deps: [Injector],
});

services.set(service, token);
return token;
};

class MyComponent {
constructor(
@Inject(myService(FooWorkerService)) private foo: MyService <FooWorkerService>,
@Inject(myService(BarWorkerService)) private bar: MyService <BarWorkerService>,
) {
}
}

原创

你是对的,泛型在编译后不存在,因此不能用作提供者。

要解决它,您需要在组件中注入(inject)工厂本身。因为无论如何您都会指定泛型的类型,所以现在您需要更多代码来实现所需的行为。

您可以使用


const MY_SERVICE_FACTORY = new InjectionToken<<T>(t: T) => MyService<T>>('MY_SERVICE_FACTORY', {
providedIn: 'root',
factory: () => worker => new MyService(worker),
});

// just a helper to extract type, can be omitted
export type InjectionTokenType<Type> = Type extends InjectionToken<infer V> ? V : never;

class MyComponent {
constructor(
@Inject(MY_SERVICE_FACTORY) private serviceFactory: InjectionTokenType<typeof MY_SERVICE_FACTORY>,
foo: FooWorkerService,
bar: BarWorkerService,
) {
this.fooWorker = this.serviceFactory(foo);
this.barWorker = this.serviceFactory(bar);
}
}

还为了保持构造函数中的代码更清晰,您可以将其移动到组件的提供者。

@Component({
// blabla
providers: [
{
provide: 'foo',
useFactory: t => new MyService(t),
deps: [FooWorkerService],
},
{
provide: 'bar',
useFactory: t => new MyService(t),
deps: [BarWorkerService],
},
],
})
class MyComponent {
constructor(
@Inject('foo') private fooWorker: MyService<FooWorkerService>,
@Inject('bar') private barWorker: MyService<BarWorkerService>,
) {}
}

关于angular - 通过 Angular 中的工厂打开通用注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61792963/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com