src/app/core/import/import-file/import-file.component.ts

Description

Import sub-step: Let user load a file and return parsed data.

Example

Metadata

Relationships

Used by

Depends on

Index

Properties
Methods
Inputs
Outputs
Accessors

Inputs

additionalSettings
Type : ImportAdditionalSettings
entityType
Type : string

Outputs

dataLoaded
Type : EventEmitter
additionalSettings
Type : ImportAdditionalSettings

Methods

onFileLoad
onFileLoad(parsedData: ParsedData<any>)

Handle a freshly parsed file emitted by the parsed-file-input child. Pre-selects the column-separator dropdown to the auto-detected delimiter (which may be outside the defaults — the dropdown will include it automatically via separatorOptions). Falls back to comma only when nothing was detected.

Parameters :
Name Type Optional
parsedData ParsedData<any> No
Returns : void
onSeparatorChange
onSeparatorChange(value: string | undefined)

Handle a user-driven change of the column-separator dropdown: remember the choice and ask the parsed-file-input to re-parse the cached file content with the new delimiter (which will re-emit dataLoaded).

Parameters :
Name Type Optional
value string | undefined No
Returns : void
onTrimValuesChange
onTrimValuesChange(value: boolean)
Parameters :
Name Type Optional
value boolean No
Returns : void
Public reset
reset()
Returns : void

Properties

createCustomSeparator
Type : unknown
Default value : () => {...}
data
Type : ParsedData<any>
Readonly multiValueSeparatorOptions
Type : string[]
Default value : [",", ";"]
parsedFileInputField
Type : ParsedFileInputComponent
Decorators :
@ViewChild(ParsedFileInputComponent)
Readonly selectedDelimiter
Type : unknown
Default value : signal<string | undefined>(undefined)
Readonly separatorOptions
Type : unknown
Default value : computed<string[]>(() => { const detected = this.selectedDelimiter(); if (detected && !this.defaultSeparatorOptions.includes(detected)) { return [...this.defaultSeparatorOptions, detected]; } return this.defaultSeparatorOptions; })

Dropdown options: the project-supported defaults plus the auto-detected delimiter if PapaParse picked something outside the defaults (e.g. tab, ASCII control chars). This keeps the dropdown in sync with the actual delimiter used to parse the file, so the user always sees and can override the real value.

Readonly trimValues
Type : unknown
Default value : computed( () => this.additionalSettings()?.trimValues !== false, )

Accessors

multiValueSeparator
getmultiValueSeparator()
setmultiValueSeparator(value: string)
Parameters :
Name Type Optional
value string No
Returns : void
import {
  Component,
  EventEmitter,
  Output,
  ViewChild,
  ChangeDetectionStrategy,
  signal,
  computed,
  input,
  model,
} from "@angular/core";
import {
  ParsedFileInputComponent,
  ParsedData,
} from "../../common-components/parsed-file-input/parsed-file-input.component";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatExpansionModule } from "@angular/material/expansion";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { FormsModule } from "@angular/forms";
import { HelpButtonComponent } from "../../common-components/help-button/help-button.component";
import { BasicAutocompleteComponent } from "../../common-components/basic-autocomplete/basic-autocomplete.component";
import { ImportAdditionalSettings } from "../import-additional-settings";

/**
 * Import sub-step: Let user load a file and return parsed data.
 */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-import-file",
  templateUrl: "./import-file.component.html",
  styleUrls: ["./import-file.component.scss"],
  imports: [
    ParsedFileInputComponent,
    MatFormFieldModule,
    MatExpansionModule,
    MatCheckboxModule,
    FormsModule,
    HelpButtonComponent,
    BasicAutocompleteComponent,
  ],
})
export class ImportFileComponent {
  entityType = input<string>();
  additionalSettings = model<ImportAdditionalSettings>({});

  @Output() dataLoaded = new EventEmitter<ParsedData<any>>();

  data: ParsedData<any>;
  readonly selectedDelimiter = signal<string | undefined>(undefined);

  private readonly defaultSeparatorOptions: string[] = [",", ";", "|"];

  /**
   * Dropdown options: the project-supported defaults plus the
   * auto-detected delimiter if PapaParse picked something outside the
   * defaults (e.g. tab, ASCII control chars). This keeps the dropdown
   * in sync with the actual delimiter used to parse the file, so the
   * user always sees and can override the real value.
   */
  readonly separatorOptions = computed<string[]>(() => {
    const detected = this.selectedDelimiter();
    if (detected && !this.defaultSeparatorOptions.includes(detected)) {
      return [...this.defaultSeparatorOptions, detected];
    }
    return this.defaultSeparatorOptions;
  });

