Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

angular - Trigger mat-expand-panel event with button in a different component

I am pretty new to Angular, and I am trying to implement a button in my search/filter bar that will expand the mat-expansion-panel on click, but the panels are housed in a different component and I am not sure of the structure for this.

Button HTML:

      <input *ngIf="!(isMobile$ | async) && (isOnline$ | async)"
      #filterTextInput
      id="filter-text-input"
      class="numeric-input"
      type="text"
      [value]="itemFilterText$ | async"
      autocomplete="off"
      placeholder="{{'HEADER.SEARCH.PLACEHOLDER' | translate}}"
      attr.aria-label="{{'HEADER.SEARCH.PLACEHOLDER' | translate }}"
      (keyup.enter)="searchForValue(searchString.value)" />
      <button mat-raised-button (click)="togglePanel">Open</button>

Here is the TS file where the functionality should be placed:

import { Component, OnInit, Output, EventEmitter, ViewChildren, QueryList, ElementRef, ChangeDetectionStrategy } from '@angular/core';
import { Worksheet, StorageArea, WorksheetItem, CountableItem, CountingUnit, ConversionUnit } from 'src/app/models';
import { StorageAreaUtilsService } from 'src/app/services/storage-area-utils.service';
import { Store } from '@ngrx/store';
import { AppState } from 'src/app/store/reducers';
import { first, filter, map, flatMap } from 'rxjs/operators';
import { SetStorageAreaExpandStatus, PatchStorageArea, GetAllCustomItemDataAttempt } from 'src/app/store/actions/worksheets.actions';
import { combineLatest, Observable } from 'rxjs';
import { PageEvent, MatPaginator } from '@angular/material';
import { InventoryConstants } from 'src/app/inventory-constants';
import { GetMeasurementUnitsAttempt } from 'src/app/store/actions/addItems.actions';
import { InventoryValueService } from 'src/app/services/inventory-value.service';

