I would like to use the async pipe "| async" instead of subscribing. This is what my subscription code currently looks like:
ngOnInit(): void {
this.activatedRoute.url
.pipe(takeUntil(this.unsubscribe$))
.subscribe(segments => {
this.quizName = segments[1].toString();
});
}
and in my template I have: <mat-card *ngIf="quiz.quizId === quizName">
Let's try this :
quizName$: Observable<any>;
ngOnInit(): void {
this.quizName$ = this.activatedRoute.url
.pipe(
takeUntil(this.unsubscribe$),
map(segments => segments[1].toString()); // Not sure about this part
);
}
<mat-card *ngIf="(quizName$ | async) === quiz.quizId">
Be careful, everytime you will use async pipe in your template, it will make a subscribe.
Add variable:
quizName$ = this.activatedRoute.url.pipe(map(segments => segments[1].toString()));
no need for takeUntil such as "| async" does it
optional(your IDE would know about it by itself)
quizName$: Observable<string> ...
in HTML:
*ngIf="(quizName$ | async) === quiz.quizId"
more "robust" solution
showQuiz$: Observable<boolean> = this.activatedRoute.url.pipe(
map(segments => segments[1].toString()),
map(quizName => quizName === this.quiz && this.quiz.id)
);
*ngIf="showQuiz$ | async"
Related
I would like to execute toggleButtonOnClick$() function from the service, only when I click on a button. This function will save current state, service will later receive this change and update usersObs$.
Whenever the above mentioned happens, the toggleButtonOnClick$ gets executed a few more times, even if I didn't click on the button again.
How can I prevent this and make this function only execute when I do .next() on the clickSubject and not when userObs$ changes?
I will write an example of the whole situation
ButtonComponent
private userClick = new Subject<null>();
private obs1$: Observable<string>();
private obs2$: Observable<string>();
private obs3$: Observable<boolean>();
ngOnInit(): void {
this.obs2$ = this.obs1$.pipe(
switchMap((value) => this.someService.getSomePropBasedOnValue$(value))
);
this.obs3$ = this.obs2$.pipe(
switchMap((value) => this.someService.checksAndReturnsBoolean$(value))
this.subscriptions.add(
this.someService.toggleButtonOnClick$(this.obs2$, this.userClick).subscribe()
)
}
ngOnDestroy(): void {
this.subscriptions.unsuscribe();
}
onClick(): void {
// emit a value when click on button to start observable chain
this.userClick.next();
}
HTML
<div
[attr.tooltip]="(obs3$ | async) ?
('text_translation_1' | transloco)
: ('text_translation_2' | transloco)"
>
<span
*ngIf="(obs3$ | async) && !isHovering"
>
Something here
</span>
<span
*ngIf="(obs3$ | async) && isHovering"
>
Something here
</span>
<span
*ngIf="!(obs3$ | async)"
>
Something here
</span>
</div>
SomeService
public checksAndReturnsBoolean$(id): Observable<boolean> {
return this.userObs$.pipe(
map((users) => { users.some(((user) => user.id === id)) }
);
}
public getSomePropBasedOnValue$(id): Observable<SomeObject | null> {
return this.userObs$.pipe(
map((users) => { users.find(((user) => user.id === id)) ?? null }
);
}
public toggleButtonOnClick$(obs2$, userClick): Observable<void> {
return userClick.pipe(
switchMap(() => obs3$),
switchMap((id) => combineLatest([this.getSomeDataById$(id), of(id)]))
).pipe(
map(([data, id]) => {
// some code block that gets executed everytime an observable emits new value
})
);
Once everything finishes, I try to store the users decision after a click is made. So the userObs$ gets updated, once that happens, the block within toggleButtonOnClick$ is executed again, and not once, but 2 sometimes 3 or 4.
Btw, in the component, the obs2$ im using it on the DOM with Async pipe to show/hide stuff. Maybe that is also triggering the calls after the service observable changes.
I've tried several things already without luck.
Any tip or help or guiding would be appreciated.
Thanks.
What I was needing was to use shareReplay(1) and take(1) on different functions on my service to make it work as expected without repeating unnecessary calls.
It would end up looking like this:
public checksAndReturnsBoolean$(id): Observable<boolean> {
return this.userObs$.pipe(
map((users) => { users.some((user) => user.id === id) }),
take(1)
);
}
public getSomePropBasedOnValue$(id): Observable<SomeObject | null> {
return this.userObs$.pipe(
map((users) => { users.find((user) => user.id === id) ?? null }),
shareReplay(1)
);
}
I am trying to use of but my editor is saying that it is deprecated. How can I get this to working using of?
public save(): Observable<ISaveResult> | Observable<boolean> {
if (this.item) {
return this.databaseService.save(this.userId, this.item)
}
return of(false)
}
public componentSave() {
const sub = this.userService
.save()
.subscribe(val => {
// This callback shows the below error in the editor
})
}
When I use this, I get the following error in my editor:
#deprecated — Use an observer instead of a complete callback
'(next: null, error: (error: any) => void, complete: () => void): Subscription' is deprecated
Expected 2-3 arguments, but got 1.
Never mind I think I found the problem. Change the return type of the save function from
public save(): Observable<ISaveResult> | Observable<boolean>
to
public save(): Observable<ISaveResult | boolean>
I want to remove the current user that login thats why I used filter to.
but I get an error which is:
Type 'Subscription' is missing the following properties from type 'Observable[]>': _isScalar, source, operator, lift, and 6 more.
Here's the code:
#Select(UserPageState.get('collection')) users$: Observable<Array<Partial<User>>>;
async ngOnInit() {
const USER = this.creds.credentials['id'];
this.users$.subscribe(param => param.filter(x => x.id !== USER));
await this.store.dispatch(new UserPageList({ start: 1, length: this.pageSize })).toPromise();
}
HTML
<ag-grid-angular
style="width: 100%; height: 100%;"
[class]="(darkMode$ | async) ? 'ag-theme-balham-dark' : 'ag-theme-balham'"
[gridOptions]="gridOptions"
[rowData]="users$ | async"
[columnDefs]="columnDefs"
[frameworkComponents]="frameworkComponents"
(gridReady)="onGridReady($event)"
(firstDataRendered)="onFirstDataRendered($event)"
>
</ag-grid-angular>
The issue is in below statement, subscribing to an observable returns an Subscription instance and you are again assigning it to observable.
this.users$ = this.users$.subscribe(param => param.filter(x => x.id !== USER));
Just don't assign it to observable again, it will resolve the issue.
this.users$.subscribe(param => param.filter(x => x.id !== USER));
I can not display my observables directly when I want to use the Async Pipe.
I can do without the pipe async, and it works. My comment code works well, but I would really like to use the Async Pipe for Angular and unsubscribe alone.
user.service
public newsfeed: BehaviorSubject<MessageUser> = new BehaviorSubject(null);
reloadNewsFeed(): Observable<MessageUser> {
return this.http.get<MessageUser>('api/user/newsfeed').pipe(
tap((x: MessageUser)=>{
this.newsfeed.next(x)
}), switchMap(()=>{
return this.newsfeed
}))
};
newsfeed.component.ts
//public lesMessage2;
public lesMessages:Observable<MessageUser>
constructor(
private userService: UserService,
) { }
ngOnInit() {
// It does not work
this.lesMessages = this.userService.reloadNewsFeed()
// OK that works
// this.subMessage = this.userService.reloadNewsFeed().subscribe((user: MessageUser)=>{
// console.log(user);
// this.lesMessage2 = user;
// }, (err)=>{
// console.log(err);
// })
}
newsfeed.component.html
<span *ngIf="lesMessage | async; let user">
<p>Iterer sur mon Subjects : <strong> {{ user.message }} </strong> </p>
</span>
The thing is that in TS file you have property named as this.lesMessages but in template you try to subscribe to "lesMessage" (should be lesMessages). Try to change your current string:
... *ngIf="lesMessages | async" ...
I have an Angular application where I am trying to check an external data service for changes every few seconds, and update the view.
I've tried to implement Polling from rxjs but I'm not able to access the object, conversely it seems the polling function isn't working but assume this is because the returned object is inaccessible.
app.component.ts
export class AppComponent {
polledItems: Observable<Item>;
items : Item[] = [];
title = 'site';
landing = true;
tap = false;
url:string;
Math: any;
getScreen(randomCode) {
const items$: Observable<any> = this.dataService.get_screen(randomCode)
.pipe(tap( () => {
this.landing = false;
this.Math = Math
}
));
const polledItems$ = timer(0, 1000)
.pipe(( () => items$))
console.log(this.polledItems);
console.log(items$);
}
excerpt from app.component.html
<h3 class="beer-name text-white">{{(polledItems$ | async).item_name}}</h3>
excerpt from data.service.ts
get_screen(randomCode) {
return this.httpClient.get(this.apiUrl + '/tap/' + randomCode)
}
assuming that you want an array of items you could go with something like this.
// dont subscribe here but use the
// observable directly or with async pipe
private readonly items$: Observable<Item[]> = this.dataService.get_screen(randomCode)
// do your side effects in rxjs tap()
// better move this to your polledItems$
// observable after the switchMap
.pipe(
tap( () => { return {this.landing = false; this.Math = Math}; })
);
// get new items periodicly
public readonly polledItems$ = timer(0, 1000)
.pipe(
concatMap( () => items$),
tap( items => console.log(items))
)
the template:
// pipe your observable through async and THEN access the member
<ng-container *ngFor="let polledItem of (polledItems$ | async)>
<h3 class="item-name text-white">{{polledItem.item_name}}</h3>
</ng-container>
take a look at: https://blog.strongbrew.io/rxjs-polling/
if you are not awaiting an array but a single than you dont need the ngFor but access your item_name like:
<h3 class="item-name text-white">{{(polledItems$ | async).item_name}}</h3>