Home > front end >  Angular: search filter rxjs
Angular: search filter rxjs

Time:05-29

I implement a function that it looks for in the input button by name But I have a problem I made a condition there that I want the input to be empty at the moment so it will get false and display all my data.

Right now as soon as the INPUT is empty I wrote a message "not found": enter image description here

What I want it to be is that as soon as the input is empty it will show me all the data and if it is not empty then it will show me the appropriate data or if it does not exist then it will not be found: enter image description here

My interface:

export interface Post{
    id:number,
    date: Date,
    authorName:string,
    content:string;
}

My seedDate:

  createDb() {
    const posts: Post[] = [
      {id:1,authorName:"Daniel",content:"Amazon’s Garage Delivery Service"
      ,date:new Date('11/12/20')
    },
      {id:2,authorName:"Omer",content:"Jake From State Farm "
      ,date:new Date('12/20/21')},
      {id:3,authorName:"Lior",content:"The General"
      ,date:new Date('02/01/22')},
      {id:4,authorName:"Tomer",content:"Spotify’s Wrapped "
      ,date:new Date('11/11/20')},
    ];
    return {posts};

html:

<input
  type="search"
  (input)="SearchPostsByName($event)"
  [(ngModel)]="authorName"
/>
<div *ngFor="let post of validPosts">
  <ng-container *ngIf="isExists">
    The name of the author is <b>{{ post?.authorName }}</b> with content
    <b>{{ post?.content }}</b> and released it on the date
    <b>{{ post?.date }}</b>
  </ng-container>
</div>
<br>

typescript:

posts: Post[] = [];
   authorName = "";
   content = "";
   date = new Date();
   isExists = false;
   validPosts: Post[] = [];
   Search:string=''; 

  constructor(private postService: PostService) { }

  ngOnInit(): void {
    this.postService.getPosts().subscribe((posts)=> {
      this.posts = posts;
    });
  }
  
  SearchPostsByName($event : Event) {
    console.log($event);
    if (this.authorName) {
      this.validPosts = this.posts.filter((value) => value.authorName === this.authorName!); 
      if (this.validPosts.length > 0
      ) {
        console.log('found');
        this.isExists = true;
      } else {
        this.isExists = false;
      }
    }
  }

My service:

export class PostService {

  private postsUrl = 'api/posts';

  constructor(private http: HttpClient) { }

  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>(this.postsUrl);
  }

  addPost(post: Post): Observable<Post> {
    let httpOptions = {
      headers: new HttpHeaders({'Content-Type':'application/json'})
    };
    
    return this.http.post<Post>(this.postsUrl, post, httpOptions)
  }

CodePudding user response:

I believe the biggest mistake is that you forgot to initialize your validPosts inside ngOnInit, which explains why you're not seeing any posts in the beginning:

  ngOnInit(): void {
    this.postService.getPosts().subscribe((posts) => {
      this.posts = posts;
      this.validPosts = posts;
    });
  }

If you want to display all the posts on an empty search, you can check if the search key word is an empty string, if so set your validPosts to posts:

    const searchKey = ($event.target as HTMLInputElement).value;

    if (searchKey === '') {
      this.validPosts = this.posts;
      return;
    }

That's should be it!

Here are some other suggestions that I have if you want to improve this even further:

  1. You do not need to use two way data binding for a simple search like this, simply filtering the posts by the search keyword would be perfectly fine.
  2. You don't have to create the isExists variable just to track if you have any validPosts, since it's an array, you can check its length instead.
  3. You can create a template to display the empty post html
  SearchPostsByName($event: Event) {
    const searchKey = ($event.target as HTMLInputElement).value;

    if (searchKey === '') {
      this.validPosts = this.posts;
      return;
    }

    this.validPosts = this.posts.filter(
      (value) => value.authorName === searchKey
    );
  }
<input type="search" (input)="SearchPostsByName($event)" />
<ng-container *ngIf="validPosts.length; else emptyPostTpl">
  <div *ngFor="let post of validPosts">
    The name of the author is <b>{{ post?.authorName }}</b> with content
    <b>{{ post?.content }}</b> and released it on the date
    <b>{{ post?.date }}</b>
  </div>
</ng-container>
<br />

<ng-template #emptyPostTpl>
  <span style="font-size: 1rem; font-weight: bold; display: block"
    >No post found</span
  >
</ng-template>

Want to take it even further?

  1. Take a look at onPush change detection to improve performance.
  2. Learn RXJS and implement a debounce search!

CodePudding user response:

For what i can see, when your input is empty, your searchPostsByName method is not doing anything, if you add the not empty condition and return all posts you'd get a list of all posts when no filter is present:

  SearchPostsByName($event : Event) {
    if (!this.authorName) { // if empty, return all posts
     this.isExists = true;
     return this.posts;
    }

    if (this.authorName) { // if not empty, filter
      this.validPosts = this.posts.filter((value) => value.authorName === this.authorName!); 
      if (this.validPosts.length > 0
      ) {
        this.isExists = true;
      } else {
        this.isExists = false;
      }
    }

CodePudding user response:

I suggest you to replace your input event, with keyup.

<input
  type="search"
  (keyup)="SearchPostsByName($event)"
  [(ngModel)]="authorName"
/>

In SearchPostsByName($event), query event$.target.value to check if there's any input:

SearchPostsByName($event : Event) {

    if(!$event.target.value.length) {
      return;
    }
    if (this.authorName) {
      this.validPosts = this.posts.filter((value) => value.authorName === this.authorName!); 
      if (this.validPosts.length > 0
      ) {
        console.log('found');
        this.isExists = true;
      } else {
        this.isExists = false;
      }
    }

  • Related