  @ViewChild(ParsedFileInputComponent)
  parsedFileInputField: ParsedFileInputComponent;

  readonly multiValueSeparatorOptions: string[] = [",", ";"];

  get multiValueSeparator(): string {
    return this.additionalSettings()?.multiValueSeparator ?? ",";
  }

  set multiValueSeparator(value: string) {
    this.additionalSettings.update((settings) => ({
      ...settings,
      multiValueSeparator: value,
    }));
  }

  readonly trimValues = computed(
    () => this.additionalSettings()?.trimValues !== false,
  );

  onTrimValuesChange(value: boolean) {
    this.additionalSettings.update((settings) => ({
      ...settings,
      trimValues: value,
    }));
  }

  createCustomSeparator = async (input: string) => input;

  /**
   * Handle a freshly parsed file emitted by the parsed-file-input child.
   * Pre-selects the column-separator dropdown to the auto-detected
   * delimiter (which may be outside the defaults — the dropdown will
   * include it automatically via `separatorOptions`). Falls back to
   * comma only when nothing was detected.
   */
  onFileLoad(parsedData: ParsedData<any>) {
    this.data = parsedData;
    this.selectedDelimiter.set(parsedData.detectedDelimiter || ",");
    this.dataLoaded.emit(parsedData);
  }

  /**
   * Handle a user-driven change of the column-separator dropdown:
   * remember the choice and ask the parsed-file-input to re-parse the cached
   * file content with the new delimiter (which will re-emit `dataLoaded`).
   */
  onSeparatorChange(value: string | undefined) {
    if (!value || value === this.selectedDelimiter()) {
      return;
    }
    this.selectedDelimiter.set(value);
    this.parsedFileInputField?.reparseWithDelimiter(value);
  }

  public reset() {
    delete this.data;
    this.selectedDelimiter.set(undefined);
    this.parsedFileInputField.formControl.reset();
  }
}
<p i18n>Select a .xlsx or .csv file with data to import:</p>

<app-parsed-file-input
  (fileLoad)="onFileLoad($event)"
  [fileType]="['csv', 'xlsx']"
></app-parsed-file-input>

@if (data) {
  <div i18n>{{ data.data?.length }} rows detected for import</div>

  <mat-expansion-panel class="advanced-options-panel">
    <mat-expansion-panel-header>
      <mat-panel-title i18n="CSV advanced options panel title"
        >Advanced Options</mat-panel-title
      >
    </mat-expansion-panel-header>

    <div class="settings-row flex-row align-center gap-small">
      <mat-form-field>
        <mat-label i18n="CSV column separator label"
          >Column separator</mat-label
        >
        <app-basic-autocomplete
          [ngModel]="selectedDelimiter()"
          (ngModelChange)="onSeparatorChange($event)"
          [options]="separatorOptions()"
        ></app-basic-autocomplete>
      </mat-form-field>
      <app-help-button
        text="The character that separates columns in your CSV file.
              It is auto-detected from your file - only change this if the columns appear incorrect after import."
        i18n-text="CSV column separator - help text"
      ></app-help-button>
    </div>

    <div class="settings-row flex-row align-center gap-small">
      <mat-form-field>
        <mat-label i18n>Multi-value separator</mat-label>
        <app-basic-autocomplete
          [(ngModel)]="multiValueSeparator"
          [options]="multiValueSeparatorOptions"
          [createOption]="createCustomSeparator"
        ></app-basic-autocomplete>
      </mat-form-field>
      <app-help-button
        text="Which character is used to separate multiple values in a single column cell for array-type fields (e.g., multi-select options)."
        i18n-text="import - multi-value separator - help text"
      ></app-help-button>
    </div>
    <div class="settings-row flex-row align-center gap-small">
      <mat-checkbox
        [ngModel]="trimValues()"
        (ngModelChange)="onTrimValuesChange($event)"
        i18n
      >
        Trim leading/trailing spaces from values
      </mat-checkbox>
      <app-help-button
        text="Automatically remove leading and trailing spaces from all text values during import."
        i18n-text="import - trim values - help text"
      ></app-help-button>
    </div>
  </mat-expansion-panel>
}

./import-file.component.scss

.advanced-options-panel {
  margin-top: 1rem;
}

.settings-row {
  margin-bottom: 0.5rem;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""