1

我知道这是一个非常简单的问题,但我还没有找到可以以完全有意义的方式工作或解释的资源解决方案。多年后我试图回到 Angular 并且之前从未使用过 TypeScript。目前正在为错误和 TypeScript 实际期望我做的事情而苦苦挣扎。

我有一个连接到Open Brewery DB的应用程序。我正在尝试制作一个基于:idURL 参数获取数据的详细信息页面。

  • app.com/breweries,给我一份啤酒厂的清单
  • app.com/breweries/:id,给我关于那家啤酒厂的具体细节

我有一个获取 Breweries 列表的列表组件。因此,无论返回什么都会显示在列表中。

http.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { IBrewery } from './brewery/brewery';

@Injectable({
  providedIn: 'root'
})
export class HttpService {

    constructor(private http: HttpClient) { }

    getBreweries() {
        return this.http.get('https://api.openbrewerydb.org/breweries');
    }

    getBrewery(id) {
        return this.http.get<IBrewery[]>(`https://api.openbrewerydb.org/breweries/${id}`)
    }
}

list.component.ts

import { Component, OnInit } from '@angular/core';

import { HttpService } from '../http.service';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {

    breweries: Object;

  constructor(private _http: HttpService) { }

  ngOnInit(): void {
        this._http.getBreweries().subscribe(data => {
            this.breweries = data;
        });
  }

}

list.component.html

<h1>Breweries</h1>

<ul *ngIf="breweries">
    <li *ngFor="let brewery of breweries">
        <p class="name">{{ brewery.name }}</p>
        <p class="country">{{ brewery.country}}</p>
        <a href="{{brewery.website_url}}" class="site" target="_blank">Visit Website</a>
    </li>
</ul>

所以所有这一切都没有错误一切似乎都很好......然后是配置文件和事情发生故障的地方。

brewery.component.ts

import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';

import { HttpService } from '../http.service';

@Component({
  selector: 'app-brewery',
  templateUrl: './brewery.component.html',
  styleUrls: ['./brewery.component.scss']
})

export class BreweryComponent implements OnInit {

    brewery: object = {};
    breweryId: string;

  constructor(private _http: HttpService, private activatedRoute: ActivatedRoute) { }

  ngOnInit(): void {
        this.breweryId = this.activatedRoute.snapshot.params.id;
        this._http.getBrewery(this.breweryId).subscribe(data => {
            this.brewery = data;
        })
    }

}

brewery.component.html

<ul *ngIf="brewery">
    <li>
        {{brewery.name}}
    </li>
    <li>
        {{brewery.city}}, {{brewery.state}}
    </li>
</ul>

啤酒厂.ts

export interface IBrewery {
    name: string,
    city: string,
    state: string
};

我得到的错误是: - ERROR in src/app/brewery/brewery.component.html:7:13 - error TS2339: Property 'name' does not exist on type 'object'. - Error occurs in the template of component BreweryComponent. src/app/brewery/brewery.component.html:10:13 - error TS2339: Property 'city' does not exist on type 'object'. -Error occurs in the template of component BreweryComponent. src/app/brewery/brewery.component.html:10:31 - error TS2339: Property 'state' does not exist on type 'object'.

所以我认为的问题是,brewery在我可以在组件模板中声明它们之前,需要分配与这些属性关联的属性和类型。如果这是真的,对于我的生活,我无法弄清楚我应该如何或在哪里IBrewery正确使用它。我已经看到了servicemycomponent.component.ts文件中使用它的示例。在任何一种情况下,如何解决问题都非常清楚。

4

2 回答 2

1

简短答案:使用安全导航运算符

如下更新您的 html。

<ul *ngIf="brewery">
    <li>
        {{brewery?.name}}
    </li>
    <li>
        {{brewery?.city}}, {{brewery?.state}}
    </li>
</ul>

更好的方法:使用加载微调器。

<div *ngIf="loading">
  some loading spinner
</div>

<div *ngIf="!loading">
    <li>
        {{brewery?.name}}
    </li>
    <li>
        {{brewery?.city}}, {{brewery?.state}}
    </li>
</ul>


export class BreweryComponent implements OnInit {

    brewery; // be default type will be any. 
    breweryId: string;
    loading = false; // loading spinner.

    constructor(private _http: HttpService, 
               private activatedRoute: ActivatedRoute) { }

    ngOnInit(): void {

        this.breweryId = this.activatedRoute.snapshot.params.id;
        this.get();

    }

    get() {

        this.loading = true;
        this._http.getBrewery(this.breweryId)
             .subscribe(data => {

            this.brewery = data;
            this.loading = false; // loading spinner hidden.

        }, (error) => {

            // handle error;

        });

    }

}
于 2020-05-21T01:55:17.937 回答
0

首先,您应该在服务中正确输入。它应该如下所示:

getBreweries() {
    return this.http.get<IBrewery[]>('https://api.openbrewerydb.org/breweries');
}

getBrewery(id) {
    return this.http.get<IBrewery>(`https://api.openbrewerydb.org/breweries/${id}`)
}

如您所见,我将预期类型添加到getBreweries并更改了预期类型getBrewery(id)。我不确定为什么IBrewery[]之前设置它,因为你告诉我们它应该只提供啤酒厂的一个具体细节。

现在,当您订阅这些时,subscibe 函数内的参数将被推断为您在gettype 参数中设置的类型。因此,最好将组件实例变量的类型也设置为该类型,如下所示:

export class ListComponent implements OnInit {

    breweries: IBrewery[];
    ...
}

export class BreweryComponent implements OnInit {

    brewery: IBrewery;
    ...
}

通常,您不想使用类型objector Object,因为它不会告诉您有关类型结构的任何信息。如果你不知道你的类型的确切结构或者懒得创建接口,你应该使用any.

顺便说一句,ListComponent 开始工作的原因有点幸运。出于某种原因,该let x of y语法是允许y类型object的,并且似乎x被推断为 as any,因此您可以编写任何您想要的内容而不会出错。重要的是要理解 typescript 在运行时不会改变任何东西,所以无论你的 typescript 类型说什么,运行时类型都将是它们的任何内容。

于 2020-05-21T11:05:39.950 回答