Javascript 角度表达式在选中后已更改

Javascript 角度表达式在选中后已更改,javascript,angular,typescript,Javascript,Angular,Typescript,我在Angular应用程序中得到了一个众所周知的错误,但不确定它为什么会发生以及如何修复。我尝试了几种方法,包括添加setTimeout、delay(0)、切换到不同的钩子,但在我的情况下,它们中的任何一种似乎都有效 问题描述: 我有一个产品列表,点击单个产品可以添加到购物车与选定的产品 //product.list.component.ts addToProductCart(product: IProduct) { this.productsService.addProductTo

我在Angular应用程序中得到了一个众所周知的错误,但不确定它为什么会发生以及如何修复。我尝试了几种方法,包括添加setTimeout、delay(0)、切换到不同的钩子,但在我的情况下,它们中的任何一种似乎都有效

问题描述:

我有一个产品列表,点击单个产品可以添加到购物车与选定的产品 //product.list.component.ts

  addToProductCart(product: IProduct) {
    this.productsService.addProductToSelectedProducts(product);
  }

  openDialog() {
    this.dialog.open(CartDetailsComponent, {
      width: '450px',
      height: '650px',
      data: {
        positionRelativeToElement: this.openCartButton
      }
    });
  }

  getSelectedProductsCount(): void {
    this.productsService.product.subscribe((products) => {
      this.selectedProductsCount = products.length;
    });
  }

  export class CartDetailsComponent implements OnInit, OnDestroy {
  private positionRelativeToElement: ElementRef;
  isOpen = false;
  totalSum = 0;
  totalPrices: number[] = [];
  private destroySubject: Subject<boolean> = new Subject<boolean>();
  selectedProductsCount: number;
  selectedProducts: Record<string, IProduct[]>;
  productSumPrice: number;
  products: IProduct[] = [];
  constructor(public dialogRef: MatDialogRef<CartDetailsComponent>,
              private productsService: ProductsService,
              @Inject(MAT_DIALOG_DATA) public data: { positionRelativeToElement: ElementRef }) {

    this.positionRelativeToElement = data.positionRelativeToElement;
  }

  ngOnInit() {
    const matDialogConfig = new MatDialogConfig();
    const rect: DOMRect = this.positionRelativeToElement.nativeElement.getBoundingClientRect();

    matDialogConfig.position = { right: `10px`, top: `${rect.bottom + 2}px` };
    this.dialogRef.updatePosition(matDialogConfig.position);
    this.getSelectedProducts();
    this.calculatePrices();
  }

  ngOnDestroy() {
    this.destroySubject.next(true);
  }

  close() {
    this.dialogRef.close();
  }

  deleteProduct(product: IProduct) {
    const prodId: number = product.id;
    this.selectedProducts[prodId] = this.selectedProducts[prodId].slice(0, this.selectedProducts[prodId].length - 1);
    const index: number = this.products.map(x => {
      return x.id;
    }).indexOf(product.id);
    this.products.splice(index, 1);
    this.productsService.removeSelectedProduct(this.products);
    this.calculatePrices();
  }

  viewProductDetails(product: IProduct): void {
    console.log(product);
  }

  animateCurrentItem(product: IProduct) {
    console.log(product, 'animation');
  }

  calculatePrices() {
    if (this.products.length > 0) {
      this.totalPrices = [];
      Object.values((this.selectedProducts))
        .map((prod) => {
          if (prod.length > 0) {
            (prod as IProduct[]).map((p) => {
              this.totalPrices.push(Number(p.price));
            });
          }
        });
      if (this.totalPrices.length > 0) {
        this.totalSum = this.totalPrices.reduce((prev, cur) => {
          return prev + cur;
        });
      } else {
        this.totalSum = 0;
        this.productsService.clearSelectedProducts();
      }
    }
  }

  getSelectedProducts() {
    this.productsService.product
      .pipe(
        delay(0),
        startWith([]),
        takeUntil(this.destroySubject),
      )
      .subscribe((products) => {
        if (products.length > 0) {
          this.products = products;
          this.productSumPrice = _.sumBy(products, (prod) => parseFloat(prod.price));
          this.selectedProductsCount = _.sum(Object.values(_.countBy(products, product => product.id)));
          this.selectedProducts = _.groupBy(products, 'id');
        }
      });

  }
}


该服务如下所示:

//产品服务

