From the help of "paolo mania" comment and "jfriend00" answer I have solved the issue for Angular2 framework and Socket.IO library.
So first i have created a service to keep the Socket.IO connection open throughout the component creation and destruction cycles.
import { Injectable } from '@angular/core';
declare var io: any;
@Injectable()
export class SocketService {
// We create socket object for socket.io lib and events object
// to store all events that are bound from specific component
socket;
events;
// When the service is injected it checks if socket.io and our
// events objects are initialised
constructor() {
if(!this.socket) {
this.socket = io(environment.apiURL);
this.events = {};
}
}
// Then we create a middleware function to intercept the socket.on()
// function and we request one more parameter which is the component name.
on(component, event, callback) {
// We check if component key already exists in our events object.
if(this.events[component]) {
// We check if the wanted event is already bound in selected component
// (basically we check if its added to events[component] array)
if(this.events[component].indexOf(event) < 1) {
// If it's not added we bind the event to socket and add it to events[component] array
this.events[component].push(event);
this.socket.on(event, callback);
}
// If the component key does not exist we initialise event[component] as an array
// and we add our event to the array as well as we bind the socket event
} else {
this.events[component] = [];
this.events[component].push(event);
this.socket.on(event, callback);
}
}
// We also create a middleware function to intercept the socket.emit()
// function and just forward data to socket.emit
emit(event, data = {}) {
this.socket.emit(event, data);
}
// And last we add unsubscribe function so we can unbind all listeners from
// single component (mostly used in onNgDestroy() when we don't want that bound events
// are persistent between more than one component)
unsubscribe(component) {
// We check if component key exists in events object
if(this.events[component]) {
// We iterate through array and we remove listeners for every
// event that was bound to selected component
this.events[component].forEach((event) => {
this.socket.off(event);
});
// And in the end we remove component key from events
delete this.events[component];
}
}
}
So we have service on which we can bind as many events we want, they are sorted by components so we know which component has which bindings. If we want to bind same event in same component we just ignore it and when we want we can unbind everything in component so some funny actions are not triggered when our component is destroyed or not active.
On component side we use the service like all other:
import { ApiService } from '../core/socket.service';
@Component({
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
// We inject our socket service in constructor
constructor(private socket: SocketService) {
// We bind wanted events with component name and handle them as we would normally in socket.io
this.socket.on(this.constructor.name, 'event1', (data) => {
console.log(data);
});
this.socket.on(this.constructor.name, 'event2', (data) => {
console.log(data);
});
}
... other functions ...
// If we don't want to keep our events persistent for another component we just unsubscribe.
// When we will return to this component it will re-bind the events stated in component constructor.
// But if we choose to use persistent mode it will not duplicate the events.
ngOnDestroy() {
this.socket.unsubscribe(this.constructor.name);
}
}