【问题标题】:How to randomize (shuffle) an angular Observable?如何随机化(洗牌)有角度的 Observable?
【发布时间】:2021-12-17 02:47:04
【问题描述】:

我正在使用 Angular 和 Firebase。我在 Firebase 中有 170 种产品。当我调用 firebase 时,我得到了存储在 products$ 中的产品的 Observable。

问题:我想将所有产品相互洗牌。这样当我刷新网页时,每次的产品列表都会不一样。我尝试了数组方法,但它不起作用,因为 products$ 是 Observable 而不是数组! 我能做什么?提前致谢!

product.component.ts
import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ActivatedRoute } from '@angular/router';
import { ProductsService } from './../products.service';
import { map, switchMap } from 'rxjs/operators';
import { Observable, of, Subscription } from 'rxjs';
import { Component } from '@angular/core';

@Component({
  selector: 'products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
  products$: Observable<Product[]>;
 
  cart$: Observable<Cart>;

  constructor(
    private productsService: ProductsService,
    private cartService: CartService,
    private route: ActivatedRoute
  ) {
    this.getProducts();
    this.getCart();
  }

  private getProducts(): void {
    this.products$ = this.route.queryParamMap.pipe(
      switchMap((params) => {
        if (!params) return of(null);

        let category = params.get('category');
        return this.applyFilter(category);
      })
    );
  }

  private applyFilter(category: string): Observable<Product[]> {
    if (!category)
      return this.productsService
        .getAll()
        .snapshotChanges()
        .pipe(
          map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
        );

    return this.productsService
      .getByCategory(category)
      .snapshotChanges()
      .pipe(
        map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
      );
  }

  private async getCart() {
    this.cart$ = (await this.cartService.getCart())
      .snapshotChanges()
      .pipe(
        map(
          (sc) =>
            new Cart(
              sc.key,
              sc.payload.val().cartLines,
              sc.payload.val().createdOn
            )
        )
      );
  }
}
product.component.html
<div class="row">
    <div class="col-3">
        <products-filter></products-filter>
    </div>
    <div class="col-9">
        <div *ngIf="cart$ | async as cart" class="row">
            <ng-container *ngFor="let product of products$ | async; let i = index">
                <div class="col">
                    <product-card [product]="product" [cart]="cart"></product-card>
                    <div *ngIf="(i + 1) % 2 === 0" class="row row-cols-2"></div>
                </div>
            </ng-container>
        </div>
        <div *ngIf="(products$ | async)?.length === 0" class="alert alert-info" role="alert">
        <h1>Keine Produkte gefunden!</h1>
        </div>
    </div>
</div>

【问题讨论】:

  • products$ 是一个数组的可观察对象,因此您可以通过管道输入一个 map 运算符来打乱数组。
  • @jBuchholz,我应该在存储所有产品后取一个新变量并调用 products$ 吗?
  • 当您映射值时...map((sps) =&gt; sps.map((sp) =&gt; ({ key: sp.key, ...sp.payload.val() }))) 之后...像 jBuchholz 所说的那样对数组进行洗牌 :) 不需要任何其他变量。

标签: angular angular-observable


【解决方案1】:

当然有更好/更短的方法可以做到这一点,但您可以尝试使用“BehaviourSubject”并将其传递给您的数组,按照您喜欢的方式排序(在这种情况下随机排序)。

你可以试试这样的:

  1. 将此添加到您的 product.component.ts:
// product.component.ts

import { ActivatedRoute } from '@angular/router';
import { Component } from '@angular/core';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ProductsService } from './../products.service';

@Component({
  selector: 'products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
  products$: Observable<Product[]>;
  randomProductsSource = new BehaviorSubject<Product[] | null>(null);
  randomProducts$ = this.randomProductsSource.asObservable();
  cart$: Observable<Cart>;

  constructor(
    private productsService: ProductsService,
    private cartService: CartService,
    private route: ActivatedRoute
  ) {
    this.getProducts();
    this.getCart();

    this.products$.subscribe( _products => {
      if ( _products && products?.length > 0) { 
         this.randomizeArray(_products);
         this.setRandomProducts(_products);
      }
    })

  }

  private getProducts(): void {
    this.products$ = this.route.queryParamMap.pipe(
      switchMap((params) => {
        if (!params) return of(null);

        let category = params.get('category');
        return this.applyFilter(category);
      })
    );
  }

  private applyFilter(category: string): Observable<Product[]> {
    if (!category)
      return this.productsService
        .getAll()
        .snapshotChanges()
        .pipe(
          map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
        );

    return this.productsService
      .getByCategory(category)
      .snapshotChanges()
      .pipe(
        map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
      );
  }

  private async getCart() {
    this.cart$ = (await this.cartService.getCart())
      .snapshotChanges()
      .pipe(
        map(
          (sc) =>
            new Cart(
              sc.key,
              sc.payload.val().cartLines,
              sc.payload.val().createdOn
            )
        )
      );
  }

  private setRandomProducts(data: Product[] | null) {
    this.randomProductsSource.next(data);
  }

  private randomizeArray(arrayProducts) {
     for (let i = arrayProducts.length-1; i > 0; i--) {
        const j = Math.floor( Math.random()*(i+1) );
        [arrayProducts[i], arrayProducts[j]] = [arrayProducts[j], arrayProducts[i]];
     }
  }


}


  1. 然后在你的 product.component.html 中更改,只需将 products$ 更改为 randomProducts$:
<div *ngIf="(randomProducts$ | async)?.length === 0" class="alert alert-info" role="alert">

我从HERE得到了随机化方法的想法

【讨论】:

  • 不起作用。在此处获取错误,setRandomProducts(data: Product[] | null) { this.randomProductsSource.next(data); } randomizeArray(arrayProducts) { for (let i = arrayProducts.length-1; i > 0; i--) { const j = Math.floor( Math.random()*(i+1) ); [arrayProducts[i], arrayProducts[j]] = [arrayProducts[j], arrayProducts[i]]; },在“ProductsComponent”类型上不存在
  • 对不起,我会编辑我的答案并将我的代码合并到你的。
  • 非常感谢!这绝对是我想要的!为了批准这个答案,我还需要 2 个声誉。您能否在我的问题中提一下,以便我批准答案?再次非常感谢。
  • 我很高兴它有帮助!
【解决方案2】:

只需在您的映射中添加一个额外的步骤,您实际上在返回之前对数组进行洗牌。但是你想洗牌,是一种选择,这里有几个选择:How to randomize (shuffle) a JavaScript array? 那里的答案之一介绍了这个:

/* Randomize array in-place using Durstenfeld shuffle algorithm */
shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

因此,只需在您想要随机排列数组的位置调用此函数。进行过滤后,您的 getProducts 函数内将有一个适当的时间:

private getProducts(): void {
  this.products$ = this.route.queryParamMap.pipe(
    switchMap((params) => {
      if (!params) return of(null);

      let category = params.get('category');
      return this.applyFilter(category);
    }),
    // Shuffle the array before returning it!
    map((products: Product[]) => this.shuffleArray(products))
  );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 2016-04-26
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    相关资源
    最近更新 更多