How to use Phoenix Channels / Sockets in an Angular 2 app? - javascript

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

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.

Related

How to use JavaScript files in angular

I am trying to call the javascript function into the angular here is the plugin I am using "npm I global payments-3ds" of which I copied javascript files from node_modules and tried to call in my component
Below is the example :
import {
Component,
OnInit
} from '#angular/core';
import {
handleInitiateAuthentication
} from './globalpayments-3ds/types/index';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
name = 'Angular';
ngOnInit(): void {
const status: any = "CHALLENGE_REQUIRED"
const resp = {
challenge: {
encodedChallengeRequest: "abcd",
requestUrl: "url,
},
challengeMandated: "MANDATED",
dsTransactionId: "44444",
id: "444444",
status: status,
};
const windowSize: any = "WINDOWED_600X400";
const displayMode: any = "lightbox";
const challengeWindow = {
windowSize: windowSize,
displayMode: displayMode,
};
handleInitiateAuthentication(resp, challengeWindow)
}
}
I am trying to call the handleInitiateAuthentication() which is giving me the below error
Here is the file structure
from index.d.ts i am calling the handleInitiateAuthentication() function
Here is Stackblitz code for the same
https://stackblitz.com/edit/angular-vodezz?file=src%2Fapp%2Fapp.component.ts
Please help I never used the js function in angular I tried to add in assets not worked
I have tried to create an angular library and add the js files in it and update the package, by converting the file to .tgz but nothing working it showing always the same error,
Why am I doing is I have to update one of the files from node_modules, basically I wanna change files from node modules which is why i copied those files locally
this is also giving error
You have to import directly js file.
// #ts-ignore
import { handleInitiateAuthentication } from './globalpayments-3ds/globalpayments-3ds.esm.js';
For error about module, it's because you have to define type of your module in TypeScript. You can directly use // #ts-ignore.
See this stackblitz : https://stackblitz.com/edit/angular-xz4kmp?file=src%2Fapp%2Fapp.component.ts
You don't need to import a library like that. First of all install the library to your project:
npm i globalpayments-3ds --save
then in your ts file:
import { handleInitiateAuthentication } from 'globalpayments-3ds';
see this stackblitz
The recommended way to make your own modified versions from open source libraries is to fork them and build your own versions.
Also note that you must take into account the license of that NPM package which in the case of https://github.com/globalpayments/globalpayments-js is GPL-v2, so if you will use it for commercial purposes you must follow the agreement. Check this branch: GNU General Public License (v2): can a company use the licensed software for free?.
Taking a look to your Stackblitz code, you may notice that there are several JS versions of the same module in src/app/global-payments-3ds/ folder:
globalpayments-3ds.js (CommonJS, used in Node environments).
globalpayments-3ds.min.js (CommonJS minified).
globalpayments-3ds.js.map (CommonJS minified map file to reference during debugging).
globalpayments-3ds.esm.js (ESM, ECMA Standard Module).
...
To use an external JS Module in an Angular App, as it is JavaScript and not TypeScript, you must tell TypeScript Compiler that you want to allow JS modules by enabling allowJS: true in tsconfig.ts file at the root of the project.
After that you should be be able to import the ESM version (globalpayments-3ds.esm.js) in your Angular App, or if you want to use the CommonJS version, you can also enable esModuleInterop: true in tsconfig.ts to allow importing CommonJS/AMD/UMD JS modules in your project, like globalpayments-3ds.js.

Cannot use newly installed plugins (node modules) in Nuxt pages/components

First off, I'm a beginner with NuxtJS and front-end development in general, so it might be that I'm missing something - though I do believe I went through all the options before posting here. Apologies in advance if that is not the case.
I've been having trouble using installed modules that I've registered as plugins. For example, take mapbox-sdk.
After installing it with npm install #mapbox/mapbox-sdk, which correctly creates #mapbox/mapbox-sdk in node_modules, I register it in nuxt.config.js:
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Of course, I also create the mapbox-sdk.js file in plugins/, containing:
import "#mapbox/mapbox-sdk";
Then, in a page (say, myMap.vue), when I try:
var mapboxClient = mapboxSdk({ accessToken: MY_ACCESS_TOKEN });
which is the basic usage example in the documentation, I get:
mapboxSdk is not defined
in the console. This behavior extends to every single module I installed today, but is not the case for modules I had previously installed.
The reason why you're getting the error mapboxSdk is not defined is because there are a few issues with the way you've set up this plugin.
Docs here https://nuxtjs.org/docs/2.x/directory-structure/plugins/, they have some useful diagrams.
There are a couple of ways you can use this package.
Plugin
// ~/plugins/mapbox-sdk.js
import mapboxSdk from '#mapbox/mapbox-sdk'
export default (_ctx, inject) => {
// Exposing the mapboxSdk to your Nuxt app as $mapBox.
inject('mapBox', mapboxSdk)
}
Then in nuxt.config.js, same as you've already done.
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Then in your component myMap.vue
var mapboxClient = this.$mapBox({ accessToken: MY_ACCESS_TOKEN });
Directly in the component:
If you don't wish to use a plugin, the way that #kissu mentioned above https://stackoverflow.com/a/67421094/12205549 will also work.
Try adding this after the import to let Vue know that this method exists (in the same .vue file) at first
<script>
import mapboxSdk from '#mapbox/mapbox-sdk'
export default {
methods: {
mapboxSdk,
},
mounted() {
console.log('mapbox function >>', mapboxSdk)
},
}
</script>
Do you have it working in a .vue component at first ?

