根据输入值更新 ngif

update ngif depending on input value

我正在尝试搜索以过滤博客中的帖子
这些帖子是从 ngrx 商店作为可观察的
因此,当用户更改输入的值时,帖子 (with async pipe) 将由名为 filter-posts[=26 的管道更新=] 如果新数组为空,则应显示一条消息 'no posts found'
问题是更改输入值后未显示消息
这是代码:

posts.component.html:

<div class="posts">
  <!--search-->
    <input
      [(ngModel)]="searchValue"
      class="p-2 rounded border w-100 bg-secondary text-light" type="search"
      placeholder="Search..." id="search">

  <!--posts-->
  <ng-container *ngIf="(posts | async).length > 0; else noPosts">
    <div class="post" *ngFor="let post of posts | async | filterPosts:searchValue">
      <div class="p-title">
        <a (click)="goTo('blog/posts/' + post.id.toString(), post)">{{post.title}}</a>
      </div>
      <div class="p-image">
        <img [src]="post.imageSrc" [alt]="post.title">
      </div>
      <div class="p-cont">
        {{post.content | slice:0:80}}
      </div>
      <div class="p-category">
        <a *ngFor="let category of post.categories" class="p-1 m-1 text-light fw-bold">{{category}}</a>
      </div>
      <div class="p-date">
        {{post.date}}
      </div>
      <app-author class="p-author d-block" [author]="post.author"></app-author>
    </div>
  </ng-container>
  <ng-template #noPosts>
    <div class="bg-danger w-100">No Posts Found</div>
  </ng-template>

</div>

posts.component.ts:

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Router } from '@angular/router';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { Post } from 'src/app/models/post';
import { User } from 'src/app/models/user';
import { setSelectedPostId } from 'src/app/state/posts/actions';
import { getPosts, PostState } from 'src/app/state/posts/reducer';
import { getUserById } from 'src/app/state/users/reducer';

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

  constructor(private store: Store<PostState>, private router: Router) { }

  search = faSearch;

  searchValue: string = '';

  goTo(path: string, post: Post) {
    this.router.navigate([path]);
    this.setSelectedPost(post.id);
  }

  setSelectedPost(id: string) {
    this.store.dispatch(setSelectedPostId({id}));
  }

  getUser(id: string): Observable<User> {
    return this.store.select(getUserById(id));
  }

  posts: Observable<Post[]> = this.store.select(getPosts);

}

过滤器-posts.pipe.ts:

import { Pipe, PipeTransform } from '@angular/core';
import { Post } from '../models/post';

@Pipe({
  name: 'filterPosts',
  pure: false
})
export class FilterPostsPipe implements PipeTransform {
  transform(value: Post[], searchValue: string): Post[] {
    return value.filter(
      (post) =>
        post.title.toLowerCase().includes(searchValue.toLowerCase()) ||
        post.content.toLowerCase().includes(searchValue.toLowerCase()) ||
        post.author.name.toLowerCase().includes(searchValue.toLowerCase()) ||
        post.categories.includes(searchValue.toLowerCase())
    );
  }
}

对这种操作使用 pipe 一开始似乎是个好主意,但是当你依赖一个在每个变化检测周期都会 运行 的不纯管道时,性能应用程序的安全性可能会受到损害。

有更简单的方法可以根据另一个值过滤可观察值,我的建议是将搜索输入的值也视为流。

如果我们不使用 [(ngModel)] 而使用 FormControl 来监听输入的 valueChanges observable,我们可以组合流并过滤 posts 值基于 search 值。

你会得到这样的结果

// Create a form control for the search input
searchControl = new FormControl();

..........................

posts: Observable<Post[]> = combineLatest([
  this.store.select(getPosts),
  this.searchControl.valueChanges.pipe( // Listen for the changes of the form control
    debounceTime(250), // dont search for every keystroke
    startWith(''), // start with an empty string to show all posts
  )
]).pipe(
  map(([ posts, searchValue ]) => posts.filter(post => {
    return post.title.toLowerCase().includes(searchValue.toLowerCase()) ||
      post.content.toLowerCase().includes(searchValue.toLowerCase()) ||
      post.author.name.toLowerCase().includes(searchValue.toLowerCase()) ||
      post.categories.includes(searchValue.toLowerCase())
  })
  )
);

然后在您的模板中连接表单控件

<input [formControl]="searchControl"
       ...>

有了这个,就不需要管道了,当可观察值改变时,视图应该更新。