@Injectable({
  providedIn: 'root'
})
export class ProductsService {
  selectedProducts: BehaviorSubject<IProduct[]> = new BehaviorSubject<IProduct[]>([]);
  product = this.selectedProducts.asObservable();

  constructor(private http: HttpClient) { }

  getProductsList(): Observable<IProduct[]> {
    return this.http.get<IProduct[]>(`${environments.environment.baseUrl}/products`);
  }

  patchProductLikes(id: number, payload: Partial<IProduct>): Observable<number> {
    return this.http.patch<number>(`${environments.environment.baseUrl}/products/${id}`, payload);
  }

  addProductToSelectedProducts(product: IProduct) {
    this.selectedProducts.next([...this.selectedProducts.value, product]);
  }

  clearSelectedProducts(): void {
    this.selectedProducts.next([]);
  }

  removeSelectedProduct(products: IProduct[]): void {
    this.selectedProducts.next(products);
  }

}

如果单击标题购物车图标,则打开带有所选产品的对话框,如果没有所选产品,则应显示空购物车占位符:

//cart-details.component.html

      <span (click)="openDialog()" #openCartButton>
        <mat-icon matBadge="{{selectedProductsCount}}"matBadgePosition="above after">
          shopping_cart
        </mat-icon>
      </span>

<div *ngIf="products.length > 0 else emptyCart">
  <h5 mat-dialog-title>Total order</h5>
  <div mat-dialog-content class="product" [@loadProducts]="'in'">
    <ul>
      <li *ngFor="let groupedProducts of selectedProducts | keyvalue" class="product__product-item">
        <div *ngFor="let prod of groupedProducts.value | productPipe; let i = index" class="product-details-container">
          <div>
            <img [src]="prod.image" alt="Product photo" class="product-details-container__product-img">
          </div>
          <div class="product-info">
            <p>{{prod.name}}
              <span class="product-info__price">${{prod.price}}</span>
            </p>
            <p>
              {{prod.productMaterial}}
            </p>
            <p>
              {{prod.color}}
            </p>
            <p @deleteProduct>Amount: {{groupedProducts.value.length}} </p>
            <p>Total: ${{prod.price * groupedProducts.value.length}}</p>
            <div class="product-actions-container">
              <a (click)="deleteProduct(prod)" class="delete-product">Delete</a>
              <a (click)="viewProductDetails(prod)" class="view-details">View details</a>
            </div>
          </div>
        </div>
      </li>
      <span>SUM: ${{totalSum}}</span>
    </ul>
  </div>
</div>
<ng-template #emptyCart>
  <div class="empty-bag-container">
    <mat-icon svgIcon="empty-bag" class="empty-bag-container__empty-bag-icon"></mat-icon>
    <h4 class="empty-bag-container__empty-bag-heading">
      YOUR BAG IS EMPTY
    </h4>
    <span class="empty-bag-container__empty-bag-details"> Looks like you haven’t made your choice yet.
      Check out 100+ styles for everyone!</span>
  </div>
</ng-template>

错误是关于在我在app.module中使用的ngx ui loader lib中加载后台:

//应用程序模块

(...)
import { NgxUiLoaderModule, NgxUiLoaderHttpModule, NgxUiLoaderConfig, SPINNER, POSITION, PB_DIRECTION } from 'ngx-ui-loader';

imports: [
...
    NgxUiLoaderModule.forRoot(ngxUiLoaderConfig),
    NgxUiLoaderHttpModule,
]

你知道是什么原因导致了这个问题,以及如何解决它并在将来避免它吗? 我试图在stackblitz上复制它,但没有运气:)。虽然这可能有助于理解我的问题;P

这是因为渲染后视图发生了更改。您需要使用changeDetectorRef来检测更改。外接程序构造函数

construct(private ref: changeDetectorRef)
{}
在改变之后,你添加

this.ref.detectChanges();

尝试使用change detector refI尝试时运气不佳,但可能我在错误的组件中使用了它。我想它应该用在cart-details.component中,对吗?它应该在您更改值后放置。ie var a=10;然后添加这个.ref.detectChanges();尝试添加afterviewinit并在afterviewinit中检查它如果它仍然抛出错误,您可以尝试afterviewchecked,但它会继续检查。我在尝试这些方法之前,已经尝试过了。只是为了确保:在cart details组件(对话框上显示的组件)中,ngAfterViewInit(){this.ref.detectChanges();}或ngAfterViewChecked(){this.ref.detectChanges();}
this.ref.detectChanges();