Why is it saying subscribe undefined in the browser - undefined

updload(name,age,address)
{
debugger;
this.data=this.service.tryinsert(name,age,address);
this.data.subscribe(
(respose:any)=>{
console.log(respose);
}
)
}
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
I have imported the above two files but when I pass the data for name,age, address from the browser, it says
ERROR TypeError: Cannot read property 'subscribe' of undefined

The question says
ERROR TypeError: Cannot read property 'subscribe' of undefined
because by the time this.data.subscribe() is called the variable this.data has not yet been assigned any value.
Service functions are almost always observables in angular and therefore asynchronous. This means that the response of the server will be dependent on several factors and not instantaneous.
To correct your code you would use it like below:
updload(name,age,address)
{
debugger;
this.service.tryinsert(name,age,address)
.subscribe(responseData => {
console.log(responseData);
// use the code below if responseData is not of type string
// console.log(JSON.stringify(responseData));
});
}

Related

How to catch a property of a JSON object from an Angular HTTP call and fix the "No property" error in Angular CLI?

I am using a public API to fetch movie data. And the following is my service method for getting that data from API:
getMovieList(): Observable<Movie[]> {
return this.http.get(this.moviesURL).pipe(
map((data: Movie[]) => data),
catchError(this.handleError),
);
}
And this is the method in the component for subscribing that data:
getMovieList(): void {
this.movieApiService.getMovieList()
.subscribe(
(data: Movie[]) => {
this.movieList = data.results;
}
);
}
The problem is that the API returns an object which has 4 properties: page, results, total_pages, total_results. And I only need the results property. But when I try to assign data.results to my component's property or send data.results from my service method instead of data then angular cli gives an error of "results is an undefined property of data". My question is how do I get the results property directly without having to touch the data object and i also need to assign Movie[] type to the results. But currently I am setting the type to the data object.
The problem lies in your model, you defined that you expect array of Movies but you receive the object with 4 properties which one of them called results are the model you defined, so the solution is:
Define the interface like this:
export interface IDataFromApi {
results: Movie[];
page: number;
total_pages: number;
total_results: number;
}
Then the first function will be:
getMovieList(): Observable<IDataFromApi> {
return this.http.get(this.moviesURL).pipe(
map((data: IDataFromApi) => data),
catchError(this.handleError),
);
And method in component:
getMovieList(): void {
this.movieApiService.getMovieList()
.subscribe(
(data: IDataFromApi) => {
this.movieList = data.results;
}
);
}

Angular 6 error handlig - HttpErrorResponse has undefined status

I have a Angular 6 client consuming a REST Api developed with .Net Web Api.
Everything is working except for the error handling. When I try to process the error to react differently to different status codes (404, 403, 409, 500...) I simply can't make it work. The HttpErrorResponse object doesn't have any of the fields it is supposed to (like 'status' or 'error').
I've made a super simple service that reproduces the issue:
Request on the service.ts
public test(): Observable<any> {
let url = this.templatesUrl + '/myMethod';
console.log('GET myMethod ' + url);
return this.http.get<any>(url)
.pipe(catchError(this.handleError));
}
Error handler (pretty much straight from the official documentation):
private handleError(error: HttpErrorResponse) {
console.warn(error);
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.message}`);
}
// return an observable with a user-facing error message
return throwError('Unexpected error');
}
Service on the .Net side:
[HttpGet]
[Route("myMethod")]
public IHttpActionResult myDotNetMethod()
{
return InternalServerError(new Exception("Details about the issue"));
}
The service is called and it returns a status code 500 along with a json object:
The status of the response:
The response header, it is json:
The json object:
And what the log shows: no status and pretty much an empty object:
Loosk like the HttpErrorResponse is pretty much empty. But the response from the API was fine, the status code is there and the json object too.
What am I missing?
Update: In case you are wonder what hides behind that "Internal Server Error" that shows in the log (it is just the callstack generated by chrome):
Update 2: Here's the error object under the microscope. It is simply "Internal Server Error". No trace of status or message.
Solved... the object was empty due to an interceptor. So if something similar is happening to you check those out.
I'm sorry to have wasted everyone's time, I was unaware of the existance of that particular interceptor.
I think your problem is how you are throwing the error in .Net.
Try this:
var statusCode = HttpStatusCode.InternalServerError;
var response = new HttpResponseMessage(statusCode)
{
Content = new ObjectContent<object>(
new
{
Message = "Error Message",
ExceptionMessage = "StackTrace"
},
new JsonMediaTypeFormatter())
};
throw new HttpResponseException(response);
Or if it does not work try this: https://stackoverflow.com/a/28589333/8758483
Another good idea is to centralize your error handling by implementing an ErrorHandler in Angular.
import { ErrorHandler } from '#angular/core';
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error) {
// your custom error handling logic
}
}
Then you tell Angular you want to use this class instead of the default one by providing it in you NgModule:
#NgModule({
providers: [{provide: ErrorHandler, useClass: GlobalErrorHandler}]
})
If you want more detail you can read this article:
Expecting the Unexpected — Best practices for Error handling in Angular
In the response the property is called "Message" and not "message".

Angular2 : TypeError 'this.' is undefined

I wrote a function that gets and shows the "best player" from my array of objects (the player who has most likes). The function works fine and shows me what I want, but the browser shows me errors in the console, and the routing in my app is blocked (I can't navigate between components using routes)
this is my DashboardComponent Class
export class DashboardComponent implements OnInit {
bestPlayer:Player;
data:Player[]=[];
max:number =0;
constructor(private playerService : PlayerService ,private router: Router) { }
ngOnInit() {
this.playerService.getData().then(data => {
this.data = data;
for (var i = 0; i <= this.data.length; i++) {
if (this.data[i].likes>this.max) {
this.max=this.data[i].likes;
this.bestPlayer=this.data[i];
}
}
});
}
viewDetails(bestPlayer: Player):void {
this.router.navigate(['/detail',this.bestPlayer.id]);
}
}
This is my service:
import {Player} from './player';
import {PlayersData} from './muck-players';
import { Injectable } from '#angular/core';
#Injectable()
export class PlayerService {
players:any;
data:any;
Player:Player;
getData():Promise <Player[]> {
return Promise.resolve(PlayersData);
}
}
when I run the app the browser show me those errors :
TypeError: this.data[i] is undefined
Error: Uncaught (in promise): Error: Cannot activate an already activated outlet
Error: Uncaught (in promise): Error: Cannot activate an already activated outlet
when I delete whats in ngOninit() and viewDetails() function, routing starts working again and browser doesn't show me errors.
Any help please !
As a sidenote, always when you provide a plunker, make sure it's a working one ;) When I got it working, there was only a couple of issue actually. There was nothing wrong with your routing. The first error you were getting
this.data[i] is undefined
is because of your for loop, you had marked that we should loop until i matches the length of the array (or equal). But we need to remember that the index of arrays start with 0. So the last iteration it was trying to read an index that was not present in your array. So you should add -1 to your for loop:
for (var i = 0; i <= this.data.length-1; i++)
or do
for (var i = 0; i < this.data.length; i++)
When this was fixed, it produces new issues. Since data is coming async, so by the time the template is rendered, your variable bestPlayer is undefined. This can be fixed with safe navigation operator, or wrapping everything inside a div with the condition that bestPlayer has values. This need to be applied to both the detail page and the dashboard. With the dashboard:
<div *ngIf="bestPlayer">
<!-- code here -->
</div>
And in the detail page the same but using player instead, as that is the variable you are using there.
As mentioned, you can also use the safe navigation operator.
These actually cleared the second error you also had.
Here's your fixed PLUNKER.
I have fixed dozens of errors in the Plunker and added some missing routing features. The App is working just fine now please take a look at my forked Plunker over here
I fixed all the files paths and used in forEach method in your component just like this:
ngOnInit() {
this.playerService.getData().then(data => {
data.forEach( (arrData) => {
if (arrData.likes>this.max) {
this.max=arrData.likes;
this.bestPlayer=arrData;
}
})
});
}
Demonstration:
Hi the issue is with your service,
You are trying to loop through data that is not available,
You need to change the code ,
If you are using Observer as service then put you code inside .subscribe method,
If you are using promise then put your looping code inside .then() method.
Try to use this :
If you are returning promise from this.playerService.getData() this service
this.playerService.getData().then((data) => {
this.data = data;
for (var i = 0; i <= this.data.length; i++) {
if (this.data[i].likes>this.max) {
this.max=this.data[i].likes;
this.bestPlayer=this.data[i];
}
})
If you are returning observable from this.playerService.getData() this service
this.playerService.getData().subscribe((data) => {
this.data = data;
for (var i = 0; i <= this.data.length; i++) {
if (this.data[i].likes>this.max) {
this.max=this.data[i].likes;
this.bestPlayer=this.data[i];
}
})

How do I cast a JSON object to a typescript class when properties doesn't match?

For example, I have a class:
export class SomeClass {
id: number;
name: string;
}
I receive JSON from server than looks like this
[{"Id":1,"Name":"typicalname"},{"Id":2,"Name":"somename"},{"Id":3,"Name":"blablabla"},{"Id":4,"Name":"lol"},{"Id":5,"Name":"lil"},{"Id":6,"Name":"lal"}]
How do I cast a JSON object to a typescript class when properties doesn't match?
That's how I do it wright now, and it's not working.
getSomeClass() {
return this.http.get(this.someClassUrl)
.map(response => <SomeClass[]>response.json())
.catch(this.handleError);
}
try this:
getSomeClass() {
return this.http.get(this.someClassUrl)
.map(response => {
let json = response.json();
return json.map(m => {
return {
id: json.Id,
name: json.Name
}
}
})
.catch(this.handleError);
}
When you have a type T and a value x and you write <T>x you are not performing a cast in a runtime sense. You are performing a type assertion. What this means is that you are telling TypeScript that the type of x is T.
In this particular case, if response.json() returns a value typed as any, which is not unreasonable for a deserialization operation, then <T>response.json() will be accepted by the TypeScript compiler for any T. This is because the type any is compatible with (technically assignable to) everything.
However in this case you want to verify the shape of the response and the compiler cannot do this for you. You need to write a validation algorithm that is appropriate.
What is appropriate will depend on the domain of your application, and may be non-trivial, but here is an example. Unfortunately, since your question implies Angular 2 and RxJS, even a simple applicable answer contains a fair amount of incidental complexity.
import {Http} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
function injected(_) {} // emit decorator metadata with flag (not pertinent)
#injected export class SomeService {
constructor(readonly http: Http) {}
getSomeValue(): Observable<Expected> {
return this.http.get(this.someResourceUrl)
.catch(handleError)
.mergeMap(response => {
const deserialized = response.json();
if (isExpected(deserialized)) {
// note the type of derserialized is Expected in this block
return Observable.of(deserialized);
}
return Observable.throw('response data did not have the expected shape');
});
}
}
export interface Expected {
id: number;
name: string;
}
function isExpected(deserialized : any): deserialized is Expected {
return typeof deserialized.id === 'number' && typeof deserialized.name === 'string';
}
function handleError(error) { // this is not really necessary, but was in the question
console.error(error); // log
return Observable.throw(error); // rethrow.
}
The most significant thing here is the isExpected function.
It takes a value of any type, validates it based on our criteria, and states that if it returns true, then the given value was indeed of the expected type, Expected.
What does it mean to be of the expected type?
Well our isExpected function determines that, and provides this information to the TypeScript language by way of its return type which says that if the function returns true, then the value passed to it is of type Expected.
This is known as a User Defined Type Guard function and you can read more about it at https://www.typescriptlang.org/docs/handbook/advanced-types.html.

Angular 2 sees my Json object as an function

I have a webapp that gets via Json stuff put it in objects to display them.
I already did it two times with services and classes.
But now i copy and paste the old code make some slight changes to make sure it redirect to the good classes but now i get an array with functions instead an array with objects.
Here is my constructor that calls upon the the service classes and send things to the console
constructor(private featureService : FeatureService, private scenarioservice : ScenarioService, private failuresService : FailuresService){
//hier worden de features en failures opgehaald
this.featureService.getFeatures().subscribe(res => {
this.featureArray = res.getFeatureArray();
console.log(res.getFeatureArray());
});
this.failuresService.getFailures().subscribe(res => {
this.failureArray = res.getFailureArray();
console.log(res.failures[0].method);
console.log(this.failureArray);
});
}
}
Here is failuresService.getFailures:
import { Injectable } from "#angular/core";
import {Http, Response} from "#angular/http";
import {Failure} from "./Failure";
import {Failures} from "./failures";
#Injectable()
export class FailuresService {
constructor(protected http: Http) {}
getFailures() {
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => response.json())
.map(({failures = [Failure]}) => new Failures(failures));// deze error is bullshit
}
}
This is the Json that I get and want to get in an class:
{
"failures:": [
{
"method": "canRollOneGame",
"object": "BowlingGame.GameTest"
},
{
"method": "canCountNighteeneight",
"object": "FizzBuzz.FizzBuzzTest"
}
]
}
Here are the Failure and Failures classes:
import {Failure} from "./Failure";
export class Failures {
constructor(public failures : Failure[]){}
public getFailureArray(): Failure[]{
return this.failures;
}
}
export class Failure{
constructor(public method : String , public object : String ){ }
}
I could be wrong but this doesn't look right:
getFailures() {
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => response.json())
.map(({failures = [Failure]}) => new Failures(failures));// deze error is bullshit
}
That last mapping, I'm not sure what it is supposed to do. I've never seen that syntax (doesn't mean it's wrong). I get that you have an incoming array. But what this syntax does {failures = [Failure]} is what I think your problem is. I don't think that will do what you think.
Try this and see what happens:
.map(failures => { console.log ( failures ); new Failures(failures) } );// deze error is bullshit
If you try that with your old mapping, and then this new one, it'd be interesting to see what that console.log traces out. I think doing it your way, you won't see what you expect.
If that sorts you out, then you can try typing the incoming payload (though I'm not sure it'll work; it should be receiving JSON from the previous mapping).
.map( (failures: Array<Failure>) => { console.log ( failures ); new Failures(failures) } );
So if I read it correctly: Your goal is to create an array of Failure objects with the values coming from a JSON array where each of this has a method you'd like to execute somewhere in the code.
If thats the case then I understand what went wrong. As Tim Consolazion mentioned in the comments you only get the values for the Failure object not the Failure object itself (See custom created method error: "is not a function"). So in order to execute the commands from Failure you have to create for each object in your JSON array a Failure object. Something like this:
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => {
let content = response.json();
let failureList: Failure[] = [];
content.forEach(failure => {
failureList.push(new Failure(failure.method, failure.object))
});
return failureList || {};
}
What I wonder is where you found {failures = [Failure]}? I've never seen this before and I've no idea what this is for.
EDIT: I edited the question so it fits your class Failure. The failure in content.forEach(failure => { in an object from your JSON array. You can access the values of it like it is a normal object. In your case with failure.method and failure.object.
I found my problem in the Json the name was function: but in my code it was function so all that was necessary was to make sure it sends function instead of function: