How to fix the error observable in angular - javascript

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));

Related

Type 'string | undefined' is not > assignable to type 'string'

I just want to compare two arrays together but one of the array comes from a 3rd party AWS, which I cannot make sure that it exists.
Somehow I am not able to bypass my error.
I am protecting the call of the function by returning and I still get this error:
Argument of type '(string | undefined)[]' is not assignable to
parameter of type 'string[]'. Type 'string | undefined' is not
assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2345)
I'm also getting a warning trying to check the both arrays match.
const fileNames = files?.Contents?.filter((content) => content.Key?.endsWith('.docx')).map((content) =>
content.Key?.replace(FOLDER_PATH, '')
);
if (!fileNames || !fileNames.length || fileNames === undefined) {
return;
}
compareFileNames(fileNames, configFiles) // compilation error above
// ...
const compareFileNames = (a: string[], b: string[]) => {
if (a.length !== b.length) return false;
return a.sort().toString() === b.sort().toString(); // Warning Move this array "sort" operation to a separate statement
}
Playground Link
What are the problems here?
In your example you write:
if(!data || typeof data === 'undefined' || typeof data[0] === 'undefined') {
return;
}
which can be shortened to:
if(data.some(item => item === undefined)) {
return;
}
Next, you're passing data to a method that only accepts string[] but data still has the type Array<string | undefined> because typescript cannot infer that there are no undefined elements inside of the array based on the if condition.
You can either cast the array to a string[] manually, or use a typeguard:
Cast:
const main = () => {
const data: Array<string | undefined> = [undefined];
if(data.some(item => item === undefined)) {
return;
}
foo(data as Array<string>);
}
Typeguard:
const main = () => {
const data: Array<string | undefined> = [undefined];
if(!isNotUndefinedArray(data)) {
return;
}
foo(data);
}
function isNotUndefinedArray<T>(a_arr: Array<T | undefined>): a_arr is Array<T> {
return !a_arr.some(a_item => a_item === undefined);
}
The expression
.map((content) => content.Key?.replace(FOLDER_PATH, ''))
returns either a string or undefined. Therefor the inferred type of fileNames is (string | undefined)[].
You can change your statement to this
const fileNames: string[] = files?.Contents?
.filter((content) => content.Key?.endsWith('.docx'))
.map((content) =>
content.Key?.replace(FOLDER_PATH, '')
)
.filter( s => s);
The last filter removes all undefined entries.

Trigger observables chain only if there was a button click

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)
);
}

TypeScript Type 'Promise<void | Object>' is not assignable to type 'Promise<Object>' on Promise.then()

I am currently trying to implement a service for caching document in base64 format. I want this service to fetch a document from sessionStorage, and if there is no document in sessionStorage, fetch it from IRequestService and then save it to sessionStorage. I tried this approach, but I am getting a type error
Type 'Promise<void | GetDocumentResult>' is not assignable to type 'Promise<GetDocumentResult>'.
The code looks like this:
getDocument(requestId: string, documentId: DocumentID, requestService: IRequestService): Promise<GetDocumentResult> {
if (this.storageKey in sessionStorage) {
const documentsMap: Map<DocumentID, GetDocumentResult> = new Map(JSON.parse(sessionStorage.getItem(this.storageKey)!))
if (documentsMap.get(documentId) !== undefined) {
return new Promise(resolve => resolve(documentsMap.get(documentId)!))
}
}
return requestService.getDocument(requestId, documentId)
.then(value => {this.setDocument(documentId, value)})
}
I was expecting return type of Promise< GetDocumentResult >.then to be Promise< GetDocumentResult >, but for some reason which I dont understand, it's Promise<void | GetDocumentResult>
Do someone knows why this is happening?
Thank you
As already explained, your then callback does return void not the result. You're looking for
getDocument(requestId: string, documentId: DocumentID, requestService: IRequestService): Promise<GetDocumentResult> {
if (this.storageKey in sessionStorage) {
const documentsMap: Map<DocumentID, GetDocumentResult> = new Map(JSON.parse(sessionStorage.getItem(this.storageKey)!));
const document = documentsMap.get(documentId);
if (document !== undefined) {
return Promise.resolve(document);
}
}
return requestService.getDocument(requestId, documentId).then(value => {
this.setDocument(documentId, value);
return value;
// ^^^^^^^^^^^^
});
}
Btw, I would recommend to extract the cache lookup and storage (setDocument?) into a separate service, and to have that service keep the documentsMap in memory instead of parsing JSON every time getDocument is called.
Most likely you have an issue because you are not returning anything inside your .then()
here:
.then(value => {this.setDocument(documentId, value)})
and that makes your returned type be Promise<void> instead of Promise<GetDocumentResult>
final versions should be like here:
getDocument(requestId: string, documentId: DocumentID, requestService: IRequestService): Promise<GetDocumentResult> {
if (this.storageKey in sessionStorage) {
const documentsMap: Map<DocumentID, GetDocumentResult> = new Map(JSON.parse(sessionStorage.getItem(this.storageKey)!))
if (documentsMap.get(documentId) !== undefined) {
return new Promise(resolve => resolve(documentsMap.get(documentId)!))
}
}
return requestService.getDocument(requestId, documentId)
.then(value => { return this.setDocument(documentId, value)})
}

How do I use async pipe instead of using subscribe?

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"

SyntaxError: Unexpected end of input Angular 6 and RxJS

In Angular 7, I was trying to create a custom Aysnc validator which checks van item already exists or not and if exists it will show error as part of form validation.
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.itemService.isItemExists(this.listId, ctrl.value).pipe(
map(isExists => (isExists) ? { alreadyExits: true } : null),
catchError(() => null)
);
}
And my .itemService.isItemExists() methods as below
isItemExists(listId: string, itemNumber: number) {
const url = "http://localhost:41469/isValid?listId='abc'&itemNumber=6";
return this.http.get<boolean>(url);
}
Don't mind URL, It is proper in my code. But getting error
"SyntaxError: Unexpected end of input, Array.map ()...."
How to fix this issue?

Categories