从服务附加 DOM 事件处理程序

Attaching DOM event handler from a service

当前附加 DOM 事件处理程序的做法是使用 Renderer2.listen() 以防您需要这样做而不是从模板。这种方法适用于 Directives \ Components.

如果您需要在 Service 中执行相同的操作,问题是尝试将 Renderer2 实例注入服务构造函数:

export class SomeService {
    public constructor(private readonly renderer: Renderer2) {
    }
}

会导致缺少 DI 提供程序异常:

StaticInjectorError(Platform: core)[DefaultValueAccessor -> Renderer2]

为了缓解它,我们可以使用 RendererFactory2 class 来:

Creates and initializes a custom renderer that implements the Renderer2 base class.

但我不确定为在服务中使用它而创建新的 Render2 是否是一种 good\recommended 方法。

那么,问题是在服务中附加 DOM 事件处理程序的推荐做法是什么?

其他人评论说这种行为可能更适合 ComponentDirective,但在较低级别上查看 Angular 中发生的事情很有趣。这是一个非常模块化的框架,这是一个了解更多关于 DI 的好机会。

Renderer2 在服务方面有点特殊,因为它通过内部 Renderer2Interceptor 模板 有更多控制。它的 scope 与在根级别注入的提供程序或其他全局 Angular 提供程序不同:因为它用于呈现组件和指令的模板,所以它的范围仅限于那些声明的类型。这是一个非常低级的提供者,Angular 用来创建带有视图的声明,因此它可以被注入到它们中——而且由于服务也是一个 @Injectable,Angular DI 层次结构单独处理它们,因此 Renderer2 在它们的层次结构中不可用。

综上所述,Renderer2 在一个模块中可供 declarations 使用。但不是 providersdeclarations 是实例,需要附加模板,但 providers 是单例,模板对他们来说意义不大。

使用 RendererFactory 直接 Renderer2 注入服务的唯一方法。

虽然我认为您对不创建 Renderer2 的另一个实例的担忧是有道理的,但请查看关于 the Angular repo:

的评论

rendererFactory.createRenderer(null, null) would help, if no (concrete) parameters passed, it will just return the default renderer without create a new one. (sic)

因此,使用 RendererFactory2.createRenderer(null, null) 只会 return 默认值 DomRenderer。工厂的存在是为了提供创建 custom 渲染器的能力,幸运的是 Angular 维护得很好,如果调用工厂就不会创建重复的 DomRenderer .

因此,只需将 RendererFactory2 注入服务并从中获取默认值 Renderer2

此外,感谢您 知道 Renderer 在 Angular 中很重要,并努力使用它。直接去 DOM 操纵的人太多了。