0

Hello I have a quick question about using HTTP service :

I would like to know if this is the right way to do.

Task

export interface Task { Title: string; AssignedTo: User; TaskStatus: string; } 

TaskService

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import 'rxjs/add/operator/map'; import { environment } from '../../environments/environment'; import { Task } from '../interfaces/task' @Injectable() export class TaskService { constructor(private http: HttpClient) { } getTask(id: number): Observable<Task> { const serviceUrl: string = environment.apiUrl + 'Tasks/' + id; return this.http.get(serviceUrl) .map((res: Task) => { return res; }); } } 

TaskComponent

export class TaskComponent implements OnInit { task: Task; constructor( private taskService: TaskService ) { } ngOnInit() { this.route.params.subscribe(params => { this.getTask(<number>params.id); }); } getTask(id: number) { this.taskService.getTask(id).subscribe( res => { this.task = res; console.log(this.task) }, err => { console.log(err); } ) } } 

template

<input type="text" class="form-control" id="title" placeholder="Title"[(ngModel)]="task.Title" name="title"> 

I'm not sure if that's the way to go now... I still have errors like: "ERROR TypeError: Cannot read property 'Title' of undefined", Except when I replace in my component "Task: Task" in "task: {}" ... Do I have to use an interface?

0

4 Answers 4

1

you're getting that error because you're trying to access the Title property of something that is undefined until the observable finishes. To guard against this, you should use ngIf:

<input *ngIf="task" type="text" class="form-control" id="title" placeholder="Title"[(ngModel)]="task.Title" name="title"> 

this way, your element won't attempt to render until task is a truthy value.

But best practice though, in my opinion, is to use the async pipe always, as this handles subscription management for you and facilitates things like onPush change detection and makes cleaner, more readable code:

export class TaskComponent implements OnInit { task$: Observable<Task>; constructor( private taskService: TaskService ) { } ngOnInit() { this.task$ = this.route.params.switchMap(p => this.taskService.getTask(<number>p.id)); } } <input *ngIf="task$ | async as task" type="text" class="form-control" id="title" placeholder="Title"[(ngModel)]="task.Title" name="title"> 

you give async an observable and it subscribes for you and cleans everything up to prevent memory leaks (though that wouldn't be a problem in this specific situation). This makes it more clear what is happening in your code and when and why, as you can see, this change reduced your code quite a bit (in relative terms). It also does allow for you to switch change detection to on push, which is a huge app performance booster.

Sign up to request clarification or add additional context in comments.

Comments

0

I would instance the object this way:

task = <Task>{}; 

This way it will be or empty or filled, but not undefined.

Comments

0

I think you have forget .json() in your service:

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import 'rxjs/add/operator/map'; import { environment } from '../../environments/environment'; import { Task } from '../interfaces/task' @Injectable() export class TaskService { constructor(private http: HttpClient) { } getTask(id: number): Observable<Task> { const serviceUrl: string = environment.apiUrl + 'Tasks/' + id; return this.http.get(serviceUrl) .map((res: Task) => { return res.json(); //<-- here }); } } 

If you don't do this, the result is an Response object with more information about the request (like statusCode, body, ...) https://developer.mozilla.org/en-US/docs/Web/API/Response

For futher

You use your result structure in your application, it's not very good. You couple your code to the API.

You have a good idea to have an interface to cast the result, like this you don't have any type in your code. This interface it's a DTO.

After this in your code you must have your own object than you control.

And in your service you can have a mapper:

@Injectable() export class TaskService { constructor(private http: HttpClient) { } getTask(id: number): Observable<Task> { const serviceUrl: string = environment.apiUrl + 'Tasks/' + id; return this.http .get(serviceUrl) .map(res => this.mapper(res.json()); } private mapper(res: TaskDTO): MyOwnTask { return MyOwnTask(res.Title, res.User, res.TaskStatus) } } 

1 Comment

the behavior of the http client was changed in angular 5, no need for the json() calls anymore.
0

Well there are two things why it's not working:

1.) the interface isn't transpired into JS. If you look at the JavaScript code that’s generated you won’t see interfaces used at all. (JavaScript simply doesn’t support them) Meaning your task property remains undefined after transpiling your code.

2/) Since your property isn't a valid object, it wasn't bound to your element, so even if your http request gets back with a valid response , it will not update the content.

Now the solution is rather easy, either assign an empty object to the task property ... or create a task class which will implement your interface and contain properties with some default values ( null or "" values depending on the property type. ). Then instantiate it while while initializing your component.

(below may not be syntactically correct, but you get the message )

interface iTask{ title: string } class Task implements iTask{ title: "" } export class TaskComponent implements OnInit { task: Task; constructor(){ this.task = new Task(); } } 

1 Comment

the only reason it isn't working is because of item 2, it doesn't matter that the interface goes away once transpiled. JS doesn't care about it, all it cares about is that you're trying to access a property of an undefined reference.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.