我一直在阅读有关MEAN Stack的书,其中的任务是创建一个全栈Angular应用程序,该应用程序检索主页上的(咖啡馆)位置和数组。用户可以通过本地存储令牌登录和注销应用程序。通过http://localhost:7777/location/<location-id>
的各个位置将显示各个位置以及Google地图(基于纬度/经度坐标),用户评论,开放/关闭时间以及与每个位置实例相关的其他相关信息。我面临的问题是我无法通过带有*ngFor
的基本'/'路由(即HomepageComponent)来显示locations数组。注意:与检索位置数组有关的所有相关代码都位于其自己的名为HomeListComponent
的组件中,该组件又嵌入HomepageComponent
中。
我进入了MongoDb数据库的本地实例,以查询位置列表,以便可以检索每个位置的ID。然后,我在Postman中对(EX)http://localhost:7777/location/5970dc1215504ef93ad88919
进行了一个简单的GET请求,以查看是否已针对各个位置正确检索了数据。在成功查看通过Postman返回的数据后,我将上面的相同URL复制/粘贴到了浏览器中,并且确实可以通过/location/
路由在浏览器中看到渲染到前端的数据。我的目的是排除可能由于后端路由/架构形成而导致的所有错误/错误。
该问题似乎与如何将Angular前端中的getLocations()
函数绑定(通过绑定)到getPosition()
方法有关,但是我不能肯定地说(我不是有角的退伍军人)。当我在浏览器中转到http://localhost:7777/
并打开开发工具以找到<app-home-list>
时,我看到绑定没有返回任何内容:
bindings={
"ng-reflect-ng-for-of": ""
}
我还试图在HomeListComponent
中给app-routing-module
分配自己的路由(例如,以便我可以查看/测试数据嵌入方式是否存在问题:>
// {
// path: 'locations',
// component: HomeListComponent
// },
...但是那不能解决问题。
我正在使用Node v11.6.0 + Angular v8。
这是后端的基本路线:
router.get('/', ctrlLocations.homelist);
..以及控制器/辅助功能homelist
:
const homelist = (req, res) => {
const path = `/api/locations`;
const requestOptions = {
url: `${apiOptions.server}${path}`,
method: 'GET',
json: {},
qs: {
lng: -116.715256,
lat: 33.746747,
maxDistance: 20000
}
};
request(
requestOptions,
(err, {statusCode}, body) => {
let data = [];
if (statusCode === 200 && body.length) {
data = body.map( (item) => {
item.distance = formatDistance(item.distance);
return item;
});
}
renderHomepage(req, res, data);
}
);
};
前端/角度:
location.ts
:
class OpeningTimes {
days: string;
opening: string;
closing: string;
closed: boolean;
}
export class Review {
author: string;
rating: number;
reviewText: string;
}
export class Location {
_id: string;
name: string;
distance: number;
address: string;
rating: number;
facilities: string[];
reviews: Review[];
coords: number[];
openingTimes: OpeningTimes[];
}
geolocation.service.ts
:
public getPosition(cbSuccess, cbError, cbNoGeo): void {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(cbSuccess, cbError);
} else {
cbNoGeo();
}
}
distance.pipe.ts
:
export class DistancePipe implements PipeTransform {
transform(distance: number): string {
const isNumeric = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
if (distance && isNumeric(distance)) {
let thisDistance = '0';
let unit = 'm';
if (distance > 1000) {
thisDistance = (distance / 1000).toFixed(1);
unit = 'km';
} else {
thisDistance = Math.floor(distance).toString();
}
return thisDistance + unit;
} else {
return '?';
}
}
}
coffeebuzz-data.service.ts
:
export class CoffeeBuzzDataService {
constructor(
private http: HttpClient,
@Inject(BROWSER_STORAGE) private storage: Storage) { }
private apiBaseUrl = 'http://localhost:7777/api';
public getLocations(lat: number, lng: number): Promise<Location[]> {
const maxDistance: number = 20000;
const url: string = `${this.apiBaseUrl}/locations?lng=${lng}&lat=${lat}&maxDistance=${maxDistance}`;
return this.http
.get(url)
.toPromise()
.then(response => response as Location[])
.catch(this.handleError);
}
public getLocationById(locationId: string): Promise<Location> {
const url: string = `${this.apiBaseUrl}/locations/${locationId}`;
return this.http
.get(url)
.toPromise()
.then(response => response as Location)
.catch(this.handleError);
}
public addReviewByLocationId(locationId: string, formData: Review): Promise<Review> {
const url: string = `${this.apiBaseUrl}/locations/${locationId}/reviews`;
const httpOptions = {
headers: new HttpHeaders({
'Authorization': `Bearer ${this.storage.getItem('CoffeeBuzz-token')}`
})
};
return this.http
.post(url, formData, httpOptions)
.toPromise()
.then(response => response as Review)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('Something has gone wrong', error);
return Promise.reject(error.message || error);
}
public login(user: User): Promise<AuthResponse> {
return this.makeAuthApiCall('login', user);
}
public register(user: User): Promise<AuthResponse> {
return this.makeAuthApiCall('register', user);
}
private makeAuthApiCall(urlPath: string, user: User): Promise<AuthResponse> {
const url: string = `${this.apiBaseUrl}/${urlPath}`;
return this.http
.post(url, user)
.toPromise()
.then(response => response as AuthResponse)
.catch(this.handleError);
}
}
home-list.component.ts
:
export class HomeListComponent implements OnInit {
constructor(
private coffeeBuzzDataService: CoffeeBuzzDataService,
private geoLocationService: GeolocationService
) { }
public locations: Location[] = [];
public message: string;
ngOnInit() {
this.getPosition();
}
private getPosition(): void {
this.message = 'Getting your location...';
this.geoLocationService.getPosition(
this.getLocations.bind(this),
this.showError.bind(this),
this.noGeo.bind(this));
}
private getLocations(position: any): void {
this.message = 'Searching for nearby places';
const lat: number = position.coords.latitude;
const lng: number = position.coords.longitude;
this.coffeeBuzzDataService
.getLocations(lat, lng)
.then(foundLocations => {
this.message = foundLocations.length > 0 ? '' : 'No locations found';
this.locations = foundLocations;
});
}
private showError(error: any): void {
this.message = error.message;
}
private noGeo(): void {
this.message = 'Geolocation not supported by this browser';
}
}
home-list.component.html
:
<div class="card" *ngFor="let location of locations">
<div class="card-block">
<h4>
<a routerLink="/location/{{location._id}}">{{location.name}}</a>
<small>
<i class="fa{{ location.rating < 1 ? 'r' : 's' }} fa-star"></i>
<i class="fa{{ location.rating < 2 ? 'r' : 's' }} fa-star"></i>
<i class="fa{{ location.rating < 3 ? 'r' : 's' }} fa-star"></i>
<i class="fa{{ location.rating < 4 ? 'r' : 's' }} fa-star"></i>
<i class="fa{{ location.rating < 5 ? 'r' : 's' }} fa-star"></i>
</small>
<span class="badge badge-pill badge-default float-right">{{location.distance | distance }}</span>
</h4>
<p class="address">{{location.address}}</p>
<div class="facilities">
<span *ngFor="let facility of location.facilities" class="badge badge-warning">{{facility}}</span>
</div>
</div>
</div>
..最后...
homepage.component.html
<app-page-header [content]="pageContent.header"></app-page-header>
<div class="row">
<div class="col-12 col-md-8">
<div class="error"></div>
<app-home-list></app-home-list>
</div>
<app-sidebar [content]="pageContent.sidebar" class="col-12 col-md-4"></app-sidebar>
</div>
我几乎忘了提到我通过家庭/基本路由在后端获得200的状态代码,而没有这样的错误,再次使我相信我无法返回页面上的数据数组加载是Angular绑定问题:
GET / 200 5.431 ms - 1108
在此先感谢任何可以提供一些建议以解决此问题的人。我(诚然)已经对此卡住了一段时间。
干杯:-)