【问题标题】:How to write a service that requires constructor parameters?如何编写需要构造函数参数的服务?
【发布时间】:2017-02-22 16:16:38
【问题描述】:

我有一个声明MetricsService 服务的组件。此服务需要 HttpModule 和两个 strings,用于定义主机和要使用的身份验证密钥。

metrics服务如下:

@Injectable()
export class MetricsService {
    constructor(
        private http: Http,
        public wsAuthKey: string,
        public wsHost: string
        ) {
        this.wsAuthKey  = wsAuthKey || "blahblahblahblahblahblahblahblah=";
        this.wsHost     = wsHost    || "https://preprod-admin.myservice.ws";
    }

使用它的组件写法如下:

export class DatavizComponent implements OnInit, OnChanges {
    constructor(
        private zms: MetricsService,
    ) { 
    }

我的问题是我应该如何编写组件构造函数,以便整个工作,包括传递主机和密钥(但不传递 http)?

注意:当前编写的代码无法编译。

更准确地说,我希望该组件提供与应用相关的数据,如下所示:

 export class DatavizComponent implements OnInit, OnChanges {
        constructor(
            private zms = MetricsService("http://myhost.com", "mykey"),
        ) { 
        }

但如果这样可行,如何通过http?

提出解决方案后的更新:

export class MetricsService {

    constructor(
        private http: Http,
        @Inject('wsAuthKey') @Optional() public wsAuthKey?: string,
        @Inject('wsHost') @Optional() public wsHost?: string
        ) {
        this.wsAuthKey  = wsAuthKey || "blahblah=";
        this.wsHost     = wsHost    || "https://preprod-admin.host.ws";


        console.log("MetricsService constructor="
            + " wsAuthKey="+this.wsAuthKey
            + ", wsHost="+this.wsHost
        );

    }

在组件中:

@Component({
    selector:    'dataviz-offers-volumes',
    templateUrl: 'app/dataviz.component.html',
    styleUrls:  ['app/dataviz.component.css'],
    encapsulation: ViewEncapsulation.None,
    providers: [
        {provide: 'wsAuthKey',  useValue: 'abc'}, 
        {provide: 'wsHost',     useValue: 'efg'}, 
    ],
})
export class DatavizComponent implements OnInit, OnChanges {

    @ViewChild('chart') private chartContainer: ElementRef;
    @Input() private graphId:string;
    @Input() private wsAuthKey:string;
    @Input() private wsHost:string;
    @Input() private maxSamples=12;

    constructor(
        private zms: MetricsService
    ) { 
    }

在构造函数中,日志如下(不传值):

MetricsService constructor= wsAuthKey=blahblah=, wsHost=https://preprod-admin.host.ws

应该在哪里显示“abc”和“efg”。

但我想知道使用 dataviz 组件的组件是否没有问题。 在该组件中,传递了以下信息:

@Input() private wsAuthKey:string;
@Input() private wsHost:string;

我希望标签可以选择性地预设主机和密钥:

                <h1>dataviz volume</h1>
                <div class="chartContainer left" title="Simultaneous offers via dataviz directive">
                    <dataviz-offers-volumes 
                        id="dataviz-volumes1"
                        [graphId]="graphId"
                        [wsAuthKey]="'myauthkey'"
                        [wsHost]="'http://myhost.com'"
                        [maxSamples]="123"
                    >
                    </dataviz-offers-volumes>
                </div>

【问题讨论】:

  • 您打算将这些 wsAuthKey 和 wsHost 传递到哪里?它们是特定于应用程序还是特定组件?您似乎没有尝试在示例中传递任何内容,因此尚不清楚这应该如何工作。
  • 这些数据特定于使用该组件的应用程序(尽管该服务提供默认值)。我期望从组件内提供它们? (我在问题中添加了一小部分)@estus
  • 当然,它们在构造函数中是未定义的。它们是绑定,在构造时不可用。此外,我认为它们没有理由成为绑定(输入),它们没有改变。这完全取决于如何使用这些值。这些值应该通过 attrs 传递是否有真正的原因,或者您是否正在尝试提供此功能以防万一您需要它?这将使整个事情变得更加复杂。您需要提供有关 MetricsService 如何工作的详细信息 - 例如,它可能不能是单例。
  • 关于它应该如何工作:一个页面可以包含多个相同或不同类的dataviz实例(class=相同类型的图形但可能具有不同的属性。属性可以在页面中提供给点到特定基础设施(由指标服务提供的预生产、开发等)或比较来自不同基础设施的相同指标。此外,一个属性用作周期(每天、每周等),因此您可以有两个数据可视化相同的课程,相同的基础,但同一页面上的不同时期。(我正在将我的 js 移植到 Angular)
  • 有问题的属性具体是 wsAuthKey 和 wsHost。真的应该为每个指令提供它们吗?因为它们在那里是明显的障碍。如果不知道它们是如何在 MetricsService 中使用的,就不可能对更新的问题给出高质量的答案。

标签: angular service constructor


【解决方案1】:

您可以通过添加@Optional() (DI) 和? (TypeScript) 来使参数可选,对于不支持作为提供程序键的原始值使用@Inject(somekey)

@Injectable()
export class MetricsService {
    constructor(
        private http: Http,
        @Inject('wsAuthKey') @Optional() public wsAuthKey?: string,
        @Inject('wsHost') @Optional() public wsHost?: string
        ) {
        this.wsAuthKey  = wsAuthKey || "blahblahblahblahblahblahblahblah=";
        this.wsHost     = wsHost    || "https://preprod-admin.myservice.ws";
    }
providers: [
  {provide: 'wsAuthKey', useValue: 'abc'}, 
  {provide: 'wsHost', useValue: 'efg'}, 
]

如果提供,则通过,否则将被忽略,但 DI 仍然可以注入 MetricsService

【讨论】:

  • 到目前为止一切顺利,除了提供者部分:我把它放在使用服务的组件中,对吧?
  • 如果组件中也提供了服务本身,则可以将提供者放入组件中。需要在与服务相同或更高级别提供这些值。 AppModule 提供的服务无法从 MyComponent 注入值。
  • 正如我所说,这只有在组件中也提供了 MetricsService 时才有效。 NgModule 中提供的服务不能从组件中注入值。
  • 我用 Angular 6 试过了,但它不工作。我必须使用InjectorToken,正如stackoverflow.com/a/49202291/9662881 中所述。
【解决方案2】:

这是this question 中特别描述的常见配方。它应该是一个保存配置的服务:

@Injectable()
export class MetricsConfig {
  wsAuthKey = "blahblahblahblahblahblahblahblah=";
  wsHost = "https://preprod-admin.myservice.ws";
}

@Injectable()
export class MetricsService {
    constructor(
        private http: Http,
        metricsConfig: MetricsConfig
    ) {
        this.wsAuthKey  = metricsConfig.wsAuthKey;
        this.wsHost     = metricsConfig.wsHost;
    }
}

在需要更改的情况下,可以为整个模块或特定组件覆盖或扩展它:

@Component(
  ...
  { provide: MetricsConfig, useClass: class ExtendedMetricsConfig { ... } }
)
export class DatavizComponent ...

在这种情况下,没有必要将MetricsConfig 设为一个类。它也可以是 OpaqueToken 价值提供者。但是类可以很方便地扩展,更容易注入,并且已经提供了输入接口。

【讨论】:

    【解决方案3】:

    来自官方文档:https://angular.io/guide/dependency-injection-in-action#injectiontoken

    在构造函数中使用@Optional 装饰器:

    export class MyService {
      constructor( @Optional() public var: type = value ) { }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-15
      • 1970-01-01
      • 2021-11-24
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多