각도와 우주적 특성을 가진 전자상거래 제품 필터를 구축하다


* 본고는 주어진 임무에 중점을 두기 위해 기본 정보knowledge of Angular and CMS를 가정하고자 합니다.구현에 대한 자세한 내용은 언제든지 문의하십시오.

TL;박사


보기:

  • The GitHub repo
    Install the app
  • View a demo
  • 우리는 무엇을 건설해야 합니까?


    본 사이트는 이전의 예를 바탕으로 전자상거래 사이트를 만들 것이다. 그 목적은 우리가 어떻게 모든 사람에게 맞춤형 체험을 제공하는지 보여주는 것이다.나는 우리가 그곳을 건설하는 기초 위에서 일할 것이기 때문에 너에게 강력하게 건의한다.이번에 우리는 필터 기능을 추가하여 read the first article 기능을 보여줄 것이다.우리의 데이터는 우주에서 저장되고 서비스될 것이며, 우리는 각도를 전면으로 사용할 것이다.

    우주 고급 조회 우리 통 준비해.


    우리가 해야 할 첫 번째 일은 우리의 우주통을 준비하는 것이다.우리는 이미 다음과 같은 세 가지 객체 유형을 가지고 있다.
  • 범주
  • 제품
  • 사용자
  • product 은 현재 하나의 color 속성을 포함하고, 각 category 은 하나의 isRoot 속성을 포함합니다.필터를 구축할 때, 이 속성들은 우리에게 더 많은 작업을 제공할 것이다.
    새로운 유형도 생성합니다.
  • 가격 필터
  • price filterminmax 속성을 갖습니다.이 새로운 유형은 우리가 가격 범위를 정의한 후에 필터에서 사용할 수 있도록 할 것이다.또한 제품에 포함된 모든 가격대에 따라 직접 선별할 수 있지만 잠재적인 편집자/담당자에게 모든 것을 설정할 수 있기 때문에 고객에게 더 큰 유연성을 제공할 수 있는 다른 선택도 있습니다.
    만약 네가 나처럼 게으르다면, 너는 언제든지 프레젠테이션 통을 복제할 수 있다 .

    응용 프로그램 설치 모델 업데이트


    우리는 통의 변화를 우리의 모델에 반영해야 한다.이것은 가격 필터의 모델이 될 것입니다.
    export class PriceFilter {
      _id: string;
      slug: string;
      title: string;
      max: number;
      min: number;
    
      constructor(obj) {
        this._id = obj._id;
        this.slug = obj.slug;
        this.title = obj.title;
        this.max = obj.metadata.max;
        this.min = obj.metadata.min;
      }
    }
    
    물론 제품 및 카테고리 모델을 업데이트해야 합니다.
    import { Category } from './category';
    
    export class Product {
      _id: string;
      slug: string;
      title: string;
      price: string;
      categories: Category[];
      image: string;
      color: string;
    
      constructor(obj) {
        this._id = obj._id;
        this.slug = obj.slug;
        this.title = obj.title;
        this.price = obj.metadata.price;
        this.image = obj.metadata.image.url;
        this.color = obj.metadata.color;
        this.categories = [];
    
        if (obj.metadata && obj.metadata.categories) {
          obj.metadata.categories.map(category => this.categories.push(new Category(category)));
        }
      }
    }
    
    export class Category {
      _id: string;
      slug: string;
      title: string;
      isRoot: boolean;
    
      constructor(obj) {
        this._id = obj._id;
        this.slug = obj.slug;
        this.title = obj.title;
        this.isRoot = obj.metadata ? obj.metadata.root : false;
      }
    }
    

    서비스 수정


    고급 조회를 최대한 활용하기 위해 Google 서비스에 새로운 접근 방식을 만듭니다.
    getProductsByQuery(query?: string): Observable<Product[]> {
        if (!this.products$.get(query)) {
          const querystring = query ? '&query=' + query : '';
    
          const response = this.http.get<Product[]>(this.productObjectsUrl + '&sort=random' + querystring).pipe(
            tap(_ => console.log('fetched products')),
            map(_ => {
              if (_['objects']) {
                return _['objects'].map(element => new Product(element));
              }
            }),
            shareReplay(1),
            catchError(this.handleError('getProducts', []))
          );
          this.products$.set(query, response);
        }
        return this.products$.get(query);
      }
    
    * 이전 getProducts() 과 유일한 차이점은 선택적 query 매개변수를 포함하는 것입니다.
    또한 새로운 가격 필터를 얻을 수 있는 방법을 만듭니다.
    private priceFiltersUrl = this.objectTypePath + '/pricefilters';
    private priceFilters$: Observable<PriceFilter[]>;
    
    getPriceFilters(): Observable<PriceFilter[]> {
        if (!this.priceFilters$) {
          this.priceFilters$ = this.http.get<PriceFilter[]>(this.priceFiltersUrl).pipe(
            tap(_ => console.log('fetched price filters')),
            map(_ => {
              return _['objects'].map(element => new PriceFilter(element));
            }),
            shareReplay(1),
            catchError(this.handleError('getPriceFilters', []))
          );
        }
        return this.priceFilters$;
      }
    

    필터 어셈블리 생성하기


    현재 우리는 고급 방식으로 제품을 조회하는 방법이 있지만, 우리는 여전히 조회를 구성해야 하기 때문에, 사용자가 다른 필터 옵션을 선택할 수 있도록 구성 요소를 구축할 것이다.
    우리는 고객이 서로 다른 종류, 색깔, 가격 범위를 선택할 수 있도록 허용하기를 희망합니다. 이를 위해 우리는 우리의 서비스를 구독하고 결과를 지도에 분배할 것입니다. 이 지도는 한 쌍object, boolean을 저장할 것입니다.이렇게 하면 우리는 사용자의 선택을 추적할 수 있다.
    export class FilterComponent implements OnInit {
      public rootCategoryList: Map<Category, boolean> = new Map<Category, boolean>();
      public categoryList: Map<Category, boolean> = new Map<Category, boolean>();
      public colorList: Map<string, boolean> = new Map<string, boolean>();
      public priceList: Map<PriceFilter, boolean> = new Map<PriceFilter, boolean>();
    
      @Output() selectedFilters = new EventEmitter<string>();
    
      constructor(private cosmicService: CosmicService) {}
    
      ngOnInit() {
        forkJoin(this.cosmicService.getCategories(), this.cosmicService.getProducts(), this.cosmicService.getPriceFilters()).subscribe(
          ([categories, products, priceFilters]) => {
            // categories
            categories.forEach(cat => {
              cat.isRoot ? this.rootCategoryList.set(cat, false) : this.categoryList.set(cat, false);
            });
    
            // colors
    
            const colorSet = new Set<string>(); // Using a Set will automatically discard repeated colors
            products.forEach(p => colorSet.add(p.color));
            colorSet.forEach(c => {
              this.colorList.set(c, false);
            });
    
            // prices
            priceFilters.forEach(pf => this.priceList.set(pf, false));
    
            this.updateSelectedFilters();
          }
        );
      }
    ...
    
    * 루트/무루트 간에 분류를 구분하는 이유는 사용자에게 이 분류 모델에 대한 시각적 힌트를 제공하고 싶지만 작업과는 무관합니다.
    현재 html은 다음과 같습니다.
    <ul>
      <li class="mb-3" *ngFor="let category of rootCategoryList | keyvalue">
        <label class="radio is-size-4" >
          <input type="checkbox" value="{{category.key.slug}}" [checked]="category.value" (change)="filterRootCategory(category)">
          <span class="pl-2">{{category.key.title}}</span>
        </label>
      </li>
    </ul>
    <hr/>
    <ul>
      <li class="mb-3" *ngFor="let category of categoryList | keyvalue">
        <label class="checkbox is-size-4" >
          <input type="checkbox" value="{{category.key.slug}}" [checked]="category.value" (change)="filterCategory(category)">
          <span class="pl-2">{{category.key.title}}</span>
        </label>
      </li>
    </ul>
    <hr/>
    <ul>
      <li class="mb-3 color-item" *ngFor="let color of colorList | keyvalue">
          <label class="checkbox is-size-4">
            <input type="checkbox" value="{{color.key}}" [checked]="color.value" (change)="filterColor(color)">
            <span [style.background-color]="color.key"></span>
          </label>
        </li>
    </ul>
    <hr/>
    <ul>
      <li class="mb-3" *ngFor="let price of priceList | keyvalue">
        <label class="checkbox is-size-4" >
          <input type="checkbox" value="{{price.key.slug}}" [checked]="price.value" (change)="filterPrice(price)">
          <span class="pl-2">{{price.key.title}}</span>
        </label>
      </li>
    </ul>
    
    
    모든 변경 이벤트는 동일해 보이며, 요소만 맵에서 선택/선택되지 않음(확인란 값과 바인딩되므로 DOM을 수동으로 수정할 필요가 없음)으로 표시하고 필터 업데이트를 트리거합니다.
    filterCategory(entry: { key: Category; value: boolean }) {
        this.categoryList.set(entry.key, !entry.value);
        this.updateSelectedFilters();
      }
    
    * 잠깐...
    지금 봅시다updateSelectedFilters().이 방법은 지도에서 현재 선택한 내용(aux 방법setCategoryFilterSelection() 등의 도움말 감사)을 심사하여 우리의 조회를 구축할 것입니다.
    updateSelectedFilters() {
        // categories
        const catInSelection: string[] = [];
        const catNotInSelection: string[] = [];
    
        this.setCategoryFilterSelection(this.categoryList, catInSelection, catNotInSelection);
        this.setCategoryFilterSelection(this.rootCategoryList, catInSelection, catNotInSelection);
    
        // colors
    
        const colorInSelection: string[] = this.setColorFilterSelection(this.colorList);
    
        // price
        const pricesInSelection: number[][] = this.setPriceFilterSelection(this.priceList);
    
        // query
        let jsonObj = {};
        if (catInSelection.length > 0 && catNotInSelection.length > 0) {
          jsonObj['metadata.categories'] = {
            $in: catInSelection,
            $nin: catNotInSelection
          };
        }
        if (colorInSelection.length > 0) {
          jsonObj['metadata.color'] = { $in: colorInSelection };
        }
    
        if (pricesInSelection.length > 0) {
          jsonObj['$or'] = [];
          pricesInSelection.forEach(price => {
            jsonObj['$or'].push({
              $and: [
                {
                  'metadata.price': {
                    $gte: price[0]
                  }
                },
                {
                  'metadata.price': {
                    $lte: price[1]
                  }
                }
              ]
            });
          });
    
          // Introducing "$or" means we need to combine with an "$and" for the other conditions
          const auxObj = { $and: [] };
    
          auxObj.$and.push(
            { "'metadata.categories": jsonObj['metadata.categories'], 'metadata.color': jsonObj['metadata.color'] },
            { $or: jsonObj['$or'] }
          );
          jsonObj = auxObj;
        }
    
        const query = encodeURIComponent(JSON.stringify(jsonObj));
        this.selectedFilters.emit(query);
      }
    

    그것을 한데 포장하다


    너는 우리가 조회를 보내고 있다는 것을 알아차렸니?이제 우리의 제품 목록으로 돌아가서 우리가 한 모든 변경에 적응하기 위해 어떻게 제품을 요청하는지 수정할 때가 되었다.먼저 HTML을 업데이트하여 새 필터 구성 요소를 포함합니다.
    <div class="columns">
    <div class="column is-one-fifth filters">
      <app-filter (selectedFilters)="onChangeFilters($event)"></app-filter>
    </div>
    <div class="column columns" *ngIf="productList && user">
      <ng-container *ngFor="let product of (productList | customSort:user.interests)">
              <div class="product-tile column is-one-third">
                <img src="{{ product.image }}" class="image"/>
                <div class="level is-size-4 is-uppercase">
                    <span class="level-item">{{product.title}}</span>
                    <span class="level-item has-text-weight-bold">${{product.price}}</span>
                </div>
                <app-actions [product]="product"></app-actions>
              </div>
      </ng-container>
      <div *ngIf="productList.length === 0">
        <span>There are no products that match your search, try something else.</span>
      </div>
    </div>
    </div>
    
    
    이제 다음과 같은 방법으로 selectedFilters 이벤트를 정의할 수 있습니다.
      onChangeFilters(selectedFilters: string) {
        this.cosmicService.getProductsByQuery(selectedFilters).subscribe(products => {
          this.productList = products ? products : [];
        });
      }
    
    그뿐이야.이전의 전자상거래 응용 프로그램을 몇 번 업데이트하기만 하면 우리는 기능이 강한 필터 구성 요소를 추가하여 고객이 원하는 제품을 찾을 수 있도록 도와줄 수 있다.
    이런 글에 더 관심이 있습니까? 본 강좌와 유사한 강좌를 더 많이 알아보거나 Cosmic articles 그곳에서 당신 같은 수백 명의 개발자들이 무두 사이트의 미래를 토론하고 있습니다.

    좋은 웹페이지 즐겨찾기