Is an event subscribed to an observable still fired on refresh? - javascript

I have an issue which I have been figuring out for a while now.
I have this piece of code:
this.service.process(this.Object.id, this.Object.userId,this.pageNr)
.subscribe((data) => {
this.processed.emit(this.pageNr);
});
Now when debugging I see that the process method is called, when I refresh in the middle of the process, I do not see the processed.emit().
So, is it possible that on refresh the process( which is one transaction) goes through, but subscribed "processed" does not happen?
If someone could give a link to more information that would be awesome, or maybe information about how to avoid this.

Try this to avoid multiple subscription:
subscription:Subscription
...
if(!!this.subscription){
// unsubsribe from old subscription
this.subscription.unsubscribe();
}
this.subscription = this.service.process(this.Object.id, this.Object.userId,this.pageNr)
.subscribe((data) => {
this.processed.emit(this.pageNr);
});

Related

rxjs subscribe in ngOnInit

Button show triggered on ngIf in template. After this code the button is not shown.
public navBackVisible = false
ngOnInit() {
this.router.events
//.pipe(filter(event => event instanceof NavigationEnd))
//.pipe(pairwise())
.subscribe(
() => this.navBackVisible = true); // here true
console.log(this.navBackVisible); // here false
}
template:
<button *ngIf = "navBackVisible"><icon>arrow_back</icon></button>
UPD:
this is all code
ChangeDetection.Default
router emmits events
function inside subscribe is called
function inside subscribe fires before ngIf
But this.http.get(url).subscribe(() => this.navBackVisible = true) works fine
everything works if I write the same logic in the constructor of some service and call it from this component
Pairwise (https://rxjs.dev/api/operators/pairwise), only emitting value(s) if and only if after at least one value has been emitted, and router events here is only emitting 1 value and then completed.
You can not use pairwise() and filter(event => event instanceof NavigationEnd) at the same time. Choose one or the other because pairwise emits previous and current value in pairs. If you filter out the last router event (which is NaviationEnd event) your pairwise never emits and you never fall into function to turn this.navBackVisible = true. At least, that's my understanding of this (if I am mistaken please correct me).
Use:
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe(() => {
console.log('i am here');
this.navBackVisible = true;
});
As a result, ngOnInit called the getter variable from the service-class. And in the service in the constructor, I used the same logic. Everything works. A little later I changed the variable to sessionStorage (it fits my logic). I still don't know what it was. If this happens to someone, then this is a good step towards finding a problem. This solution satisfied my problem, but the question is still open, I will follow the answers and answer your clarifications.

Socket.io returning multiple times for one event

Before this is flagged as a duplicate, I have checked the majority of other posts and solutions, to no avail. If anyone wants to double check, here they are:
1) Socket.io Multiple returns for a single event
2) Socket.io message event firing multiple times
3) socket.on event gets triggered multiple times
And many others.
Now to get to the meat of my question!
I have a socket in my client and my server code. Once my server-side socket emits a message, it is received by the client-side socket and prints out the message. With research, this probably ties into event listeners, but I can't find out how to apply it to my code. FYI the following client code is ran when a button is clicked.
Here are snippets of my client code
onButtonClick() {
socket.emit('message_to_server', 'ping');
socket.on('reply', (tmp) => {
console.log(tmp); // in this case, call it 'pong'
this.doSomethingWithMe(tmp);
});
}
doSomethingWithMe(msg) {
// do something with the information
}
The first time I click the button, I receive
> pong
The second time I click the button, I receive
> pong
> pong
It continues to grow exponentially.
I can post my server code if needed, but I'm 100% sure that it emits the information correctly.
Does anyone have any idea for fixing this issue? I can't figure out how the listeners play into this scenario, so I would appreciate any advice!
EDIT: I changed some of my code to the following:
import React ...
const socket = socketIOClient('http://localhost:3000')
socket.on('reply', (tmp) => {
console.log(tmp); // in this case, call it 'pong'
var inst = new drawMe();
inst.doSomethingWithMe(tmp);
});
class drawMe extends Component {
constructor(props) { this.state = { allData: ''}}
onButtonClick() {
...
}
doSomethingWithMe(data) {
this.setState({ allData: data });
}
I am now receiving an error saying that you cannot call setState on a component that is not yet mounted. I probably will not open another question for this issue, but I would appreciate any advice on it. If mods/anyone wants me to close, I have no problem doing so.
EDIT2: If anyone else has this issue, I made it work by moving the code to instantiate the socket and for the event inside my constructor.
You're attaching your event handler again every time the button is clicked. You have socket.on inside the click handler; that method attaches a new handler -- that is, each time it's run, it adds the specified function to the end of the list of functions to run when the event fires. So yes, every time you click the button, you're adding the function to the end of the list and it'll run once for every time it was added. (More accurately, since you're using an anonymous function there, it's creating a new function every time you click the button and adding it to the event handler list to be run when the event fires.)
You should only be attaching event handlers once, for instance just after creating the socket, not on every click.

SetTimeOut function inside ngOnInit of Angular component while using Angular Material DataTable

I was going through an Angular code in one of my existing projects and found below snippet.
We are using Angular material datatable to render the view on the page
export class Component implements OnInit,AfterViewInit{
private dataSource: MatTableDataSource<Product> = null;
#ViewChild(MatPaginator) paginator: MatPaginator;
columnsToDisplay = ['productId','productname'];
constructor(private _service : DataService) { }
ngOnInit() {
this._service.getProducts().subscribe(
((data : Product[]) => this.dataSource = new MatTableDataSource(data)),
() => console.log('THIS IS ERROR')
);
setTimeout(() => this.dataSource.paginator = this.paginator);
//this.dataSource.paginator = this.paginator;
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
}
My question is :
1) Since this.service.getData() returns an Observable and subscribe will be called asynchronously whenever the HttpResponse is available ,
does operation inside setTimeout function will be called ONLY AFTER the subscribe method is called ?
2) I have seen that ngAfterViewInit method also contains exactly the same code as in setTimeout method in ngOnInit method
3) But when this method is called (ngAfterViewInit) , this.products is still NULL indicating that subscribe is not yet called '
4) Is that the reason setTimeout is called inside ngOnInit method ?
5)If this is the case , what is the use of ngAfterViewInit method ?
1) It depends. the subscription execute the code only when the action is done. So, when the this.service.getData() has finished its job. The setTimeout do the job after a delay. If the subscription need less time than the setTimeout, it will be executed first.
2) Maybe you were trying to notice when the function is executed?
3) the AfterViewInit is fired multiple times. You can check like this if(!!something) and then execute some code.
4) you should ALWAYS avoid to use settimeout (just use it for debug purposes).
EDIT:
ngOnInit() {
this._service.getProducts().subscribe(
((data : Product[]) => this.dataSource = new MatTableDataSource(data)),
() => console.log('THIS IS ERROR')
);
setTimeout(() => this.dataSource.paginator = this.paginator);
//this.dataSource.paginator = this.paginator;
}
`
Let's simply this code a bit:
ngOnInit() {
this.service.doStuff()
.subscribe(result => {
this.functionA();
},
err => {
//Do other stuff in case of an error
});
this.functionB();
}
functionA(){
console.log("Hello,");
}
functionB(){
console.log("world!");
}
The output of this code will be:
world!Hello,
But why?
That's because of the observable pattern.
You can imagine that as you walking with two people: one that know english, one that doesn't. So even if you say "How are you?" first to the guy who doesn't know english, he will need time to understand what did you say and answer you. At the same time, the other guy (that know english very well) answer you instantly.
The example of functionA and functionB is the same. FunctionA is executed only when the subscription has catch something. That's why it isn't fired first. You can see that putting a debug point here:
ngOnInit() {
this.service.doStuff()
.subscribe(result => {
---> this.functionA();
},
err => {
//Do other stuff in case of an error
});
---> this.functionB();
}
hope to have explained well this.
Now let's move on, let's use the timeout:
ngOnInit() {
this.service.doStuff()
.subscribe(result => {
this.functionA();
},
err => {
//Do other stuff in case of an error
});
settimeout(() => {
this.functionB();
}, 500);
}
Which function will be executed first?
Spoiler: You can't know that.
If you are wondering why, it's easy: You know exactly that the functionB will be called after 500ms, but you can't know how much time will use the subscription to be ready. So if you are lucky, and your subscription usually need about 500ms to complete, you can try to reload the page several time, sometimes you will see Hello, world!, sometimes you will see world!Hello,.
To answer in a better way at your questions: I don't really know why did you put the code like this, literally no idea.
The ngAfterViewInit is a life-cycle called after the ngOnInit, and execute the logic after Angular has fully initialized a component's view.
I will try to simplify the description:
setTimeout puts the inside function in the end of the javascript queue so while javascript process is running it will pop up from the stack and call the operation. anything in the queue will get called only if the stack is empty. so setTimeout tells javascript to hold the this code till you finish your work.
subscribe and observable: observable is async data structure so once you subscribe to it you can never now how much time it will take to call the subscribe method. in other words, subscribe will get called only if as example http response returns.
Back to your question: you can not know when your setTimeout code get called but theoretically it will get called before subscribe (javascript engine faster than http response).
If you need to initialize some data table structure only after you get the data from the http request you should put it inside the subscribe method and no need for setTimeout.
ngAfterViewInit is used by angular to tell the developer that in this stage your view is ready and you can as an example use elementRef.
ngOnInit is used by angular to tell the developer that all the inputs and directives ...etc.
1. no , setTimeout will be called only once and before subscribe as its outside of its context.
2. because of Asynchronous update , if we update properties asynchronously the values will not be updated when the verification loop is running and we get no error.
3. the ViewChild is available only after ngAfterViewInit. it populates the children when creating a view and so they are available earlier.
4. ngOnInit lifecycle hook is triggered before the DOM update operation and will give no error. ngOnInit lifecycle hook is triggered after the bindings have been processed . ngAfterViewInit is invoked when the view is initially rendered i.e. it's called after a component's view, and its children's views, are created.
5. ngAfterViewInit() should be called after a component's view, and its children's views, are created, most importantly children's ngAfterViewInit()s are called before the parent's ngAfterViewInit().

Merge events to perform async work with RxJs and Angular

So I have a checkbox that is tied to a model:
<mat-checkbox [(ngModel)]="element[column]" (click)="updateRow(element, $event)"></mat-checkbox>
The click event is supposed to call the server and perform a write in the db.
So, for this, I thought that using concatMap was enough since it waits before making the next call to avoid exhausting the database.
updateRow(row, $event): void {
Observable.from([{ row: row }])
.concatMap(i => this._definitionService.updateDefinition(i) //async work
.subscribe(result => console.log('row updated'), error => {
this.snack(error);
});
}
The problem is that it does not wait since every event change is a new call, and performing too many changes exhaust the database.
What would be the solution for this? I thought about tying the events using fromEvent like this:
Observable.fromEvent($event.target, 'click')
.subscribe(i => {
console.log(i);
});
But, it only works when clicking twice on the checkbox.
What would be the correct approach?
Thanks
The reason why it's calling many requests is because every time you click the element you call updateRow that creates new instance of Observable.from. So concatMap won't help you because you when you make 10 clicks it subscribes to 10 different Observables with 10 different concatMaps.
Probably the easiest way to achieve what you want is creating an intermediate Subject and pushing item into it every time you call updateRow:
const s = new Subject();
s.concatMap(i => this._definitionService.updateDefinition(i))
.subscribe(
result => console.log('row updated'),
error => this.snack(error),
);
...
updateRow(row, $event): void {
s.next({ row });
}
Now you'll have just one subscription and pushing multiple item into Subject s (with next()) will execute them in order one after another.

Is it possible to remove just one RXJS Observer from an Observable?

If I have a service containing an Observable and I have the following code...
this.collapse.newChild.subscribe(name => {
if (name === "helpArea") {
this.collapse.process("helpArea", true);
}
// I would like to detach now
});
Is it possible to detach without canceling the original subscription? If I call unsubscribe then when I call this.collapse.newChild.next("thing") it throws an error Error: object unsubscribed.
So I don't think unsubscribe is what I am looking for, so is there a way to detach a single observer?
As a work around for now I am using...
this.collapse.newChild.map(name => name === "helpArea").first().subscribe(...)
Peter answer is correct from a design/practice PoV, but beware also that you get this error because you unsubscribe the subject.
What you want to stop is the subscription instead:
var subscription = this.collapse.newChild.subscribe(...)
// NOT this
// this.collapse.newChild.unsubscribe()
// this
subscription.unsuscribe()
This will kill/stop not the subject, but only your current subscription to it.
I don't think your "workaround" is at all bad. It even seems idiomatic: the way that Rx wants you to do it. But you can write it a bit shorter, using the form of first that takes a predicate.
this.collapse.newChild
.first(name => name === "helpArea")
.subscribe(...);
In general, the mindset to get into with Rx is to as much as possible use combinators to manipulate streams to produce exactly the values that you need, and then subscribe with a simple handler that applies the final, effectful, step: updating the UI, calling a service etc. This will lead to cleaner, more expressive code, that is easier to read and easier to re-use.
Another nice thing about this approach for your example is that there is no need to unsubscribe. RX will do that for you after the first value because it knows there won't be more. So it's much cleaner than doing it yourself.
Calling unsubscribe() on a Subscription object unsubscribes only this particular observer. So a question is how did you unsubscribed at the first place because using first() sends complete notification which causes unsubscription anyway.
Btw, you can always unsubscribe inside your own subscriber's callback:
var subscription = this.collapse.newChild.subscribe(name => {
if (name === "helpArea") {
this.collapse.process("helpArea", true);
}
subscription.unsubscribe();
});

Categories