How to include external JavaScript libraries in Angular 2?

I am trying to include an external JS library in my Angular 2 app and trying to make all the methods in that JS file as a service in Angular 2 app.
For eg: lets say my JS file contains.
var hello = {
helloworld : function(){
console.log('helloworld');
},
gmorning : function(){
console.log('good morning');
}
}
So I am trying to use this JS file and reuse all the methods in this object and add it to a service, so that my service has public methods, which in turn calls this JS methods. I am trying to reuse the code, without reimplementing all the methods in my typescript based Angular 2 app. I am dependent on an external library, which I cant modify.
Please help, thank you in advance.
With ES6, you could export your variable:
export var hello = {
(...)
};
and import it like this into another module:
import {hello} from './hello-module';
assuming that the first module is located into the hello-module.js file and in the same folder than the second one. It's not necessary to have them in the same folder (you can do something like that: import {hello} from '../folder/hello-module';). What is important is that the folder is correctly handled by SystemJS (for example with the configuration in the packages block).
When using external libs which are loaded into the browser externally (e.g. by the index.html) you just need to say your services/component that it is defined via "declare" and then just use it. For example I recently used socket.io in my angular2 component:
import { Component, Input, Observable, AfterContentInit } from angular2/angular2';
import { Http } from 'angular2/http';
//needed to use socket.io! io is globally known by the browser!
declare var io:any;
#Component({
selector: 'my-weather-cmp',
template: `...`
})
export class WeatherComp implements AfterContentInit{
//the socket.io connection
public weather:any;
//the temperature stream as Observable
public temperature:Observable<number>;
//#Input() isn't set yet
constructor(public http: Http) {
const BASE_URL = 'ws://'+location.hostname+':'+location.port;
this.weather = io(BASE_URL+'/weather');
//log any messages from the message event of socket.io
this.weather.on('message', (data:any) =>{
console.log(data);
});
}
//#Input() is set now!
ngAfterContentInit():void {
//add Observable
this.temperature = Observable.fromEvent(this.weather, this.city);
}
}

Angular2 dependency not injecting correct path

I am getting the following errors in my browser console, from trying to use localStorage with Angular2. It seems that the path it is generating isn't referring to the node_modules, but rather assuming that there is a localstorage.js in my site root (which there isn't). I am just referring to it normally (see my user.service below), so how do I get around this? All my other dependencies are working fine.
Error loading http://localhost:3000/localStorage.js as "localStorage" from http://localhost:3000/client/dev/user/services/user.service.js
import { Injectable } from 'angular2/core';
import { Headers } from 'angular2/http';
import { loalStorage } from 'localStorage';
#Injectable()
export class UserService {
private loggedIn = false;
constructor(private http: Http) {
this.loggedIn = !!localStorage.getItem('auth_token');
}
}
NB: I am fairly sure there isn't an actual problem with the localStorage installation, as if I run npm list localStorage, then it tells me I have localStorage#1.0.3 instaled.
If you want to use localStorage from an import, you need to configure it within SystemJS as described below:
System.config({
map: {
localStorage: 'node_modules/localStorage/lib/localStorage.js'
},
(...)
});
This way, you will be able to use the following import:
import loalStorage from 'localStorage';
See this question for more details since it's similar to the way to configure Lodash:
Lodash in angular2, declare var_:any not working

How to import Parse JS sdk into ionic 2 beta

am trying to import JS sdk into ionic 2 app, but i keep getting parse is undefined
In ionic 1.x ,parse js sdk is loaded via
<script ..parse.js </script>
and exposed as a global var, how do import in ionic 2 ,am using the npm module ,and tried
import * as parse from 'parse'
Do npm install parse --save in your project directory
Then import parse using
import { Parse } from 'parse';
It is better to create an parse provider.
You can use this starter template as a guide. It is a simple GameScores application in ionic to get you started.
https://github.com/Reinsys/Ionic-Parse
It shows how to create and read data from parse server. I also includes paging with ion-infinite-scroll scrolling.
After searching for a solution I came up with my own.
After installing the package and the typings, I opened the index.js of the node-module ionic-gulp-scripts-copy and added 'node_modules/parse/dist/parse.min.js' to the defaultSrc array.
Then, in my index.html, I included the script above the cordova.js.
Now I just need to declare var Parse: any; in every Component I want to use the SDK in.
For example, in my app.ts:
import {Component} from '#angular/core';
import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
import{LoginPage} from './pages/login/login';
declare var Parse: any;
#Component({
template: '<ion-nav [root]="rootPage"></ion-nav>',
})
export class MyApp {
private rootPage: any;
private parse;
constructor(private platform: Platform) {
//this.rootPage = TabsPage;
this.rootPage = LoginPage;
platform.ready().then(() => {
console.log("Platform ready!");
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
Parse.initialize('myStartUp', 'someKey');
Parse.serverURL = 'http://localhost:1337/parse';
});
}
}
ionicBootstrap(MyApp);
I do not think this is the way it should be used, but in the end I can use the SDK pretty easy and without much lines of implementation code.

Categories

Resources