【问题标题】:Renderer cannot be used in Service?渲染器不能在服务中使用?
【发布时间】:2017-12-21 20:16:14
【问题描述】:

是否可以在服务中或仅在组件中使用渲染器?

如果我将渲染器注入到服务中,我会收到错误,即缺少渲染器提供程序。

查看 plunker 的控制台:

import {Injectable, Component, Renderer, ElementRef} from 'angular2/core';

@Injectable()
class TestService {
  constructor(
    private _renderer: Renderer
    ) {}

  renderElement(elementRef: any) {
    this._renderer.createElement(elementRef.nativeElement, 'div');
  }
}

http://plnkr.co/edit/e8qlznCVbvZnXbLTk36z?p=preview

【问题讨论】:

  • 您希望为elementRef 注入什么元素?对于组件或指令,这是当前元素。服务没有。我不使用 TS,但我很确定 ElementRef 构造函数参数需要一个具体类型才能让注入器知道要传递什么(ElementRef 而不是 any)
  • 是的,我忘了添加 ElementRef 类型,但它也不能使用它。
  • 没想到会这样。我也只是想指出这一点。没有要为服务注入的元素。您希望注入什么元素?你想在哪里更新 DOM?
  • 您可以使用RootRenderer 获取Renderer,查看此plnkr。这个想法来自这个repo。所有功劳归于@justindujardin。
  • 谢谢,这很聪明。它工作正常。如果你回答...

标签: angular


【解决方案1】:

可能与Using Renderer in Angular 4 重复

您不能注入Renderer2,但我们可以运行RendererFactory2 以在@Injectable() 服务中获取Renderer2 实例。 有两种不同的方法可以解决这个问题issue。

在 Service 中获取 Renderer2 的实例

例如,Angular 在 webworkers 内部使用的方式。 我已经用下面的代码解决了这个问题:

import { Renderer2, RendererFactory2 } from '@angular/core';

@Injectable()
class Service {
    private renderer: Renderer2;

    constructor(rendererFactory: RendererFactory2) {
        this.renderer = rendererFactory.createRenderer(null, null);
    }
}

RendererFactory2.createRenderer方法的参数为:

  • hostElement 类型为 any
  • type 类型为 RendererType2|null

可以看到(null, null)参数在这里: https://github.com/angular/angular/blob/e3140ae888ac4037a5f119efaec7b1eaf8726286/packages/core/src/render/api.ts#L129

从组件传递 Renderer2

// declare public property in your service
@Injectable()
class Service {
  renderer: Renderer;
}

// pass renderer to service in your component file
class App {
  name:string;
  constructor(service: Service, renderer: Renderer2) {
      service.renderer = renderer;
  }
}

【讨论】:

  • 我喜欢你的第一个解决方案,但它看起来违反了 Angular 规则。您是否在项目中使用过它并发现任何问题?可以用吗?
  • @PedroArantes 这不反对 Angular 方式,我在生产中使用它。解决方案之间的唯一区别是首先创建新的渲染器,然后使用现有实例。两者都使用公共库方法。 RendererFactory2 也在 Angular 内部用于相同的提议。
  • 另一种解决方案是简单地将service 转换为directive,将指令添加到组件的HTML 模板中,然后从组件的TS 文件中像@ViewChild(DirectiveClassName) directiveVariableName: DirectiveClassName; 一样访问它
  • 这个服务文件的测试用例怎么样..??
  • 你好!感谢您的回复!您是否知道在服务中使用renderFactory.createRenderer(null, null) 与将其注入组件并在那里使用相比是否存在性能劣势?我正在使用 SVG,并且希望工具服务负责创建 SVGElement - 使用 this.renderer.createElement(..., ...),但我不确定这是否是一个明智的决定。你碰巧知道吗?
【解决方案2】:

是的,就像 Eric 说的那样,您可以将它从实际组件传递到服务,它就会起作用。

但是,渲染对象的责任在于组件,而不是服务,服务旨在处理业务逻辑(主要检索数据,具体取决于您的应用)。

整个想法是,如果您改变您的演示框架/库或任何与演示相关的东西,您可以重用整个服务层,如果您这样做,您会将您的服务耦合到您的演示,使以后更难进行更改。

如果你想在组件之间重用渲染逻辑,我会创建一个带有逻辑的父抽象类并使用组件扩展它,子组件将被注入渲染器并使用 @ 将其传递给父类987654321@ 应该调用父级构造函数的方法。

至少您可以像建议的解决方案一样创建一个@Injectable 对象,但将其称为SomethingRenderer 而不是Service,并将其与您的其他真实数据服务分开。

【讨论】:

  • 如何监听 Window 上的全局事件是一个组件的职责?可以加window.addEventListener,不过好像不可测试
  • 我要指出有很多服务注入一个操纵 dom。 Toast,对话服务。处理 RTL 的服务,出于响应性或其他原因在 dom 中添加/删除类。由于这个原因,我不确定这是否应该是公认的答案。
猜你喜欢
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-22
  • 2023-02-01
  • 2017-11-05
  • 2020-10-20
  • 1970-01-01
相关资源
最近更新 更多