3
votes

I have a backend built with Elixir / Phoenix and a frontend built with Angular 2 (Typescript, Brunch,io for building, ES6). I now want to use Phoenix Channels. And I'm a bit desperate trying to use the Phoenix Javascript Client in my frontend.

When I install https://www.npmjs.com/package/phoenix-js via npm install phoenix-js and then try to inject it into a service in angular like this:

import { Socket } from "phoenix-js";

I always get the error Cannot find module phoenix-js during compilation.

I'm a bit stuck and every hint on how to get this to work would be greatly appreciated.

Thanks

1

1 Answers

8
votes

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.