@Component({
  selector: 'app-storage-area-item-panel',
  templateUrl: './storage-area-item-panel.component.html',
  styleUrls: ['./storage-area-item-panel.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class StorageAreaItemPanelComponent implements OnInit {
  worksheet$ = this.store.select(state => {
    return state.worksheets.worksheets[state.worksheets.selected];
  });
  currentLang$ = this.store.select(state => state.layout.language);
  isMobile$ = this.store.select(state => state.layout.isMobile);
  inventoryItems$ = this.store.select(state => state.inventoryItems.inventoryItems);
  itemFilterText$ = this.store.select(state => state.worksheets.itemFilterText);
  assignedStorageAreas$: Observable<StorageArea[]>;
  unAssignedStorageArea$: Observable<StorageArea>;
  pricingPermissions = InventoryConstants.INVENTORY_ROLE_PERMISSIONS.pricing;
  standardUnits$ = this.store.select(state => state.addItems.measurementUnits).pipe(
    map(units => {
      return units.filter(unit => unit.measurementType === 'weight');
    })
  );
  customItemData$ = this.store.select(state => state.worksheets.customItemData);
  standardCountingUnits$ = this.store.select(state => state.worksheets.countingUnits);
  customCountingUnits$ = this.customItemData$.pipe(
    map(data => {
      const itemUnits: CountingUnit[] = [];
      data.forEach(datum => {
        datum.countingUnits.forEach(unit => {
          if (unit.custom) {
            itemUnits.push({
              itemId: datum.itemId,
              unit: { unitType: 'CUSTOM', unitId: unit.custom.id } as ConversionUnit,
              customUnit: unit
            } as CountingUnit);
          }
        });
      });
      return itemUnits;
    }));
  countingUnits$ = combineLatest(this.standardCountingUnits$, this.customCountingUnits$).pipe(
    map(response => {
      return [].concat(...response);
    }));

  @Output() afterExpand = new EventEmitter();

  storageAreaPaging: { [s: string]: number };

  @ViewChildren('storageAreaPaginator')
  paginators: QueryList<MatPaginator>;
  @ViewChildren('storageAreaPaginatorIndexPlaceholder')
  paginatorsDOM: QueryList<ElementRef>;

  constructor(
    private store: Store<AppState>,
    private storageAreaUtils: StorageAreaUtilsService,
    private inventoryValueService: InventoryValueService
  ) {
    this.storageAreaPaging = {};
  }

  ngOnInit() {
    this.store.dispatch(new GetMeasurementUnitsAttempt());
    this.store.dispatch(new GetAllCustomItemDataAttempt());
    this.expandPanels();
    this.assignedStorageAreas$ = this.storageAreaUtils.getStorageAreasMinusUnassigned(this.worksheet$);
    this.unAssignedStorageArea$ = this.storageAreaUtils.getUnassignedStorageArea(this.worksheet$);
  }

  onLastItemEnter(storageArea?: StorageArea) {
    const pageSize = 10;
    if (storageArea) {
      let pageIndex = this.storageAreaPaging[storageArea.id] / pageSize;
      if (!pageIndex) {
        pageIndex = 0;
      }
      if ((pageIndex + 1) * pageSize < storageArea.worksheetItems.length) {
        this.storageAreaPaging[storageArea.id] = (pageIndex + 1) * pageSize;
        const paginatorIndex = this.paginatorsDOM.toArray().findIndex(element => {
          return element.nativeElement.id === `paginator-${storageArea.id}`;
        });
        const paginator = this.paginators.toArray()[paginatorIndex];
        paginator.nextPage();
      }
    } else {
      this.unAssignedStorageArea$.pipe(first()).subscribe(unassigned => {
        let pageIndex = this.storageAreaPaging[unassigned.id] / pageSize;
        if (!pageIndex) {
          pageIndex = 0;
        }
        if ((pageIndex + 1) * pageSize < unassigned.worksheetItems.length) {
          this.storageAreaPaging[unassigned.id] = (pageIndex + 1) * pageSize;
          const paginator = this.paginators.toArray()[0];
          paginator.nextPage();
        }

      });
    }
  }

  onPageClick(event: PageEvent, storageAreaId?: string) {
    if (storageAreaId) {
      this.storageAreaPaging[storageAreaId] = event.pageIndex * event.pageSize;
    } else {
      this.unAssignedStorageArea$.pipe(first()).subscribe(unassigned => {
        this.storageAreaPaging[unassigned.id] = event.pageIndex * event.pageSize;
      });
    }
  }

  getCurrentPageByStorageAreaId(storageAreaId: string, offsetToAdd: number = 0) {
    return this.storageAreaPaging[storageAreaId] ? this.storageAreaPaging[storageAreaId] + offsetToAdd : 0 + offsetToAdd;
  }

  getTranslation(name: string) {
    return this.storageAreaUtils.getTranslation(name);
  }

  getRowItemCount(storageArea: StorageArea) {
    return this.storageAreaUtils.getRowItemCount(storageArea);
  }

  getWorksheetId(): Observable<string> {
    return this.worksheet$.pipe(
      filter(ws => !!ws),
      map((ws: Worksheet) => ws.id)
    );
  }

  // expands the first panel with items if desktop, expands all if mobile
  expandPanels() {
    combineLatest(this.isMobile$, this.worksheet$).pipe(
      filter(([isMobile, ws]) => {
        if (ws && ws.storageAreas) {
          return true;
        } else {
           return false;
        }
      }),
      first()
    ).subscribe(([isMobile, ws]) => {
      if (isMobile) {
        ws.storageAreas.forEach((area: StorageArea) => {
          if (area.expandStatus !== true) {
            this.store.dispatch(new SetStorageAreaExpandStatus({ status: true, areaId: area.id }));
          }
        });
      } else if (ws.storageAreas) {
        const firstStorageAreaWithRowItems: StorageArea = ws.storageAreas.find(stArea => {
          return stArea.worksheetItems.length > 0;
        });
        if (firstStorageAreaWithRowItems
          && firstStorageAreaWithRowItems.expandStatus !== true) {
            this.store.dispatch(new SetStorageAreaExpandStatus({ status: true, areaId: firstStorageAreaWithRowItems.id }));
        }
      }
    });
  }

  fireSetStorageAreaExpandStatus(event: Event, area: StorageArea) {
    if (area !== null) {
      this.store.dispatch(new SetStorageAreaExpandStatus({
        status: !area.expandStatus,
        areaId: area.id
      }));
    } else if (area === null) {
      this.unAssignedStorageArea$
        .pipe(first()).subscribe(unassigned => {
          this.store.dispatch(new SetStorageAreaExpandStatus({
            status: !unassigned.expandStatus,
            areaId: unassigned.id
          }));
        });

    }
  }

  /** Filters general items to only the items contained in the given storage area
   * @param area: the storage area to filter by
   */
  filterInventoryItemsByArea(area: StorageArea): Observable<{ [s: string]: CountableItem }> {
    return this.inventoryItems$.pipe(
      filter(() => !!area && !!area.worksheetItems),
      map((items) => {
        return Object.keys(items)
          .filter(id => {
            return area.worksheetItems.some(rowItem => rowItem.itemId === id);
          })
          .reduce((acc, id) => {
            acc[id] = items[id];
            return acc;
          }, {});
      })
    );
  }

  filterStorageAreaItemsByFilterText(area: StorageArea): Observable<WorksheetItem[]> {
    return combineLatest(this.itemFilterText$, this.inventoryItems$, this.currentLang$)
      .pipe(
        map(([text, invItems, lang]) => {
          if (area.worksheetItems) {
            return this.getSortedWorksheetItems(area).filter(workItem => {
              const countableItem = invItems[workItem.itemId];
              if (countableItem) {
                if (countableItem.Item) {
                  // the item exists and contains the search text in the name, brand, or item SKU
                  return ((countableItem.Item.description
                          .find(localValue => localValue.languageCode === lang) !== undefined
                        && countableItem.Item.description
                          .find(localValue => localValue.languageCode === lang).value
                        && countableItem.Item.description
                          .find(localValue => localValue.languageCode === lang).value.toLowerCase().includes(text.toLowerCase()))
                      || (countableItem.Item.brand
                          .find(localValue => localValue.languageCode === lang) !== undefined
                        && countableItem.Item.brand
                          .find(localValue => localValue.languageCode === lang).value
                        && countableItem.Item.brand
                          .find(localValue => localValue.languageCode === lang).value.toLowerCase().includes(text.toLowerCase()))
                      || workItem.itemId.includes(text));
                } else if (countableItem.customItem) {
                  return (countableItem.customItem.description.toLowerCase().includes(text.toLowerCase())
                    || countableItem.customItem.supplierId.toLowerCase().includes(text.toLowerCase())
                    || workItem.itemId.includes(text));
                } else if (countableItem.generalItem) {
                return (countableItem.generalItem.description.toLowerCase().includes(text.toLowerCase())
                  || workItem.itemId.includes(text));
                }
              } else { // Item info is missing...
                return workItem.itemId.includes(text);
              }
            });
          } else {
            return [];
          }
        })
      );
  }

  panelOpenState: boolean = false;

togglePanel() {
    this.panelOpenState = !this.panelOpenState
}

If more of the code is needed le


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can expand the mat-expansion-panel by changing the [expanded]property.

<mat-expansion-panel [expanded]="true"></mat-expansion-panel>

To communicate between two components you usually use an EventEmitter

This is an example from the Angular website on how to use them:

@Component({
selector: 'zippy',
  template: `
  <div class="zippy">
    <div (click)="toggle()">Toggle</div>
    <div [hidden]="!visible">
      <ng-content></ng-content>
    </div>
 </div>`})
export class Zippy {
  visible: boolean = true;
  @Output() open: EventEmitter<any> = new EventEmitter();
  @Output() close: EventEmitter<any> = new EventEmitter();

  toggle() {
    this.visible = !this.visible;
    if (this.visible) {
      this.open.emit(null);
    } else {
      this.close.emit(null);
    }
  }
}

In your template you can then select a method to be called when the eventemitter emits:

<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>

So in your case, the component with the button would hold the event emitters and your StorageAreaPanel would subscribe to them.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...