角度:订阅未检测到更改,因此我的视图未更新

时间:2019-06-29 04:03:27

标签: angular typescript rxjs observable

我有两个组件,其中一个是主要组件html中的子路由。

每当我在主要组件中选择类别的复选框时,便会将它们分配给服务中的变量(字符串数组)。在子路由中,我订阅了一个可观察到的服务变量。我的目标是更新子路线的视图以仅显示那些类别的书。

因此,当服务变量的值更改时,我不应该看到日志吗(因为我在子组件中给出了console.log())? 值每次更改都不会订阅获取更新的值吗? 最后,订阅是否调用ngOnChanges()方法? (以更新视图)

我的代码:- 父Component.html:-

selectedCategories: string[] = [];
 //...

 categoryFilterSelect(){
    this.userService._filterByGenre = this.selectedCategories;
  }

父Component.ts:-

export class HomeComponent implements OnInit {
  allBooks: Book[] = [];
  selectedCategories: string[];
  filteredBooks: Book[];

  constructor(private userService: UserService) {}

  ngOnInit() {
    //initially display all books
    this.userService.getAllBooks().subscribe(data => {
      this.allBooks = data;
      this.filteredBooks = this.allBooks;
    });


    //This part not working
    this.userService.filterByGenre.subscribe(data => {
      this.selectedCategories = data;

      //this line not working
      console.log(this.selectedCategories);


      //perform filter logic here and update the filteredBooks array based 
      // onselected categories
    }
    );
  }
}

第二个组件的ts :-(路由到Parent Component.html中)

<div class="container">
  <div class="child" *ngFor="let book of filteredBooks">
....
  </div>
</div>

second component.html

export class UserService {

  _filterByGenre: string[] = [];
  public get filterByGenre(): Observable<string[]> {
    return of(this._filterByGenre);
  }
}

service.ts

x2

控制台输出:- [] (因为最初没有选择类别,所以将其记录下来。)

我的Git回购:- https://github.com/Lucifer-77/ATL

2 个答案:

答案 0 :(得分:1)

@Lucifer,事情并没有您所想的那样。角不是神奇的。不用担心,这是一开始的常见错误。当您订阅一个Observable时,Angular不会放一个“老大哥”来查看数据发生了什么。例如当我们对调用dbs的httpClient.get(url)进行预订时,对dbs Angular的任何更改都不会考虑到这一点。 (它不能做到)。

因此,您需要创建一个服务,说Angular考虑了更改:您需要使用类似的Subject。让我来展示一项有效的服务

  private _filterByGenre:string[];

  private filterByGenreSource = new Subject<any>();
  filterByGenreObservable=this.filterByGenreSource.asObservable();

  get filterByGenre()
  {
    return this._filterByGenre
  }
  set filterByGenre(value)  //when we use dataService.filterBtGenre=...
  {
    this._filterByGenre=value;           //equal to the private variable
    this.filterByGenreSource.next(value) //say to Angular that something change
  }

您可以订阅filterByGenreObservable

this.dataService.filterByGenreObservable.subscribe(data => {
  this.selectedCategories = [...data]; //(*)
});

//(*) is we make simply this.selectedCategories=data, we can not see the data change
//so, I used the spread operator

如果我们做类似的事情

this.dataService.filterByGenre = this.selectedCategories;

在使用setter时,发出一个新值

好吧,我不使用p-checkbox,但是您可以在此stackblitz

中使用此答案的代码

更新好,很显然,我们希望在更改类别时做出一些“改变”。

想象一下,我们有一个名为allBooks的数组,我们希望按选定的类别进行过滤。但是首先我们要更改发送给用户服务的数据,使其与p-checkbox更加相似,因此我将函数categoryFileterSelect更改为以数组形式发送,而不是[true,false,false,true] else [“ Horror” ,“科幻小说”]

categoryFilterSelect() {
    this.dataService.filterByGenre =
      this.options.filter((x, index) => this.selectedCategories[index]);

  }

所以,我们可以写

this.userService.filterByGenreObservable.subscribe(
  data => {
    this.filteredBooks=this.allBooks.filter(x=>data.indexOf(x.genre)>=0)
  },
  error => null,
  () => console.log("error")
);

好吧,我首先希望使用this.userService.filterByGenre的值进行一次调用,因此我将使用rxjs运算符startWith这样,最后我的订阅变为

this.userService.filterByGenreObservable.pipe(
  startWith(this.userService.filterByGenre))
  .subscribe(
  data => {
    this.filteredBooks=data?this.allBooks.filter(x=>data.indexOf(x.genre)>=0)
                           :this.allBooks
  },
  error => null,
  () => console.log("error")
);

答案 1 :(得分:0)

请尝试在第二个组件中对filterByGenre()进行以下调用。

this.userService.filterByGenre().subscribe((data:string[]) => {
  this.selectedCategories = data;

  console.log(this.selectedCategories);


  //perform filter logic here and update the filteredBooks array based 
  // onselected categories
}