-
Notifications
You must be signed in to change notification settings - Fork 484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
socket.io with ssr #846
Comments
This is sort of a tricky question. Sorry I can't provide specifics on how to do this since i've never used socket.io |
To follow-up on this comment, @matheusdavidson socket.io should work assuming the client you're using is Universal compatible (i.e. doesn't try to reference |
@CaerusKaru This here |
After throwing together a basic WebSocket example, which socket.io will use if available, it looks like WebSockets are tracked by Zones. Here's the example on StackBlitz, running the WebSocket echo test and then closing the socket. I also updated the appRef subscribe to match what |
@CaerusKaru WebSockets are tracked be Zones, you can see that by putting However as I mentioned above, WebSockets are long lived, they stay open untill they are closed. In your example the WebSocket is never closed, that's why you don't see the stable event fire and why Universal wont work with socket.io or firebase without special handling for the websockets |
@Toxicable on line 25 the socket is closed once the message is received. I saw that code too and this leads me to believe that closing the socket is not sufficient to restoring stability. After looking into this a little more, this is apparently a documented and long-lived issue. However, I think the one advantage Universal has in this case is that I think it's almost guaranteed this won't hold up rendering, since the appRef call is almost immediately invoked after the components render (ie the initial view is stable, and then immediately not stable). But if this turns out not to be the case after testing, the recommendation would be (1) move socket.io into the browser build only or (2) run all socket.io-related code outside the Angular Zone, which may result in a performance hit/change-detection issues. |
@CaerusKaru Whops, sorry missed that line, in that case I don't know whats going on in that example. While both of those could be a solution, I personally think comming up with another way / api for handling when to render on the server should be investigated. |
@Toxicable I think that's a great idea! Some consistency would be a relief. Do you want to open a new issue to track the feature? I have some preliminary ideas that should probably go there. |
@CaerusKaru sure, but open it up on the angular repo since that's where this stems from |
Tracking here: angular/angular#20520 |
I did try but ended up using socket only in the browser build because as you guys pointed out angular doesn't finish rendering the page, probably its waiting for a response from the socket event. Not sure if closing the connection all the time i receive a response would be very good because we could have other code waiting for socket events. Maybe an option would be using socket.io with promises, i will try this when i have some time. |
Hi - You can make a task trackable by Zone.js by doing something similar to here Would that satisfy your use case? |
any update on this? Can you write some instruction about |
It is very useful an official example with server + client using socket.io.
|
A workaround in my case was to force close the socket after a certain time. It increases the render time but it works:
I can't wait to see a more elegant solution :) |
how about rxjs support? it doesn't work when using rxjs for websocket communication (see: #1042) |
@Ks89 , We are experiencing the same error with Express + Universal. Were you able to find a solution ?
|
Check this. an answer on stackoverflow. You need to detect if it is browser of or server side. |
I found a solution that works for me. I'm using feathers.js and don't handle the socket directy. The second step is therefore a guess how it works with pure socket.io. There is somewhere a similar issue where I found parts of the solution, but I could not find it anymore - so thanks original author :) First, the socket creation needs to run outside of angular: this.ngZone.runOutsideAngular(() => {
this.socket = io(this.BASE_URL)
}) Second, the this.socket.on('message', (data) => {
ngZone.run(() => {
doStuff(data)
})
}) Now it should work, but you just have created a memory leak because the socket does not get closed. You need to add a ngOnDestroy method to your service that created the socket: ngOnDestroy(): void {
this.socket.close()
} For completeness here is my code that works with feathers.js: data.service.ts: import { Services } from './../../../../shared/types/services'
import { Injectable, NgZone, OnDestroy } from '@angular/core'
import { environment } from '../../environments/environment'
import feathers, { Application } from '@feathersjs/feathers'
import socketio from '@feathersjs/socketio-client'
import * as rx from 'feathers-reactive'
import * as io from 'socket.io-client'
import { ngFeathersWebsocketSupport } from '../../lib/ng-feathers-websocket-support'
@Injectable()
export class DataService implements OnDestroy {
constructor(
public ngZone: NgZone
) {
this.init()
}
BASE_URL = environment.dataUrl
app = feathers() as Application<Services>
socket: SocketIOClient.Socket = null
protected init() {
this.ngZone.runOutsideAngular(() => {
this.socket = io(this.BASE_URL)
this.app
.configure(socketio(this.socket, {
timeout: 10 * 1000
}))
.configure(rx({ idField: '_id' }))
.configure(ngFeathersWebsocketSupport({ ngZone: this.ngZone }))
})
}
ngOnDestroy(): void {
this.socket.close()
}
} ng-feathers-websocket-support.ts: export function ngFeathersWebsocketSupport(options: { ngZone: any }) {
const mixin = function (service) {
const originalOn = service.on.bind(service)
const serviceMixin = {
on(event, cb) {
return originalOn(event, (...args) => {
options.ngZone.run(() => {
cb(...args)
})
})
}
}
// get the extended service object
service.mixin(serviceMixin)
}
return function (app) {
app.mixins.push(mixin)
}
} Hope this helps anyone :) |
Simply we can check for
|
@sean-nicholas
in
|
I tried these solutions, but what about the acknowledgements (callback) in the emit method? Because in this case we are not able to wait for that. Actually I implemented a queue system, and wait for the response form the server, and if the queue is get emptied I close the connection. |
@Twois
in
you can send any keyword (replace 'message') you like to listen to. remember using |
I speak about this case:
In this case the angular won't wait for the response of the server. |
@ulrichdohou Muchas gracias! la solución que presentaste planteada aqui: https://stackoverflow.com/a/41721980/6261137 me ayudo a resolver el problema... me salvaste! |
Esta solución es sencilla y efectiva |
Any update or help with this? |
I’m just a beginner trying to understand the |
@Chef211 wth? |
@Twois or @hsuanyi-chou @sanjaynishad @sean-nicholas Would any of you mind helping me out with this. I am a little lost |
Hey @xtianus79 Whats your problem? Can you give some context? Have you tried the solutions above? What's working / what's not? |
@sean-nicholas so I am in the middle of trying what you are speaking of and this other solution that is working per se and seems like a hack. Let me show you what I have: import { isPlatformBrowser } from '@angular/common';
import { Injectable, NgZone, Inject, PLATFORM_ID } from "@angular/core";
import { Observable } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class SseService {
private isBrowser: boolean = isPlatformBrowser(this.platformId);
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
private _zone: NgZone
) {
if (this.isBrowser) {
console.log('this happened here');
}
}
getServerSentEvent(url: string): Observable<EventSource> {
return Observable.create(observer => {
const eventSource: EventSource = this.getEventSource(url);
eventSource.onmessage = event => {
this._zone.run(() => {
observer.next(event);
});
};
eventSource.onerror = error => {
this._zone.run(() => {
observer.error(error);
});
};
});
}
private getEventSource(url: string): EventSource {
if (this.isBrowser) {
console.log('this happened here 2');
return new EventSource(url);
}
}
} this is the implementation. ngOnInit() {
this.sseService
.getServerSentEvent('http://localhost:4201/user/stream')
.subscribe(data => console.log('data here >>>>> ', data)); I am trying to implement If I don't use the browser detection hack then I get this error. FYI, I don't know why I am calling it a hack but it something about it smells.
|
@xtianus79 Okay, so I'm not quite sure if you need the runOutsideAngular if you are using server sent events. But if you do, I would think that this might work (just a guess, it's long ago that I implemented my solution and I don't know SSE) getServerSentEvent(url: string): Observable<EventSource> {
return Observable.create(observer => {
this._zone.runOutsideAngular(() => {
const eventSource: EventSource = this.getEventSource(url);
eventSource.onmessage = event => {
this._zone.run(() => {
observer.next(event);
});
};
eventSource.onerror = error => {
this._zone.run(() => {
observer.error(error);
});
};
});
});
} The But I think you should return an EventSource mock from |
Hi, It would be very helpful if someone could suggest a possible solution |
work fine for me |
Simply call WebSocket only in the browser mood. Follow the answer. Here is my socket Service
|
It looks like this issue is resolved. Please create a new issue if the problem is still a need. Thanks. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Does universal work with socket.io? and how to?
The text was updated successfully, but these errors were encountered: