Hello been out of the coding game for a few years and I am ramping up on Angular 8 (not enjoying it!) I can run ng serve and everything is ok but if I try to build I am getting an error here
userD() {
// TODO User Angular Service will get to that later.
var aToken = localStorage.getItem('token');
const headers = new HttpHeaders().set("Authorization", "Bearer " + aToken);
this.httpClient.get( 'http://127.0.0.1:8000/api/auth/user', { headers } )
.subscribe((res: any[]) => {
// @ts-ignore
this.users = res.data.result; // it doesn't like the .data.result part in build
console.log(this.users);
});
}
I am using //@ts-ignore for now, not ideal but I don't know why it has a problem with going in to the array if i have it as res it's ok.
Any information would be very helpful to help me understand this. Thank you.
NB: EDIT Here is the json and I want to extract to this results object to then use For in the html to loop through {"status":"200", "data": {"result":{"id":3,"name":"patrick lord","email":"patrick@larvel.io","email_verified_at":null,"phone":"123456789","profilePic":71,"created_at":"2019-09-19 19:43:04","updated_at":"2019-09-19 19:43:04","stripe_id":null,"card_brand":null,"card_last_four":null,"trial_ends_at":null,"gender":"male","Address1":"i don't live here S","Address2":"232","City":"Clearfield","Country":"USA","Zip_Post":"55550","YouTube"}}}
JB's comment hits the nail on the head: you're getting a TypeScript error because the any[]
type doesn't have a data
property; by definition, any[]
is expecting anything, with the only restriction being that the input must be an object of type Array
.
build --prod
and not serve
?You're getting the error on ng build --prod
and not on ng serve
is because the default Angular environment enforces strict
(as in "strict adherence to typing and syntax") on --prod
, but doesn't enforce strict
in the local dev environment. This is so you can play around and figure stuff out more easily when developing locally, where sloppy syntax is less of an issue, before cleaning up your code for production use. It took me a bit to wrap my head around this, too, but it's a Good Thing(tm) :P
JavaScript lets you do pretty much whatever you want: it has no clue what res
is going to look like, but it assumes you know what you're doing and if you say res
is going to have a property called data
, it just rolls with it.
This is great if you've got a small project, or if you're the only person who's ever going to work on it, but it starts to get tricky when the project gets larger, or if you have multiple developers submitting code. You know that res
has a data
property, but how does Samantha know that?
TypeScript
TypeScript is JavaScript...with Types. Types provide the shape of a given object, so everybody involved (including the TypeScript transpiler) knows what's up. When you say res: any[]
, what TS hears is "object res
is going to be an Object
of type Array
"; we haven't, however, told TS anything about the member-items within the array, and since it doesn't know what kind of array res
is going to be, the only things to which we have access are the generic methods and properties of the Array
type, itself.
So, what do we need to do instead of (res: any[])
to make this.users = res.data.result;
work?
We need to start by telling TS what to expect with a little more detail:
export interface ResponseObject {
data: {
result: {};
}
}
Now TS knows that there's an Object
called ResponseObject
that has (at minimum) a data
property, and that data
property is itself an Object
that has (again, at minimum) a property called result
.
We can plug this into our subscription call like this:
.subscribe( (res: ResponseObject) => {
this.users = res.data.result; // now it totally DOES like the .data.result part in build --prod!
console.log(this.users);
});
You may notice that res
is no longer expected to be an object of type Array
; based on the code you posted and the fact you said it worked via ng serve
, it sounds like you're expecting a single ResponseObject that delivers an array of UserObjects as the payload on the data.result
property; if so, the code sample above should work fine.
If, however, our .get()
really is returning an array of ResponseObjects, we're going to need to do some extra work.
Let's change up our interface declaration, first:
export interface ResponseObject {
status: number; /* It's OK to exclude things we don't need,
but, the status property is going to come in handy when
we eventually want to write a catch statement for cases
when the call returns an error instead of the data we
wanted */
data: {
/* you can also split up your interface declarations to
make them easier to read; this has the added benefit of
allowing us to more easily access the sub-properties of
our payload later on */
result: UserObject; /* we're expecting a single
UserObject per ResponseObject in this scenario,
otherwise we'd use: */
// result: UserObject[];
}
}
export interface UserObject {
/* remember to use the generic type names (lowercase 'string'),
not Primitives (Capitalized 'String') in your interface
declaration, otherwise you'll get more errors */
id: number;
name: string;
email?: string; /* the question mark lets you define a
property that *may* exist, but may not. Useful if your
target API only includes parameters with non-null values
in the ResponseObject */
// ...all the other things you want and think will exist
}
Then we can mix up our subscription call:
.subscribe(
// we tell subscribe that we're expecting an array of ResponseObjects
(res: ResponseObject[]) => {
/* users needs to be defined as an array to hold onto
multiple UserObjects; we're clearing it here to make
sure we're only dealing with fresh data and specifying
that it will be an array of type UserObject */
this.users: UserObject = [];
/* the data property isn't a property of the *array*, but
of each array *member-item*, so we need a loop */
res.forEach(
(res_item: ResponseObject) => {
// add the response object data to our users array
this.users.push(res_item.data.result);
});
// now console log should properly show our array of UserObjects!
console.log(this.users);
}
);
n.b., Please don't forget to mark this as the Accepted Answer if it does, in fact, answer your question satisfactorily. If someone comes along with a better or more concise answer to your question you can always change the Accepted Answer after-the-fact.
Cheers!