如何在Angular 2应用程序中使用Phoenix Channels / Sockets?

时间:2016-07-11 14:45:26

标签: javascript typescript angular phoenix-framework brunch

我有一个用Elixir / Phoenix构建的后端和一个用Angular 2构建的前端(Typescript,Brunch,io用于构建,ES6)。我现在想要使用Phoenix Channels。而且我有点绝望地试图在我的前端使用Phoenix Javascript客户端。

当我通过npm install phoenix-js安装https://www.npmjs.com/package/phoenix-js,然后尝试将其注入角色服务时,这样:

import { Socket } from "phoenix-js";

我在编译期间总是收到错误Cannot find module phoenix-js

我有点陷入困境,每一个如何让它发挥作用的暗示都将不胜感激。

由于

1 个答案:

答案 0 :(得分:6)

Edit: I'm going to leave the old answer below - even though it is a bit emberassing. Getting everything to work and using the most recent version of the Phoenix JS Client with Angular 2 was even easier than I thought and I was just terribly confused.

The Phoenix JS client has been extracted as an npm package and can be found here. Install it with npm install --save phoenix. Then load it as additional dependency. In my setup with SystemJS it was just a matter of adding the necessary configuration:

import { join } from 'path';
import { SeedConfig } from './seed.config';
import { InjectableDependency } from './seed.config.interfaces';

export class ProjectConfig extends SeedConfig {
  PROJECT_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'project');

  constructor() {
    super();
    // this.APP_TITLE = 'Put name of your app here';
    let additional_deps: InjectableDependency[] = [
      // {src: 'jquery/dist/jquery.min.js', inject: 'libs'},
      // {src: 'lodash/lodash.min.js', inject: 'libs'},
      {src: 'phoenix/priv/static/phoenix.js', inject: 'libs'}
    ];

    const seedDependencies = this.NPM_DEPENDENCIES;

    this.NPM_DEPENDENCIES = seedDependencies.concat(additional_deps);
  }
}

Now we have it in the global scope and just need to use declare var in the Angular 2 service typescript file where we want to use it. Here was where I made a crucial mistake. I tried to access Socket directly and therefor used declare var Socket: any. Which always led to the error Socket is undefined. But this issue got me in the right direction: If you use the transpiled ES5 version (and not ES6) you have to use Phoenix.Socket (because of namespacing I guess).

So this is how my service looks like now:

import { Injectable } from '@angular/core';

declare var Phoenix: any;

@Injectable()
export class PhoenixChannelService {

  socket: any;

  constructor() {
    this.socket = new Phoenix.Socket("ws://localhost:4000/socket", {
      logger: ((kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }),
      transport: WebSocket
    });
    this.socket.connect();
  }
}

Now everything works like a charm.

If you don't want to install the client via npm, there is a more basic way: Just get the latest version of the JS Client from GitHub from /priv/static, store it in the folder where you keep your static assets and include it directly in your index.html:

<script src="/path/to/static/js/phoenix.js"></script>

The rest stays the same.

Note: If you want to use it with typescript type definitions, this npm package might be a good starting point - even though it is a bit old.


Old and embarrassing answer: So I think I figured it out. Writing my own definition file wasn't an option. And since all the documented code on how to use the phoenix client is in ES6 I got stuck including the transpiled ES5 version directly in my index.html. But the first clue was this article.

I then found this issue on GitHub which is about extracting the Phoenix Client. Via this I then found this npm package, which is a bit outdated but seems to work. I install it with npm insall --save phoenix-js and then load the dependency in my project. Since my Angular App is based on this seed it goes into my project definition (and make sure to load the Globals version of the phoenix client:

import { join } from 'path';
import { SeedConfig } from './seed.config';
import { InjectableDependency } from './seed.config.interfaces';

export class ProjectConfig extends SeedConfig {
  PROJECT_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'project');

  constructor() {
    super();
    // this.APP_TITLE = 'Put name of your app here';
    let additional_deps: InjectableDependency[] = [
      // {src: 'jquery/dist/jquery.min.js', inject: 'libs'},
      // {src: 'lodash/lodash.min.js', inject: 'libs'},
      {src: 'phoenix-js/dist/glob/main.js', inject: 'libs'}
    ];

    const seedDependencies = this.NPM_DEPENDENCIES;

    this.NPM_DEPENDENCIES = seedDependencies.concat(additional_deps);
  }
}

Now I can use it in my angular 2 service:

import { Injectable } from '@angular/core';

declare var Socket: any;
declare var Channel: any;

@Injectable()
export class PhoenixChannelService {

  socket: any;
  channel: any;

  constructor() {

    this.socket = new Socket("/socket", {
      logger: ((kind, msg, data) => { console.log(`${kind}: ${msg}`, data) })
    });
    this.socket.connect({user_id: "123"});

  }

}

Depending on your build process (I use gulp) and other factors you might have to adapt. But I hope this provides some help to other people stuck with this issue.

Edit: This is the official extracted JS client for Phoenix. But I didn't get it to work in my setup, probably because of Typescript.