【问题标题】:Wrapping external script in Angular2 service在 Angular2 服务中包装外部脚本
【发布时间】:2017-05-19 03:42:30
【问题描述】:

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

我想把它包装在一个 Angular2 服务中,这样我就可以注入一个模拟服务来进行测试等。任务是从 URL 加载脚本,然后访问全局 dpd 对象。我查看了this SO 帖子,但无法获得公认的工作答案。如果我手动将脚本添加到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());
  }
}

【问题讨论】:

  • 能否分享一些代码
  • 可能重复:stackoverflow.com/questions/39942118/… 非常详细的代码解释
  • 你在问题​​中展示的方法有什么问题?
  • @estus 1.不知道环境如何,2.没有loadScriptFrom的优雅实现,3.如果通过向文档添加脚本标签来加载,则dpd当我使用它时,窗口上的对象不可用。如果我从 Chrome 中的控制台检查,它是可用的,所以这似乎是一个异步问题。

标签: angular angular2-services


【解决方案1】:

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

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

由于脚本要在应用初始化时加载,所以必须在APP_INITIALIZERmultiprovider中加载:

...
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 方法的promise 已实现,dpd 属性已设置。

【讨论】:

  • 效果很好。有几件事……DpdService.load() 应该是dpdService.load()。另外,在这样做之后,我得到Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function (position 23:19 in the original .ts file), resolving symbol AppModule
  • 环境方面,这里使用angular-cli,应该管理环境。
  • 所以错误似乎是aot的问题...应该与您的解决方案无关。
  • 为了避免 AoT 的问题,很可能应该是 export const dpdAppInitializerFactory = (dpdService: DpdService) =&gt; () =&gt; dpdService.load()... useFactory: dpdAppInitializerFactory
  • 那行不通……但是让它成为一个函数。你能更新你的答案吗...export function dpdAppInitializerFactory(dpdService: DpdService) { return () =&gt; dpdService.load(); }
猜你喜欢
  • 1970-01-01
  • 2017-11-23
  • 2012-07-21
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多