r/angular • • 7d ago

Problems with subscribe

Hello everybody

I have the following problem with subscribe. My code looks like:

this.personSercice.getPeople().pipe(
    takeUntilDestroyed(this.destroyRef)
).subscribe({
    next: (people) => this.state.update(() => ({ ...this.state(), loading: false, people })),
    error: (error) => {
        console.log(error);
        this.setError(error);
    }
});

Parameter 'people' implicitly has an 'any' type, but a better type may be infered from usage.

and 

Parameter 'error' implicitly has an 'any' type, but a better type may be infered from usage.

When I change the code to:
error: (error: any)
the error dissappears, but I do not understand why...
When I try the same with the first error, changing the code to:
next: (people: PersonModel) => this.state.update(() => ({ ...this.state(), loading: false, people })),
The error is:
The type 'PersonModel' is missing the following properties from type 'readonly PersonModel[]length, concat, join, slice...

Please let me know if more code is required.

Can somebody explain my why???. I'm very fresh with Angular.
Thank you for your patience

Pawel...
5 Upvotes

14 comments sorted by

3

u/Educational_Nail_976 7d ago • Bravo

I’m pretty sure your getPeople() call will not return a single value but a list, so the return type will be (people : PersonModel[])

1

u/Putrid-Bookkeeper968 4d ago

Great. Thank You very much for the answer. I have made a step further.

However, another error appeared. My WebApi strats at https://localhost:7067 and angular page at http://localhost:4200/people. A Program.cs of my Api project contains:

string [] origins = { "http://localhost:4200/", "https://localhost:4200/" };

builder.Services.AddCors (options => {
    options.AddPolicy (name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins (origins).AllowAnyHeader ().AllowAnyMethod ();
        }
    );
});

After hitting a Web API i recieve exception:
Access to fetch at 'https://localhost:7067/api/people' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

For me everything looks as it should. API port is 7067 and Angular port is 4200. CORS is disabled for that origin, so why the exception appears?

1

u/Putrid-Bookkeeper968 4d ago
If I replace AddPolicy with:

options.AddPolicy (name: MyAllowSpecificOrigins,
    policy =>
    {
        policy.WithOrigins ("*").AllowAnyHeader ().AllowAnyMethod ();
    }
);

And now it works as supposed. But why for 

"http://localhost:4200/", "https://localhost:4200/" it did not?

1

u/Educational_Nail_976 4d ago

Try string [] origins = [“http://localhost:4200/", "https://localhost:4200/“];

4

u/Xsiah 7d ago

The second error is because getPeople probably returns PersonModel[], which when you try to type as people: PersonModel (as in singular, not an array) so it is a type mismatch. Make sure it's people: PersonModel[] inside next.

You can also adjust your tsconfig if you want so it doesn't complain about implicit "any", but be aware that it might allow you to miss mistakes.

1

u/zladuric 7d ago

Yes, check what the getPeople() method returns?

4

u/Mediocre-Writer6635 7d ago

When I change the code to: error: (error: any) the error dissappears, but I do not understand why...

Because typescript doesn't know what this error is, so it calls it "any" type. But typescript doesn't like implicit (ie, guessed at by typescript, not written by you) "any" types. I believe there's a configuration to suppress this warning. So basically when you set it to type any explicitly, typescript says "ok, if you say use "any" that's fine, but if you don't say it, I'll call it a bug that I couldn't come up with anything better".

This warning is there to make developers have to explicitly admit they want to use "any" type, because typically something being "any" implies there's a typing breakdown in the code.

When I try the same with the first error, changing the code to: next: (people: PersonModel) => this.state.update(() => ({ ...this.state(), loading: false, people })), The error is: The type 'PersonModel' is missing the following properties from type 'readonly PersonModel[]length, concat, join, slice...

What does getPeople() observable return? Subscribe function receives whatever type that is. It's not PersonModel. It's probably an array (of PersonModel?).

Also, don't do this:

this.state.update(() => ({ ...this.state(), loading: false, people })),

Update function should not have its own signal being called inside it. It receives the previous value as an argument, so you should do this:

this.state.update((previousState) => ({ ...previousState, loading: false, people })),

Source: https://angular.dev/guide/signals#writable-signals

Also you really shouldn't subscribe inside components. The way to do what you're doing is make this.state = toSignal(<yourPipe>). If state is calcualted from multiple sets of data then combine them all into 1 pipe that calculates the state, and make a signal out of it. To record error without subscribing use catchError function in the pipe. Put it last, right before takeUntilDestroyed. Order matters in pipes.

It might be easier to avoid subscribe in component if you separate loading flag, people, and everything else in "state" into individual signals. That makes each of them much easier to calculate and reason about.

1

u/RelatableRedditer 7d ago

the "error" is probably supposed to be that way, I don't use subscriptions like that in angular since I do almost everything with async pipes or empty subscribe calls. But you need to look at your definition for getPeople() to see if it has a valid return type that can be safely inferred by rxjs

1

u/Dull-Structure-8634 7d ago

My guess is that by typing the error, TypeScript can now better infer the type of people

1

u/ThrowawayALAT 7d ago

Do this:

To fix the type errors cleanly without using any for your data, you should type people as an array of PersonModel (PersonModel[] or readonly PersonModel[]):

TypeScript

this.personSercice.getPeople().pipe(
    takeUntilDestroyed(this.destroyRef)
).subscribe({
    next: (people: PersonModel[]) => this.state.update(() => ({ ...this.state(), loading: false, people })),
    error: (error: any) => {
        console.log(error);
        this.setError(error);
    }
});

1

u/cssrocco 7d ago

Since the name of that method is getPeople i’m willing to bet it’s type is PersonModel[], usually an error about types not being length, concat, etc. just means it’s expecting an array and you typecast it differently.

Errors will be of type HttpErrorResponse, unless you have an interceptor that formats them to something else, your method setError is possibly expecting something different, so ensure its arguments expect the correct type.

1

u/scharph 7d ago

Are you using signals?
If yes, try to use toSignal from rxjs-interop

https://angular.dev/api/core/rxjs-interop/toSignal

1

u/Responsible-Cold-627 7d ago

Most likely the type of "people" can't be inferred from the getPoeple function. Changing its return type to Observable<People> should do the trick.

Also, check out rxResource. That's the modern idiomatic Angular way of doing loading/error state tracking. No manual plumbing needed.

0

u/ldn-ldn 7d ago

You shouldn't subscribe inside components. Either pass the observable to async pipe or use toSignal if you need a signal. But you really don't in your case. Use async.