在 Angular2 服务中包装外部脚本

Wrapping external script in Angular2 service

我有一个 angular-cli 生成的项目,我想将其与 deployd 后端一起使用。 Deployd 提供了一个脚本来访问它的 API,可以从 http://<deployd-host>/dpd.js 加载它。这将创建一个全局 dpd 对象,该对象可以从 javascript 全局上下文(例如从 Chrome 开发工具控制台)访问 API。

我想将它包装在一个 Angular2 服务中,这样我就可以注入一个模拟服务来进行测试等。任务是从 URL 加载脚本,然后获得对全局 dpd 对象。我已经查看了 SO post 但无法获得可接受的答案。如果我手动将脚本添加到 document 对象,我将无法访问 window.

上的 dpd 对象

此外,加载脚本的 URL 会因环境而异,例如http://localhost:3000/dpd.js 用于本地开发,http://dev.example.com/dpd.js 用于暂存,http://www.example.com/dpd.js 用于生产。所以理想情况下,我也可以在服务中配置它。

正在寻找类似下面的东西。

@Injectable()
export class DpdService {
  constructor() {
    if (getEnvironmentSomeHow() == 'development') {
      loadScriptFrom("http://localhost:3000/dpd.js");
    } else {
      loadScriptFrom("http://dev.example.com/dpd.js");
    }
    dpd = window.dpd;
  }

  public session(): Observable<Session> {
    return Observable.fromPromise(dpd.sessions.get());
  }
}

应用环境完全取决于开发者的选择。这些可以是有条件地包含依赖于 Node 环境变量的 TS 文件。它可以是根据客户端全局变量定义 Angular 提供程序的单个文件(可能随 Webpack DefinePluginEnvironmentPlugin 提供,例如参见 [​​=19=])。在其最简单的形式中,它只是客户端全局 ENV 变量,所有决定都是就地做出的 - 它甚至可以在 HTML 中使用服务器端模板进行设置:

<script>
   window.ENV = <% SERVER_SIDE_ENV_VARIABLE %>
</script>

由于脚本应该在应用程序初始化时加载,因此必须在 APP_INITIALIZER 多供应商中加载:

...
import {APP_INITIALIZER} from '@angular/core'
import {DOCUMENT} from '@angular/platform-browser'

@Injectable()
export class DpdService {
  dpd: any;

  constructor(@Inject(DOCUMENT) document: Document) {}

  load() {
    const srcBase = window.ENV === 'dev'
      ? 'http://localhost:3000/'
      : 'http://dev.example.com/';

    const script = this.document.createElement('script');
    this.document.body.appendChild(script);

    return new Promise((resolve, reject) => {
      script.onload = resolve;
      script.onerror = reject;
      script.async = true;
      script.src = srcBase + 'dpd.js';
    }).then(() => {
      this.dpd = window.dpd;
    });
  }

  session(): Observable<Session> {
    return Observable.fromPromise(this.dpd.sessions.get());
  }
}

export function dpdAppInitializerFactory(dpdService: DpdService) {
  return () => dpdService.load();
}

...
providers: [
  DpdService,
  {
    provide: APP_INITIALIZER,
    useFactory: dpdAppInitializerFactory,
    deps: [DpdService],
    multi: true
  }
],

...

在应用程序初始化时,来自 load 方法的承诺已实现并设置 dpd 属性。