Angular 4+最佳实践HTTP服务

时间:2017-11-16 14:17:33

标签: angular angular-services angular-http

您好我有一个关于使用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:{}”中......我是否必须使用界面?

4 个答案:

答案 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();
    }
}