无法将从 http Get 返回的数据分配给具有相同结构的打字稿对象

Unable to assign data returned from http Get to typescript object with same structure

我正在从一个组件调用一个 http Get 服务方法,目的是将响应映射回我的组件中的一个 Person 对象,以便在前端显示。

我的组件:

export class DisplayPersonComponent implements OnInit {

  personId: number;
  person: Person;
  
  constructor(private route: ActivatedRoute, private service : PersonService) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
       this.personId = params['person-id']});    
       
       this.getPerson(this.personId);
  }

  
    getPerson(id: number)
    {
        this.service.getPerson(id).subscribe((data: Person) => { console.log(data); 
          this.person = data
         });
    }
}

我的服务方式:

getPerson(personId : number): Observable<Person> {

    let urlParams = new HttpParams().set("personId", personId.toString());

    return this.http.get<Person>(this.apiUrl, { params:  urlParams})
      .pipe(map((data: Person ) => { return data }),
        catchError(error => { return throwError(' Something went wrong! ');
        })
      );
  }
}

我可以检查组件中的数据对象,当它 returns 看起来像 json 即 { PersonID: 1, 姓名: 'Name'} 等 但是 this.Person 总是未定义的,并且没有 output/error 来解释原因。 还值得一提的是,我对 POST 方法使用了相同的对象,它工作正常并且在没有任何指定映射的情况下完美地从客户端映射到服务器。

Observable 是 async,在您评估 person 时,尚未调用在 observable 完成时调用的 subscribe 方法。

我不知道你在哪里使用 person 对象,但你似乎需要重新考虑一下你的逻辑。

幸运的是,angular 有异步管道可以与 *ngIf 一起使用:

<div *ngIf="(getPerson(personId) | async) as person">

此管道接受一个可观察对象并将其“解析”为 html 模板中的 person 对象。它也只在这个请求完成后渲染它里面的东西,所以你可以在里面自由使用对象的属性!

来源和更多信息: https://ultimatecourses.com/blog/angular-ngif-async-pipe

您的服务方法returns 是可观察的,而不是直接的人。 因此,如果您在 observable 实际返回一个对象之前检查 person 对象,那么它将是未定义的。 这不是错误,这就是异步代码的工作方式。

在您的标记中,只需确保在渲染之前检查对象是否未定义即可:

<div *ngIf="person">...</div>

您的 POST 方法之所以有效,是因为您实际上可能没有对响应做任何事情。例如,如果您要在 POST 成功时显示吐司通知,那么同样的原则将适用,您将必须观察 POST 响应并在 success/error 上做一些事情。

变量this.personId被异步赋值。所以当 getPerson() 被调用时,它仍然是未定义的。

您可以使用 RxJS 高阶映射运算符(如 switchMap 来从一个可观察对象映射到另一个对象,而不是嵌套订阅。

ngOnInit() {
  this.route.params.pipe(
    switchMap(params => this.service.getPerson(params['person-id']))
  ).subscribe((data: Person) => {
    console.log(data);
    this.person = data;
  })
}

选项 2:async 管道

如果您没有在控制器中使用 this.person,您可以跳过那里的订阅并在模板中使用 async 管道

控制器

import { Observable } from 'rxjs';

export class DisplayPersonComponent implements OnInit {
  person$: Observable<Person>; // <-- type `Observable`
  
  constructor(private route: ActivatedRoute, private service : PersonService) { }

  ngOnInit() {
    this.person$ = this.route.params.pipe(
      switchMap(params => this.service.getPerson(params['person-id']))
    );
  }
}

模板

<ng-container *ngIf="(person$ | async) as person">
  {{ person }}
  <some-comp [person]="person">...</some-comp>
</ng-container>

更新:Cannot read property

抛出错误是因为 person 变量在订阅中被赋值之前是未定义的。使用 async 的第二个选项应该可以缓解这个问题。在任何情况下,您都可以使用安全导航运算符 ?. 在尝试访问变量的属性之前检查变量是否已定义。

<div>
  {{ person?.personId }}
  {{ person?.someProp }}
  {{ person?.someOtherProp }}
</div>

您可以了解有关异步数据的更多信息here