您好我有一个关于使用HTTP服务的快速问题:
我想知道这是否是正确的做法。
任务
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); }
)
}
}
模板
<input type="text" class="form-control" id="title" placeholder="Title"[(ngModel)]="task.Title" name="title">
我不确定这是不是现在的方式...我仍然有错误:“ERROR TypeError:无法读取未定义的属性'标题'”,除非我在我的组件中替换“任务:任务”在“task:{}”中......我是否必须使用界面?
答案 0 :(得分:1)
您收到该错误的原因是您在可观察完成之前尝试访问未定义的Title属性。为防止这种情况,您应该使用ngIf:
<input *ngIf="task" type="text" class="form-control" id="title" placeholder="Title"[(ngModel)]="task.Title" name="title">
通过这种方式,你的元素不会尝试渲染,直到任务成为真正的价值。
但是,在我看来,最好的做法是始终使用异步管道,因为它会为您处理订阅管理,并促进onPush更改检测等内容,并使代码更清晰,更易读:
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">
你给异步一个observable并且它为你订阅并清理所有内容以防止内存泄漏(尽管在这种特定情况下这不会成为问题)。这样可以更清楚地了解代码中发生了什么,以及何时以及为什么,正如您所看到的,此更改会使代码减少很多(相对而言)。它还允许您将更改检测切换为on push,这是一个巨大的应用程序性能助推器。
答案 1 :(得分:0)
我会以这种方式实例化对象:
task = <Task>{};
这样它将是空的或填充的,但不是未定义的。
答案 2 :(得分:0)
我认为你在服务中遗忘了.json():
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
});
}
}
如果您不这样做,结果是一个Response对象,其中包含有关请求的更多信息(如statusCode,body,...)https://developer.mozilla.org/en-US/docs/Web/API/Response
进一步
您在应用程序中使用结果结构,但效果不是很好。您将代码耦合到API。
您最好有一个界面来投射结果,就像您在代码中没有任何类型一样。这个界面是DTO。
在您的代码中执行此操作后,您必须拥有自己的对象而不是您可以控制的对象。
在您的服务中,您可以拥有一个映射器:
@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)
}
}
答案 3 :(得分:0)
有两件事情导致它无效:
1。)界面没有发生在JS中。如果查看生成的JavaScript代码,您将看不到所使用的接口。 (JavaScript根本不支持它们)意味着在编译代码后,您的任务属性仍未定义。
2 /)由于您的属性不是有效对象,因此它不会绑定到您的元素,因此即使您的http请求返回有效响应,它也会不更新内容。
现在解决方案相当简单,要么将空对象分配给任务属性...要么创建一个任务类,它将实现您的接口并包含具有一些默认值的属性(null或&#34;& #34;值取决于属性类型。)。然后在初始化组件时实例化它。
(下面可能在语法上不正确,但是你收到了消息)
interface iTask{
title: string
}
class Task implements iTask{
title: ""
}
export class TaskComponent implements OnInit {
task: Task;
constructor(){
this.task = new Task();
}
}