جاوااسکریپت - کتاب کشف Angular 2، فصل ۱، مثال ۵


این صفحه یک فهرست از غواصیها را نشان میدهد، دارای گزینه «افزودن غواصی جدید»، «پاک کردن غواصیها» و یک جعبه جستجو است که فهرست نمایش داده شده را هنگام تایپ کردن در آن فیلتر میکند.

این قالب است:

<div class="container-fluid">

  <h1>My Latest Dives (Angular/TypeScript)</h1>
  <div class="row">
    <div class="col-sm-5">
      <button class="btn btn-primary btn-lg"
        [disabled]="!enableAdd()"
        (click)="addDive()">
          Add new dive
      </button>
      <button class="btn btn-danger btn-lg"
        (click)="clearDives()">
          Clear dives
      </button>
    </div>
    <div class="col-sm-4 col-sm-offset-3">
      <input #searchBox class="form-control input-lg"
        placeholder="Search"
        (keyup)="0" />
    </div>
  </div>
  <div class="row">
    <div class="col-sm-4"
      *ngFor="let dive of dives | contentFilter:searchBox.value">
      <h3>{{dive.site}}</h3>
      <h4>{{dive.location}}</h4>
      <h2>{{dive.depth}} feet | {{dive.time}} min</h2>
    </div>
  </div>
</div>

این کد کامپوننت است:

import {Component} from '@angular/core';

@Component({
  selector: 'divelog',
  templateUrl: 'app/dive-log.template.html'
})

export class DiveLogComponent {
  public dives = [];
  private _index = 0;
  private _stockDives = [
  {
    site: 'Abu Gotta Ramada',
    location: 'Hurghada, Egypt',
    depth: 72,
    time: 54
  },
  {
    site: 'Ponte Mahoon',
    location: 'Maehbourg, Mauritius',
    depth: 54,
    time: 38
  },
  {
    site: 'Molnar Cave',
    location: 'Budapest, Hungary',
    depth: 98,
    time: 62
  }];

  public enableAdd() {
    return this._index < this._stockDives.length;
  }

  public addDive() {
    if (this.enableAdd()) {
      this.dives.push(this._stockDives[this._index++]);
    }
  }

  public clearDives() {
    this.dives = [];
    this._index = 0;
  } 
}

این کد فیلتر است:

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({name: 'contentFilter'})
export class ContentFilterPipe implements PipeTransform {

  transform(value: any[], searchFor: string) : any[] {
    if (!searchFor) return value;

    searchFor = searchFor.toLowerCase();
    return value.filter(dive => 
      dive.site.toLowerCase().indexOf(searchFor) >= 0 ||
      dive.location.toLowerCase().indexOf(searchFor) >= 0 ||
      dive.depth.toString().indexOf(searchFor) >= 0 ||
      dive.time.toString().indexOf(searchFor) >= 0);
  }
}

فیلتر فعال میشود و لیست هر بار که من در جعبه جستجو تایپ میکنم دوباره رندر میشود، اما وقتی روی دکمه «اضافه کردن» کلیک میکنم، این اتفاق نمیافتد. اگر چیزی در جعبه جستجو داشته باشم، دکمه «اضافه کردن» باعث تغییر در لیست غواصی نمیشود حتی با اینکه محتوای جعبه جستجو اجازه میدهد آیتمهای جدید نمایش داده شوند. چگونه میتوانم کد را تغییر دهم تا با کلیک روی دکمه «اضافه کردن»، لیست نمایش داده شده غواصی دوباره رندر شود؟

پاسخ شماره ۱

شما یک لوله خالص دارید بنابراین

متد آن transform تنها زمانی اجرا خواهد شد که تغییر خالصی در مقدار ورودی تشخیص دهد.

برای مورد شما

*ngFor="let dive of dives | contentFilter:searchBox.value"

مقدار ورودی خواهد بود dives و searchBox.value

طبق راهنمای Angular2 در مورد پایپها:

یک تغییر خالص یا تغییری در یک مقدار ورودی ابتدایی (رشته، عدد، بولین، نماد) است یا ارجاع به شیء تغییر یافته (تاریخ، آرایه، تابع، شیء).

  • وقتی یک dive اضافه میشود، ارجاع آرایه (dives) تغییر نمیکند - بنابراین متد transform اجرا نمیشود.
  • وقتی چیزی در ورودی فیلتر تایپ میشود، searchBox.value تغییر میکند - بنابراین transform اجرا میشود.

بنابراین یکی از راهحلهای ممکن این است که هر بار که یک دیو اضافه میشود، همیشه یک آرایه مرجع جدید داشته باشیم:

فقط جایگزین کنید:

this.dives.push(this._stockDives[this._index++]);

با :

this.dives = this.dives.concat(this._stockDives[this._index++]);

یا :

this.dives = [...this.dives, this._stockDives[this._index++]];

راه دوم برای انجام کار از طریق استفاده از لوله ناپاک است:

@Pipe({name: 'contentFilter', pure: false })