What is the best way to connect the browser and Node.js?
Is there a tutorial for that somewhere?
In a simple example I would like to have a button visible on the browser end and when clicked do something on the Node.js side, like save a file to disk using parameters from the browser side.
If I'm not mistaken I have to connect /packages/mypackagename/browser and /packages/mypackagename/node using the common folder /packages/mypackagename/common. Is that right? Also, the calls made should be using JSON-RPC. Do I have to take care of that or is this done automatically by Theia?
please have a look at http://www.theia-ide.org/doc/Json_Rpc.html
it can be a bit outdated in that case you can look at the corresponding code in the repo.
Ok, the link is indeed very outdated. Still, it helped in finding the right classes and methods.
Solved it. Description follows.
Create file packages/mypackage/src/common/mypackage-service.ts:
export const MyPackageService = Symbol("MyPackageService");
export interface MyPackageService {
getMessage(message: string): Promise<string>;
}
Create file packages/mypackage/src/node/mypackage-backend-module.ts:
import { ContainerModule } from "inversify";
import { ConnectionHandler, JsonRpcConnectionHandler } from "@theia/core/lib/common";
import { myPackageServicePath } from "../common/my-package-service";
import { MyPackageService } from "../common/my-package-service";
import { MyPackageServiceImpl } from "./my-package-service-impl";
export default new ContainerModule(bind => {
bind(MyPackageService).to(MyPackageServiceImpl).inSingletonScope();
bind(ConnectionHandler).toDynamicValue(ctx =>
new JsonRpcConnectionHandler(myPackageServicePath, () =>
ctx.container.get(MyPackageService)
)
).inSingletonScope();
});
Create file packages/mypackage/src/node/mypackage-frontend-module.ts:
import { ContainerModule } from 'inversify';
import { WidgetFactory, bindViewContribution, WebSocketConnectionProvider } from '@theia/core/lib/browser';
import { MyPackageContribution, MY_PACKAGE_WIDGET_FACTORY_ID } from './my-package-contribution';
import { MyPackageWidget } from './my-package-widget';
import { myPackageServicePath } from '../common/my-package-service';
import { MyPackageService } from '../common/my-package-service';
import '../../src/browser/style/index.css';
export default new ContainerModule(bind => {
bindViewContribution(bind, MyPackageContribution);
bind(MyPackageWidget).toSelf();
bind(WidgetFactory).toDynamicValue(ctx => ({
id: MY_PACKAGE_WIDGET_FACTORY_ID,
createWidget: () => ctx.container.get(MyPackageWidget)
}));
bind(MyPackageService).toDynamicValue(ctx => {
const provider = ctx.container.get(WebSocketConnectionProvider);
return provider.createProxy<MyPackageService>(myPackageServicePath);
}).inSingletonScope();
});
Create file packages/mypackage/src/node/mypackage-service-impl.ts:
import { injectable } from "inversify";
import { MyPackageService } from "../common/my-package-service";
@injectable()
export class MyPackageServiceImpl implements MyPackageService {
getMessage(message: string): Promise<string> {
return new Promise((resolve, reject) => {
resolve("Message from Node server side. Received message: " + message);
});
}
}
Create file packages/mypackage/src/browser/mypackage-widget.tsx:
import { injectable, inject } from "inversify";
import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import * as React from 'react';
import { MyPackageService } from '../common/my-package-service';
@injectable()
export class MyPackageWidget extends ReactWidget {
@inject(MyPackageService)
protected readonly myPackageService: MyPackageService;
private onButtonShowClicked() {
console.log("onButtonShowClicked() clicked!");
const messagePromise: Promise<string> = this.myPackageService.getMessage("This message is from the browser side!");
messagePromise.then(res => {
console.log("This is the response from the Promise: " + res);
});
messagePromise.catch(err => {
console.log("This is an error message from the button: " + err);
});
}
private createButton(): React.ReactNode {
const btnLabel: string = "Show on right side";
const className: string = "theia-button myPackageButton";
return <div className={className}
onClick={event => {
this.onButtonShowClicked();
}}>
{btnLabel}
</div >;
}
protected renderButton(): React.ReactNode {
return <div id='myPackageSolutionContainer' className='flexcontainer'>
<div className='myPackageButtonContainer flexcontainer'>{this.createButton()}</div>
</div>;
}
}
@jeanlucburot you are welcome to update docs, the corresponding file is here: https://github.com/theia-ide/theia-website/blob/master/doc/Json_Rpc.md
Most helpful comment
Ok, the link is indeed very outdated. Still, it helped in finding the right classes and methods.
Solved it. Description follows.
Create file
packages/mypackage/src/common/mypackage-service.ts:Create file
packages/mypackage/src/node/mypackage-backend-module.ts:Create file
packages/mypackage/src/node/mypackage-frontend-module.ts:Create file
packages/mypackage/src/node/mypackage-service-impl.ts:Create file
packages/mypackage/src/browser/mypackage-widget.tsx: