首页 > 解决方案 > Angular:订阅未检测到更改,因此我的视图未更新

问题描述

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

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

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

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

  <div class="ui-g-12">
    <p-checkbox
      name="categories"
      value="Science Fiction"
      label="Science Fiction"
      [(ngModel)]="selectedCategories"
      (onChange)="categoryFilterSelect()"
    ></p-checkbox>
  </div>
  <div class="ui-g-12">
    <p-checkbox
      name="categories"
      value="Horror"
      label="Horror"
      [(ngModel)]="selectedCategories"
      (onChange)="categoryFilterSelect()"
    ></p-checkbox>
  </div>
  <div class="ui-g-12">
    <p-checkbox
      name="categories"
      value="Suspense"
      label="Suspense"
      [(ngModel)]="selectedCategories"
      (onChange)="categoryFilterSelect()"
    ></p-checkbox>
  </div>
  <div class="ui-g-12">
    <p-checkbox
      name="categories"
      value="Romance"
      label="Romance"
      [(ngModel)]="selectedCategories"
      (onChange)="categoryFilterSelect()"
    ></p-checkbox>
  </div>
  <div class="ui-g-12">
    <p-checkbox
      name="categories"
      value="Action"
      label="Action"
      [(ngModel)]="selectedCategories"
      (onChange)="categoryFilterSelect()"
    ></p-checkbox>
  </div>
  <div style="margin-top: 10%">
    <router-outlet></router-outlet>
  </div>

父组件.ts:-

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

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

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

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
    }
    );
  }
}

第二个组件.html

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

服务.ts

export class UserService {

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

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

我的 Git 仓库:- https://github.com/Lucifer-77/ATL

标签: angulartypescriptrxjsobservable

解决方案


@Lucifer,事情不像你想象的那样工作。Angular 并不神奇。别担心,起初这是一个常见的错误。当你订阅一个 Observable 时,Angular 不会放一个“老大哥”来看看数据会发生什么。例如,当我们订阅调用 dbs 的 httpClient.get(url) 时,dbs Angular 的任何更改都不会考虑到这一点。(它做不到)。

因此,您需要创建一个服务,说明 Angular 会考虑更改:您需要使用类似的主题。让我展示一个有效的服务

  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 更改为以数组形式发送 not [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")
);

推荐阅读