I am using websocket from node, and I am trying to implement a broadcast method that will send a message to all clients except for the client that sent the message.
To do this, I need to know the
export class WebSocketRoom {
private _clients: WebSocketConnection[] = []
public get clients(): WebSocketConnection[] { return this._clients }
public broadcast(event: string, message: any) {
this.clients.forEach(client => {
client.emit(event, message)
})
return this
}
}
To access the method broadcast, I do this:
class Test extends Module {
public constructor(client: WebSocketConnection) {
super(client)
let room = new WebSocketRoom('hi')
room.broadcast('cool', 'sweet')
}
}
I tried doing a console log of WebSocketRoom.caller but that gives me this error:
TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context.
Is there a way that I can access the object that called broadcast from within the broadcast method without passing it as a parameter?
this.clients.forEach(client => {
if(client == sender) return
client.emit(event, message)
})
Side note
In C#, it is done like so:
public static void MyMethod(this GameObject obj, int var1, int var2) {
obj.add(var1, var2)
}
MyMethod(1,2)
As you can see obj is not passed in when it is called.
Related
I'm trying to invoke a Blazor method in JavaScript inside of an OnSuccess callback for the Plaid API.
Here's the JavaScript that's being run:
async function InitializePlaidLink(objRef, linkToken) {
//console.log("linkToken:" + linkToken);
const handler = Plaid.create({
token: linkToken,
onSuccess: (public_token, metadata) => {
//console.log("public_token: ");
//console.log(public_token);
objRef.invokeMethodAsync('OnPlaidLinkSuccess', public_token);
//console.log("After Invoke Method Async")
},
onLoad: () => {},
onExit: (err, metadata) => {},
onEvent: (eventName, metadata) => {},
//required for OAuth; if not using OAuth, set to null or omit:
//receivedRedirectUri: window.location.href,
});
handler.open();
}
Here's the Blazor code being used:
private string LinkToken { get; set; } = string.Empty;
private string PublicToken { get; set; } = string.Empty;
private async Task InitializePlaid()
{
this.LinkToken = await this.apiService.GetPlaidLinkToken();
var dotNetReference = DotNetObjectReference.Create(this);
await this.jsRuntime.InvokeVoidAsync
(
"InitializePlaidLink",
dotNetReference,
this.LinkToken
);
}
[JSInvokable]
public void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
The Blazor method InitializePlaid is being called to invoke the JS method InitializePlaidLink. Then, on success, the Blazor method OnPlaidLink Success should be called.
I used log statements to confirm that there is a public_token and the JS after the objRef.invokeMethodAsync() is being reached. Also I was able to invoke a Blazor method in a similar way with a different JS method, just not a method with the Plaid API and the onSuccess callback.
The problem is that the OnPlaidLinkSuccess method must be static as follows:
[JSInvokable]
public static void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
If you have to define a non-static function, then it will be a bit more complicated.
In this case, it is necessary to send a reference of the current component to the JavaScript method. What follows is a solution that you have to adapt yourself with your own codes.
For this reason, I first create this reference using the DotNetObjectReference.Create method and then send it to the JavaScript code using the jSRuntime.InvokeVoidAsync method. In the example below, JsSample is the name of the current component.
Also defined here, onclick refers to a method inside this component.
This reference should also be disposed at the end of the component's work. That's why you see #IDisposable implements.
#page "/js-sample"
#implements IDisposable
#inject IJSRuntime jSRuntime
<button class="btn btn-primary" #onclick="CallInstanceMethod">Invoke Instance Method</button>
#code
{
private DotNetObjectReference<JsSample> objectReference;
[JSInvokable]
public string GetAddress()
{
return "123 Main Street";
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
objectReference = DotNetObjectReference.Create(this);
}
}
private async Task CallInstanceMethod()
{
await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference);
}
public void Dispose()
{
objectReference?.Dispose();
}
}
Now the javascript code that uses this receiving slot will be as follows. In these codes, addressProvider is the received objectReference that can be used to call the component's non-static GetAddress method:
window.JsFunctionHelper = {
invokeDotnetInstanceFunction: function (addressProvider) {
addressProvider.invokeMethodAsync("GetAddress").then((data) => {
console.log(data);
});
}
};
OnPlaidLinkSuccess was being called correctly. The property this.PublicToken was being correctly updated, but the DOM was not being updated for the user to see. Calling this.StateHasChanged() fixed this.
Using rxjs websocket, I'm facing an issue when sending data.
I always wrap the message I send into a Request object that looks like this :
export class Request {
public constructor() { }
private t: string = null;
public get type(): string {return this.t;}
public set type(type: string) {this.t = type;}
private i: string = null;
public get issuer(): string {return this.i;}
public set issuer(issuer: string) {this.i = issuer;}
private n: string = null;
public get name(): string {return this.n;}
public set name(name: string) {this.n = name;}
private c: MyCandidateClass = null;
public get candidate(): MyCandidateClass {return this.c;}
public set candidate(candidate: MyCandidateClass) {this.c = candidate;}
private cis: { [id: string]: string; } = {};
public get credentialsInfo(): { [id: string]: string; } {return this.cis;}
public set credentialsInfo(credentialsInfo: { [id: string]: string; }) {this.cis = credentialsInfo;}
}
The object is built, and my cis property is a key/value map which value can be for example {_PinCode: "1234"}.
Everything is fine in my request object, but here is what's actually sent to the websocket server :
{
cis: {},
c: candidate // <- an object of class MyCandidateClass as expected
i: "some_username",
n: "some_name",
t: "some_type"
}
I am losing the cis object.
While investigating, I noticed that if I create a class CredentialsInfo with the correct properties inside and change my cis property from Request like the following :
private cis: CredentialsInfo = null;
public get credentialsInfo(): CredentialsInfo {return this.cis;}
public set credentialsInfo(credentialsInfo: CredentialsInfo) {this.cis = credentialsInfo;}
With these modifications, it works just fine.
However, I want my cis to be key/value map as defined in my initial Request, not a defined type of my own since I can't always know what will the map keys be.
I'm guessing this has to deal with default serialization/deserialization ?
How can I achieve this, and make the initial Request work ?
Note : I'm using Angular and rxjs, here are some relevant parts of my websocket service :
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
// ...
ws: WebSocketSubject<any> = null;
// ...
connect(): void {
// ...
this.ws = webSocket(this.websocketUrl);
this.ws.subscribe(
(response) => this.onReceive(ResponseInfo.map(response)),
(err) => this.onError(err),
() => this.onClose('Complete (connection closed)')
);
// ...
}
send(request: RequestInfo): void {
// ...
this.ws.next(request);
}
// ...
in my Angular App i make a simple call to a node.js server. the HttpClient "get"
function returns the right answer. This answer I want to store in a variable of my component "interfaces". But in the "subscribe" function of the get request my "this" pointer doesn't point to my component. Instead it tells me that it is of type "SafeSubscriber". Any call to my member "interfaces" lead to the following error:
TypeError: Cannot read property 'interfaces' of undefined
export class SettingsComponent implements OnInit {
public interfaces : string[];
constructor(private http: HttpClient) {
this.interfaces = [];
this.interfaces.push("huhu");
}
ngOnInit() : void {
this.http.get('http://localhost:3000/settings/interfaces').subscribe((data) => {
// Read the result field from the JSON response.
console.log(data);
this.interfaces.push("xxx");
Object.keys(data).forEach(function(k) {
console.log(k);
this.interfaces.push("xxx");
});
}),
err => {
console.log("error " + err);
};
}
}
As you can see I also tried to enter some values manually into the array just to make sure, that not the server response is causing the problem.
Any help is appreciated.
I used this code as a blueprint which is from:
https://angular.io/guide/http
#Component(...)
export class MyComponent implements OnInit {
results: string[];
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
ngOnInit(): void {
// Make the HTTP request:
this.http.get('/api/items').subscribe(data => {
// Read the result field from the JSON response.
this.results = data['results'];
});
}
}
You're losing reference to the correct this in this statement:
Object.keys(data).forEach(function(k) {..})
Inside the function block code this refers to the calling context , which is the subscribe method itself, that's why interfaces is undefined, since it's not a property of the subscribe method.
You can change the function for a lambda en it should be fine:
Object.keys(data).forEach((k) => {..})
I am attempting to do several things on connecting to my MQTT broker, I have created an mqtt provider in my ionic 2, angular 2 application, the provider is given below:
import { Component } from '#angular/core';
import { NavController, ViewController } from 'ionic-angular';
import { Observable } from 'rxjs/Observable';
import { Paho } from 'ng2-mqtt/mqttws31';
#Component({
selector: 'page-greywater',
templateUrl: 'greywater.html'
})
export class MQTT_Greenchain {
private _client: Paho.MQTT.Client;
private options = {
userName: 'rdjghvoh',
password: 'w7Ex0VTqZViw',
timeout: 30,
useSSL:true,
onSuccess:this.onConnected,
};
private topic: string;
public displayedMessage: string;
public mes: Paho.MQTT.Message;
public constructor() {
this._client = new Paho.MQTT.Client(
"m20.cloudmqtt.com",
Number(30775),
"",
"peter"
);
this._client.onConnectionLost = (responseObject: {errorCode: Number, errorMessage: string}) => {
console.log('poes');
console.log(responseObject.errorMessage);
};
this._client.onMessageArrived = (message: Paho.MQTT.Message) => {
this.onMessageArr(message);
console.log('Message arrived.');
};
this.topic = "haha";
this.displayedMessage = "what I was";
}
connectMe() {
console.log("MQTT OPTIONS: " + this.options);
this._client.connect(this.options);
}
private onConnected(): void {
console.log('Connected to broker.');
this._client.subscribe(this.topic);
this.mes = new Paho.MQTT.Message("-1"); // -1 => Notify
this.mes.destinationName = this.topic;
this._client.send(this.mes);
}
private onMessageArr(message: Paho.MQTT.Message){
this.displayedMessage = message.payloadString;
}
}
I have been able to call the following in angular 1 without trouble, and I was able to get everything MQTT-related, working. The function in angular 1 is as follows:
function onConnect() {
sharedUtils.hideLoading();
console.log("onConnect, CURRENT TOPIC: " + mqttData);
client.subscribe(mqttData.currentTopic);
}
In the above, mqttData.currentTopic is merely a string.
The function accepts 1 argument, even though it can accept 2 (an options object).
In angular 2, typescript gives me an error:
Supplied parameters do not match any signature of call target
Why is it not allowing me to call the function with one argument as in angular 1? If I give it {} as a second argument:
this._client.subscribe(this.topic, {});
I am given the error that:
AMQJS0005E Internal error. Error Message: Cannot read property 'subscribe' of undefined, Stack trace: TypeError: Cannot read property 'subscribe' of undefined
This is the error received in the response object parameter, passed to the onConnectionLost callback function.
I am quite certain that my 'this._client' is not undefined since the message 'Connected to broker.' appears in the console, indicating that onConnected, the onSuccess property callback of the connect method was obviously called?
What am I not getting here?
Try this and reply if it is working fine
this.client.subscribe(this.topic, '');
I have a property that is a custom class array. The array is populated with a call to a service which calls a web service for data. I have subscribed to the observable and I'm using the complete event to fire off a method which loads a graph.
The data the graph uses should come from the array which is populated during the subscribe, but when I attempt to do so in the method I am getting an undefined error on my component property. Why is this the case, I though that the component property should be accessible to methods in the same class.
export class MetricsComponent implements OnInit{
errorMessage: string;
metric: MetricData[] = [];
//constructor is used for dependency injection
constructor(public _metricsService: MetricsService){}
ngOnInit(): void {
console.log('talking to service...');
this._metricsService.getData()
.subscribe(
data => this.metric = data,
error => this.errorMessage = <any>error,
this.LoadChart
);
}
LoadChart(): void {
console.log(this.metric); // <== this returns as undefined
}
Use arrow functions to retain the scope of this.
ngOnInit(): void {
console.log('talking to service...');
this._metricsService.getData()
.subscribe(
data => this.metric = data,
error => this.errorMessage = <any>error,
() => this.LoadChart()
);
}
do it like this for more info read about lexical this and arrow functions one more thing if you are using chrome developer tools it will still show null there is some bug with chrome developer tools.
LoadChart = () => void {
console.log(this.metric); //
}