Using Phoenix Channels and Sockets in an Angular 2 application: A comprehensive guide

My backend is built with Elixir / Phoenix and my frontend is built with Angular 2 (Typescript, Brunch.io for building, ES6). I'm eager to start using Phoenix Channels but I'm struggling to integrate the Phoenix Javascript Client into my frontend.

After installing https://www.npmjs.com/package/phoenix-js via npm install phoenix-js, I attempted to inject it into a service in Angular as follows:

import { Socket } from "phoenix-js";

Unfortunately, I keep receiving the error message: Cannot find module phoenix-js during compilation.

I would greatly appreciate any suggestions on how to resolve this issue and successfully implement the Phoenix Javascript Client into my project.

Thank you.

Answer №1

Update: I've decided to keep the previous answer below, despite its embarrassing nature. Surprisingly, setting up and utilizing the latest version of the Phoenix JS Client with Angular 2 was much simpler than expected, and my confusion led me astray.

The Phoenix JS client is now available as an npm package, accessible here. To install it, use npm install --save phoenix. Then include it as an additional dependency. In my setup using SystemJS, all I had to do was add 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();
    let additional_deps: InjectableDependency[] = [
      {src: 'phoenix/priv/static/phoenix.js', inject: 'libs'}
    ];

    const seedDependencies = this.NPM_DEPENDENCIES;

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

With this setup, the Phoenix JS client is now globally available, requiring only a declare var in the Angular 2 service typescript file where it will be used. This is where I initially made a crucial error by trying to directly access Socket using declare var Socket: any, resulting in the error Socket is undefined. However, this thread pointed me in the right direction: when using the transpiled ES5 version (rather than ES6), you must use Phoenix.Socket due to namespacing.

Here's how my revised service looks like:

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();
  }
}

Everything is functioning perfectly now.

If you prefer not to install the client through npm, there is a more basic method: simply grab the latest version of the JS Client from GitHub under /priv/static, store it with your static assets, and include it directly in your index.html:

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

Everything else remains unchanged.

Note: For those interested in using type definitions with Typescript, consider checking out this npm package as a starting point, although it may be slightly outdated.


Previous outdated response: Initially, I struggled with finding a solution. Since all examples provided were in ES6, I attempted including the transpiled ES5 version directly into my HTML file. A helpful tip came from this article.

I then stumbled upon this issue on GitHub regarding extracting the Phoenix Client. From there, I discovered the somewhat dated phoenix-js npm package. After installing it with npm insall --save phoenix-js and incorporating it into my project, aligned with this seed as my base Angular App. The implementation went into my project definition:

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();

    let additional_deps: InjectableDependency[] = [
      {src: 'phoenix-js/dist/glob/main.js', inject: 'libs'}
    ];

    const seedDependencies = this.NPM_DEPENDENCIES;

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

Now, I can utilize 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 (such as using gulp) and other factors, adjustments may be needed. Nevertheless, I hope this assists others grappling with a similar challenge.

Update: The official extracted JS client for Phoenix is located here. Despite encountering issues making it function within my setup, likely due to Typescript constraints.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular2 components or module npm packages offer a wide range of functionality and

Looking to develop an npm package containing my Angular2 components. Should I create an angular module and export it, or is exporting just the components sufficient? What advantages does each method offer? Providing examples would be greatly appreciated ...

What could be causing the stack overflow in this (partial) MergeSort Implementation?

As I'm working on my own version of MergeSort, which implements recursion with a base case, the only issue I have yet to address is how arrays are imperfectly halved when their length % 2 != 0. For now, I require arrays of a length that is a power of ...

Changing a password on Firebase using Angular 5

I am in the process of developing a settings feature for user accounts on an application I've been working on. One key functionality I want to include is the ability for users to update their password directly from the account settings page. To enable ...

Exploring the syntax of typescript when using createContext

Just starting out with typescript and I have some questions. Could someone break down the syntax used in this code snippet for me? What is the significance of having two groups containing signIn, signOut, and user here? Is the first group responsible fo ...

Duplicating labels with JavaScript

I need assistance with copying HTML to my clipboard. The issue I am encountering is that when I try to copy the button inside the tagHolder, it ends up copying <span style="font-family: Arial; font-size: 13.3333px; text-align: center; background-color: ...

What is the method for showcasing an image before it has been uploaded?

I must start by apologizing for my English, as I am not the most fluent speaker. Here's where I'm facing a dilemma: I have created an application that allows users to edit questions for a game. These questions are stored on a server and can be ...

Is there a method in TypeScript to retrieve property names from an interface resembling reflections in C#?

I am working with an interface in TypeScript/Angular that has various properties. I'm curious if there is a way to access the property names within the code. Here's an example of what my interface looks like: export interface InterfaceName ...

The functionality of React setState seems to be malfunctioning when activated

Having encountered an unusual issue with react's setState() method. Currently, I am utilizing Azure DevOps extensions components and have a panel with an onClick event that is intended to change the state of IsUserAddedOrUpdated and trigger the addOr ...

Nullify the unfulfilled fetch call

When a value is entered in the search bar on the webpage, it gets added to a URL and used to retrieve JSON data. Everything works smoothly, but if a value is inputted that the API doesn't have information for, a null response is returned. The questio ...

Is there a way for me to have a table automatically scrolled to a specific column upon loading the HTML page?

My table contains a dynamic number of columns based on the months inputted from the database starting from a start_date and ending at an end_date. I have the current_date stored as a variable and want the table to load with the x-scrollbar positioned right ...

Utilize HTTPS and HTTP with Express framework in node.js

Currently, I am utilizing the express (3.0) framework on node.js to handle routing in my application. While most of the application makes use of the http protocol, there is a specific route that I intend to serve exclusively via https. This particular par ...

TypeScript has two variable types

I'm facing a challenge with a function parameter that can accept either a string or an array of strings. The issue arises when trying to pass this parameter to a toaster service, which only accepts the string type. As a result, when using join(' ...

The surprising behavior of Rails rendering partials even when they are commented out has

I'm intrigued by how Rails 5 handles partials and if there might be a hidden problem I haven't encountered yet. On my current page, I have two partials - one that is included in the HTML itself, and another that is supposed to render inside an aj ...

Improper application of Angular bindings in Chrome when navigating to a page using the browser's back button

Have you encountered this issue before? I have a few textboxes generated within an ngFor loop: <tr *ngFor="let tableRow of lineItems; trackBy:trackByIndex; let rowIndex = index; "> <td class="psavingsSmallerGridCell"><input c ...

How to eliminate spaces while preserving line breaks in a textarea using JS or jQuery

I have been trying to figure out how to remove extra spaces from the beginning and end of lines while keeping the line breaks intact. Here's what I attempted: function removeSpaces(string) { return string.split(' ').join(''); } ...

What is the process for defining a state using React Native and TypeScript?

Recently, I've embarked on my journey with React Native and decided to incorporate TypeScript into my development process. As I attempted to set my state, an error popped up that reads as follows: An issue arose while trying to assign the argument &a ...

Tips for managing errors when using .listen() in Express with Typescript

Currently in the process of transitioning my project to use Typescript. Previously, my code for launching Express in Node looked like this: server.listen(port, (error) => { if (error) throw error; console.info(`Ready on port ${port}`); }); However ...

Tips on how to engage in a spontaneous audio experience

I am currently developing a Discord bot with the intention of having it play a random mp3 file upon joining a voice channel. case"join": message.delete( {timeout: 5000}) const voiceChannel = message.member.voice.channel ...

Verify the level of opacity in the image

I'm working on a website that allows users to upload PNG images and save them. However, before they can save the image, I need to check if it contains transparency. Is there a way to determine if an image is not 24-bit using JavaScript? <img id= ...

Encountering the error 'node' getProperty of undefined while trying to retrieve data from an array stored in my state variable

Hello, I am currently developing an app that retrieves images from Instagram using axios. I have successfully stored the image files in an array named 'posts' within my state. Looping through this array to display each image is not an issue for m ...