Is there a way to treat an NgRx select as a Subject instead of BehaviorSubject?
I don't want to execute the next when the subscription is made the first time (default behavior). Instead, a want to execute it just when the state is changed.
this.store.pipe(select(fromAuth.selectErrorMessage)).subscribe((message) => {
/* This content is executed the first time the subscription is made,
and after that is executed everytime the state change */
this.isLoading = false;
this.errorMessage = { ...message };
});
this.store.pipe(select(fromAuth.selectErrorMessage)).pipe(skip(1))
will ignore the current value and only emit the next value.
Related
I have an overlay component that appears when a user clicks on certain things in my page, and in this overlay it gives a warning and 2 buttons, one for yes and the other for no. What I want is to create a function that'll serve this component, and then it will wait for the user to respond, and subsequently return true or false based on what button was pressed. This boolean result can then be used to further progress to other code.
This is what I have tried already. It uses promises rather than rxjs observables.
A component will call this function to bring the overlay from the service, eg this.service.promptUser().then(res => if (res === true) { doSomething() }).
In the service:
didContinue: boolean = null;
async promptUser() {
this.showOverlay.next(true) //BehaviourSubject when true brings the popup
await waitForUser();
const decision = this.didContinue;
this.closeOverlay(); //sets didContinue back to null
return decision
}
The didContinue is a property inside of the service to indicate whether they have clicked yes or no using a boolean. Otherwise it will remain null. The click events from the overlay component will set the property didContinue to true or false.
The waitForUser function to wait for the user's input:
async waitForUser() {
while (this.didContinue === null) {setTimeout(() => {}, 50};
}
Currently it'll get stuck at the waitForUser() function but the popup will have not rendered at that stage, so the user can't input anything, the didContinue property will never change, and the application will freeze.
Please do send it forward if you know of an existing solution, I miss a lot of things with my google-foo. I am currently still new to Angular.
Create a service that will handle the result
inject service in your component where you want to use it like this
constructor(public popService: popService) { } // create functions accordingly in service
Call service method with the help of constructor on click() event like this
<button (click)="popService.success()">Done
<button (click)="popService.cancel()">Done
i hope this will help, let me know if you need further help :-)
I have a page form with values already setted, previous and next button elements.
At ngOnInit, I'm getting a List with 3 items from an observable, as initial value - sometimes I get 4 items.
Before I go to the next page I have to click, necessarily, on a button that will call the function calculate() that will make a request and my observable List will have 4 items.
So, when I click on next button onNextButton() I would like to use the initial value to compare with the current, if they are the same, or check if this list had any changes (any incrementing).
The way that I'm doing, I'm not manage to keep/store the first value. On next button click, i'm getting the updated value, instead the previous.
My code:
export class PageComponent implements OnInit {
questions$: Observable<any[]>;
hasChange: boolean;
ngOnInit() {
// GETTING INITIAL VALUE
this.questions$.subscribe((quest: any[]) => {
this.hasChange = quest.length > 3 ? true : false
});
}
calculate() {
// calling a request
// at this point, my observable will update and will have 4 items.
}
onNextButton() {
if (hasChange) {
// submit()
} else {
// navigate()
}
}
}
So, in this scenario the initial value should be a list w/ 3 items, but i'm getting 4 and its breaking my logic...
How do I get the previous value of Observable and store it in a variable?
Or, how can I detect any changes?
I tried behavioursubject and pairwise from RxJS, but I'm not sure how to apply it.
Thank you!!!
As you stated, you must use ReplaySubject or BehaviourSubject, since Observable does not have that "memory" for retrieve last emitted value.
ReplaySubject and BehaviourSubject are similar, both send last emitted value on subscription, but ReplaySubject only emits last emitted value on subscription and BehaviourSubject stores a list of last emitted values (configurable size). I'll be using BehavoiurSubject since is a bit more complete.
Just replace the Observable you were using with the BehaviourSubject, and keep in mind that you must definde that memory size when you instance the class. For example, you could create a method that returns a BehaviourSubject with last boolean stored like this:
private fooBehaviourSubject;
function ngOnInit: void{
this.fooBehaviourSubject = new BehaviorSubject<boolean>(1); // 1 is the "stack" size
this.fooBehaviourSubject.next(true); // <- First value emitted here
}
function getValue: BehaviourSubject<boolean> {
return this.fooBehaviourSubject;
}
When you subscribe like this:
getValue().subscribe(e => console.log(e))
the last stored value (true) automatically will be retrieved and shown in console, but ensure you at least have emitted one first, or you wont execute the subscription until one next method is called. After that, every update of the value will trigger that console.log with the updated value.
Applied to your code, you could create the BehaviourSubject in the ngOnInit, subscribe to it also in the ngOnInit to define the callback event, and call the next method of the BehaviourSubject once the list must be updated.
export class PageComponent implements OnInit {
questions$: Observable<any[]>
hasChange: boolean
ngOnInit() {
// GETTING INITIAL VALUE
let hasSome = false
this.questions$
.subscribe((quest: any[]) => {
// ### This is works for me!
hasSome = quest.some(item => item.id === 'whatever')
// ### This is the way that I was trying to do.
// ### Is's not wrong but didn't work for me =(
// this.hasChange = quest.some(item => item.id === 'whatever')
)
this.hasChange = hasSome
}
calculate() {
// calling a request
// at this point, my observable will update and will have 4 items.
}
onNextButton() {
// ### Now this.hasChange is assigned once and where I want, OnInit.
if (this.hasChange) {
// submit()
} else {
// navigate()
}
}
}
I'm kinda new to the react framework.
As per my requirement , I want to wait until data arrives and binds to my constants in my useEffect() method.
The data is sent encrypted from the main page and is decrypted as follows :
useEffect(() => {
const DecryptedGroupID = atob(groupID.toString());
const DecryptedFactoryID = atob(factoryID.toString());
setProfitAndLossDetails({
...ProfitAndLossDetails,
groupID: DecryptedGroupID,
factoryID: DecryptedFactoryID
});
}, []);
I want to add a waiter/timer/delay to wait until the data gets bound to my const variables. (atob is a decrypting function).
The groupID is required to generate the factoryIDs for the specific group, hence during page reload since it takes time / delay for the hooks to bind, the factoryIDs won't load at times(however when refreshing it appears sometimes), I think adding a delay and giving it time to bind might fix this issue.
Just add groupID and factoryID as your useEffect dependencies. Hook will be called automatically when they are changed. Inside hook you can check if groupID and factoryID not empty, and call your setter function.
Read more about how this hook work:
https://reactjs.org/docs/hooks-reference.html#useeffect
You need to:
Test in the hook if they are defined or not
Call the effect hook when the values you are depending on change (so the hook doesn't run once when they aren't defined and then never run again) — add them to the dependency array.
Such:
useEffect(() => {
if (!groupID || !factoryID) return;
// Otherwise don't return and use them with their values
}, [groupID, factoryID]);
I am working through a Udemy course on RxJs 6 and need to ask this as it was not crystal clear to me.
Note: This is a type ahead tutorial I am currently in at the moment. So on the keyup event this method is firing off.
ngAfterViewInit() {
const searchLessons$ = fromEvent<any>(this.input.nativeElement, 'keyup')
.pipe(
map(event => event.target.value),
debounceTime(400),
distinctUntilChanged(),
// switchMap cancels prior calls.
switchMap(search => this.loadLessons(search))
);
const initialLessons$ = this.loadLessons();
this.lessons$ = concat(initialLessons$, searchLessons$);
}
Does the code mean,
for all events that fire the code will collect responses from completed calls to the loadLessons
the value of the event is referenced as search
then the => will trigger a call to the loadLessons(search)
Continue of 3: If the value of the event were lets just say an array of values, would that mean that for the => call, a separate call to the loadLessons(search) would be made passing for each individual array value
Continue of 3: or would it just pass in the entire array?
Here is line per line explanation:
ngAfterViewInit() {
const searchLessons$ = fromEvent<any>(this.input.nativeElement, 'keyup') // whenever keyup is triggered on this.input
.pipe(
map(event => event.target.value), // we extract input value from event target
debounceTime(400), // we wait for last event in 400ms span
distinctUntilChanged(), // we check that the input value did change
switchMap(search => this.loadLessons(search)) // and with that input value changed we call this.LoadLessons and then wait for its return
);
const initialLessons$ = this.loadLessons(); // this will call initial loadLeason
this.lessons$ = concat(initialLessons$, searchLessons$); // this will connect return of initial call and changes triggered by key up this is not secure for race conditions
}
Ad1. all key up events on input
Ad2. the value of input is referenced as search
Ad3. yes it would just push array as argument
Without seeing the loadLessons func, i can only assume. i will also assume you are using the concat Rxjs method.
So basically what the code does, get the "initial load of lessons" , and subscribe to it on the concatMethod, after that call completes, it goes to subscribe to the second observable searchLessons.
The searchLessons will be called again every input search, and add the new values to the lessons subscription, on the search observable .
If the params given to the loadSeassion is an array, it will depend how that method (loadSessions) works. not with rxjs although it can be done in this case i cant really tell you :)
I have two child components and i want pass data between them, to do this i use service.
In service i have
headerObject = new Subject<HeaderObject>();
In first component i have method
onClick() {
this.listImage = this.imageService.getImages();
let headers = new HeaderObject();
headers.file = this.listImage[0].data;
this.documentService.getOcrDocument(headers).subscribe(res => {
headers = res;
this.ocrService.headerObject.next(headers);
})
In second component
headerSubcribed: HeaderObject;
private headerSubscription: Subscription;
ngOnInit() {
this.headerSubscription =
this.ocrService.headerObject.subscribe((ocrHeader: HeaderObject) => {
this.headerSubcribed = ocrHeader;
}
But ngOnInit() only once after render view i want to move this code to outside ngOnit and exeute it but i don't know how ? Some tips how execute that code? I set breakpoint at next() method that emit value but later method subscribe isn't execute? What could I do wrong?
There are 2 parts which you observable/subscribe
Observing something and notifying.
In your service, you have a Subject type variable and you are pushing values to it by calling next(). when this method called, it notifies all the observers means who ever is subscribing it.
listening to the observer and do action
When you listen to an observer, you get an update when they change the value. It does not matter if you put your subscription in onNginit or constructor, if the code is there, it will notify you when there is a change into it.
You can also explore BevavourSubject if it fits to your situation.