我从API-Call获取数据。我有一种多对多的关系——我会在电影中向人们解释这一点。一部电影被很多人看,一个人可以看很多电影。因此,在Angular Frontend中,当你点击一个人时,你应该会看到这个人的更详细的视图,包括观看的电影列表。
当我运行此代码(person-detail.component.ts)时:
public selectedperson: any = []
/*Array that contains data from the selected person*/
public movie: any = []
/*Array that contains all the movie-person connections (movie-name and person_id)*/
public selectedmovie: any = []
/*array that should contain all the movies the selectedperson has watched*/
/*some code*/
getMovies(){
const url ='http://localhost:4000/api/people-movies';
this.http.get(url).subscribe(movie => {
this.movie = movie;
this.selectedmovie=this.movie.find(item=>
{
for (var i = 0; i < this.movie.arrayLength; i++) {
if(item['person_id']===this.selectedperson.person_id)
{
return true;
}
return false;
}
});
console.log(this.selectedmovie, this.selectedperson.person_id);
return this.selectedmovie;
});
}
它不起作用,但没有这个循环(如果我离开if语句):
for (var i = 0; i < this.movie.arrayLength; i++) {
}
它只返回这个人的一部电影,所以它一找到电影就停止。但既然我想列出所有的电影,我需要循环。
那么我在循环中做错了什么呢?
编辑:selectedperson中的示例数据(已填充):选定人员:
[{"person_id"=34, "name"="john"}]
电影(从API调用中获取上述URL的数据):
[{"person_id"=33, "movie"="Titanic"}{"person_id"=33, "movie"="Star Wars"}{"person_id"=34, "movie"="Titanic"}{"person_id"=34, "movie"="Star Wars"}{"person_id"=34, "movie"="Indiana Jones"}{"person_id"=35, "movie"="Titanic"}]
想要的最终结果(对于id为34的人)
Titanic
Star Wars
Indiana Jones
您的代码有一些问题,但我将从我认为适合您的解决方案开始:
getMovies(){
const url ='http://localhost:4000/api/people-movies';
this.http.get(url).subscribe(movie => {
this.movie = movie;
this.selectedmovie=this.movie.filter(item=> item['person_id'] === this.selectedperson.person_id );
console.log(this.selectedmovie, this.selectedperson.person_id);
return this.selectedmovie.map(movie.movie);
});
}
现在,你的代码出了什么问题:
您试图通过在自身上激活
find
来填充空数组selectedmovie
,但它是空的。。。相反,用movie
数组中的电影填充它。您使用的是
find
,简单地看一下文档(这里)就会发现它只返回满足测试函数的第一个元素。相反,使用了filter
,它有点相同,但会遍历整个数组并返回所有满足测试的对象。你在不使用
i
的情况下创建了一个for循环——这是一个很强的迹象,表明你实际上并不需要那个循环。当整个检查是一个布尔表达式时,不需要返回"true"或"false",而是内联返回。
只是一方面不是,你的各种各样的名字真的很难跟上。例如,包含movies的数组最好称为"movies",当您使用数组函数对其进行迭代时,每个元素都可以称为"movie"。两者使用相同的名称实在令人困惑。同时尝试使用驼色大小写表示法(selectedMovie),可以使名称更清晰。
**注意:我的解决方案只会按照您想要的方式从"getMovies"返回数组,因为.map
被链接在返回值上。如果您希望selectedmovie
只填充电影名称作为字符串,而不是整个movie-prosn_id对象,只需将.map
链接到filter
,如下所示:
this.selectedmovie=this.movie.filter(item=> item['person_id'] === this.selectedperson.person_id ).map(movie => movie.movie);
希望有帮助:)
您可以获得如下所需的结果:
const movie = [
{"person_id":33, "movie":"Titanic"},
{"person_id":33, "movie":"Star Wars"},
{"person_id":34, "movie":"Titanic"},
{"person_id":34, "movie":"Star Wars"},
{"person_id":34, "movie":"Indiana Jones"},
{"person_id":35, "movie":"Titanic"}
];
let selectedperson = [{"person_id":34, "name":"john"}];
let moviesByPerson = movie.filter(f=>
selectedperson.some(s=> f['person_id'] == s['person_id']));
console.log(`moviesByPerson: `, moviesByPerson);
在您的代码中:
getMovies(){
const url ='http://localhost:4000/api/people-movies';
this.http.get(url).subscribe(movie => {
this.movie = movie;
this.selectedmovie = movie.filter(f=>
selectedperson.some(s=> f['person_id'] == s['person_id']));
console.log(this.selectedmovie, this.selectedperson.person_id);
return this.selectedmovie;
});
}
此外,尝试将HTTP调用转移到服务中,因为分离关注点是一种很好的做法。正如Angular文档所说:
组件不应该直接获取或保存数据,当然不应该故意提供虚假数据。他们应该专注于展示数据和将数据访问委托给服务。
您可以这样尝试。
this.selectedmovie = this.selectedmovie.filter(item => {
for (var i = 0; i < this.movie.arrayLength; i++) {
if (item['person_id'] === this.selectedperson.person_id) {
return true;
}
}
return false;
});
注意:Javascript array.fund方法返回所提供数组中满足所提供测试函数的第一个元素的值。
有些问题,如前所述,您正在使用find
,它将只返回第一个匹配(如果存在)。您正在查找filter
以筛选与id匹配的电影。此外,您正在尝试从subscribe
返回,但这不起作用。我也会稍微改变一下你的做法不要使用any
,它违背了类型脚本的目的。因此,键入您的数据,例如使用接口,这将对您有所帮助,因为编译器可以告诉您所做的事情是否与您的模型不符,这将使调试更加容易!
此外,我会将所有http请求移动到一个服务,组件只订阅该http请求的结果。总之,我建议如下:
interface Movie {
person_id: number;
movie: string;
}
interface Person {
person_id: number;
name: string;
}
服务:
import { map } from "rxjs/operators";
// ...
getFilteredMovies(id: number) {
const url ='http://localhost:4000/api/people-movies';
return this.http.get<Movie[]>(url).pipe(
map((movies: Movie[]) => {
return movies.filter((m: Movie) => m.person_id === id)
})
)
}
组件:
movies = <Movie[]>[];
selectedPerson = <Person>{ person_id: 34, name: "john" };
constructor(private myService: MyService) {}
ngOnInit() {
this.myService
.getFilteredMovies(this.selectedPerson.person_id)
.subscribe((data: Movie[]) => {
this.movies = data;
});
}
因此,我们已经在服务中处理过滤,并且组件只订阅IF如果您在其他地方调用此函数,而不希望执行筛选,则改为在组件中执行筛选。
使用上面的代码堆叠BLITZ演示。
我终于解决了它-感谢Serveryone的帮助,尤其是@Gibor!以下是针对面临类似问题的人的代码:然而,要知道,这个代码绝非完美,必须在许多方面进行改进(例如,使用循环查找所有电影)。我只是把它贴在这里,希望它能帮助有类似问题的人。。。组件.ts:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-people-detail',
templateUrl: './people-detail.component.html',
styleUrls: ['./people-detail.component.css']
})
export class PeopleDetailComponent implements OnInit {
public data: any = []
public selectedperson: any = []
public movies: any = []
public moviesByPerson: any = []
constructor(
public http: HttpClient,
public route: ActivatedRoute) { }
getPeople()
{
const person_id = this.route.snapshot.paramMap.get('person_id')
const people_url ='http://localhost:4000/api/people'
const movies_url ='http://localhost:4000/api/person-movie'
this.http.get(people_url).subscribe
(data =>
{
this.data = data;
this.selectedperson=this.data.find
(item=>
{
if(item['person_id']===person_id)
{
return true;
}
return false;
}
)
this.http.get(movies_url).subscribe
(movies =>
{
this.movies = movies;
this.moviesByPerson=this.movies.filter(item => item.person_pk === this.selectedperson.person_pk)
}
)
console.log(this.selectedperson, this.moviesByPerson)
return this.selectedperson, this.moviesByPerson
}
)
}
ngOnInit()
{
this.getPeople()
}
}
component.html:
<div><span>ID: </span>{{selectedperson.person_id}}</div>
<h1>{{selectedperson.name}}</h1>
<p>{{selectedperson.age}}</p>
<p></p>
<table>
<thead>
<tr>
<th>Movies</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{moviesByPerson[0].movie}}</td>
</tr>
<tr>
<td>{{moviesByPerson[1].movie}}</td>
</tr>
<tr>
<td>{{moviesByPerson[2].movie}}</td>
</tr>
</tbody>
</table>