File

src/app/core/entity-details/entity-bulk-actions/entity-bulk-actions.component.ts

Description

Allow users to select among the registered bulk actions that are available in the current context. Also executes the action.

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

entities
Type : Entity[]
Required :  true

List of selected entities for bulk actions

Outputs

resetBulkActionMode

Event emitted when the bulk action mode should be exited after an action was executed or the user cancelled the bulk actions.

Methods

cancel
cancel()
Returns : void
Async onActionSelected
onActionSelected(action: EntityAction)
Parameters :
Name Type Optional
action EntityAction No
Returns : any

Properties

actionControl
Type : unknown
Default value : new FormControl()
actionToString
Type : unknown
Default value : () => {...}
bulkActions
Type : unknown
Default value : resource({ params: () => ({ entities: this.entities() }), loader: async ({ params }) => { const bulkActions = await this.actionsService.getActionsForBulk( params.entities, ); return bulkActions .map((action) => { if (action.action === "merge") { return { ...action, disabled: !this.entities() || this.entities().length !== 2, }; } return action; }) .filter((action) => !!action); }, defaultValue: [], })

Available bulk actions for the current selection

import {
  Component,
  inject,
  input,
  output,
  resource,
  effect,
} from "@angular/core";
import { FormControl, ReactiveFormsModule } from "@angular/forms";
import { Entity } from "../../entity/model/entity";
import { EntityActionsMenuService } from "../entity-actions-menu/entity-actions-menu.service";
import { EntityAction } from "../entity-actions-menu/entity-action.interface";
import {
  BASIC_AUTOCOMPLETE_COMPONENT_IMPORTS,
  BasicAutocompleteComponent,
} from "../../common-components/basic-autocomplete/basic-autocomplete.component";
import { MatButtonModule } from "@angular/material/button";

/**
 * Allow users to select among the registered bulk actions
 * that are available in the current context.
 * Also executes the action.
 */
@Component({
  selector: "app-entity-bulk-actions",
  templateUrl: "./entity-bulk-actions.component.html",
  styleUrls: ["./entity-bulk-actions.component.scss"],
  standalone: true,
  imports: [
    MatButtonModule,
    ReactiveFormsModule,
    BasicAutocompleteComponent,
    ...BASIC_AUTOCOMPLETE_COMPONENT_IMPORTS,
  ],
})
export class EntityBulkActionsComponent {
  /**
   * List of selected entities for bulk actions
   */
  entities = input.required<Entity[]>();
  private isExecutingAction = false;

  /**
   * Event emitted when the bulk action mode should be exited
   * after an action was executed or the user cancelled the bulk actions.
   */
  resetBulkActionMode = output();

  private readonly actionsService = inject(EntityActionsMenuService);

  /**
   * Available bulk actions for the current selection
   */
  bulkActions = resource({
    params: () => ({ entities: this.entities() }),
    loader: async ({ params }) => {
      const bulkActions = await this.actionsService.getActionsForBulk(
        params.entities,
      );
      return bulkActions
        .map((action) => {
          if (action.action === "merge") {
            return {
              ...action,
              disabled: !this.entities() || this.entities().length !== 2,
            };
          }
          return action;
        })
        .filter((action) => !!action);
    },
    defaultValue: [],
  });

  actionControl = new FormControl();
  actionToString = (action: EntityAction) => action?.label || "";

  constructor() {
    this.actionControl.valueChanges.subscribe((action) => {
      if (action) this.onActionSelected(action);
    });
    // Enable/disable actionControl based on entities selection
    effect(() => {
      const entities = this.entities();
      if (!entities || entities.length === 0) {
        this.actionControl.disable({ emitEvent: false });
      } else {
        this.actionControl.enable({ emitEvent: false });
      }
    });
  }

  async onActionSelected(action: EntityAction) {
    if (this.isExecutingAction) {
      return;
    }

    this.isExecutingAction = true;
    if (action && typeof action.execute === "function") {
      await action.execute(this.entities());
    }

    this.resetBulkActionMode.emit();
    this.actionControl.setValue(null, { emitEvent: false });
    this.isExecutingAction = false;
  }

  cancel() {
    this.resetBulkActionMode.emit();
  }
}
<div class="flex-row gap-regular overall-box">
  <div class="bulk-action-select-wrapper">
    <mat-form-field class="bulk-action-autocomplete full-width">
      <mat-label i18n>Select Bulk Action</mat-label>

      <app-basic-autocomplete
        [formControl]="actionControl"
        [options]="bulkActions.value()"
        [optionToString]="actionToString"
        display="text"
      ></app-basic-autocomplete>

      @if (!entities() || entities().length === 0) {
        <mat-hint i18n>
          Please select one or more rows to enable bulk actions.
        </mat-hint>
      } @else {
        <mat-hint i18n>
          Action will execute on {{ entities().length }} selected records.
        </mat-hint>
      }
    </mat-form-field>
  </div>
  <button mat-raised-button (click)="cancel()" i18n>Cancel</button>
</div>

./entity-bulk-actions.component.scss

@use "@angular/material/core/style/elevation" as mat-elevation;

.overall-box {
  @include mat-elevation.elevation(3);
  padding: 1em;
  margin: 1em;
}

.bulk-action-select-wrapper {
  position: relative;
  flex: 1 1 0%;
  min-width: 320px;
  max-width: 700px;
  width: 400px; /* Fixed width for stability */
}

.bulk-action-autocomplete {
  /* remove form-field bottom spacing */
  margin-bottom: 0;
}
.bulk-action-autocomplete.full-width {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""