Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select - Compare with and customizable select value to allow for object values #453

Open
1 of 2 tasks
CO97 opened this issue Nov 8, 2024 · 1 comment
Open
1 of 2 tasks
Labels
enhancement New feature or request

Comments

@CO97
Copy link

CO97 commented Nov 8, 2024

Which scope/s are relevant/related to the feature request?

Don't know / other

Information

Add a compareWith function similar to material to allow values within the select component to be objects. In many uses you would want to use an object as the value.

Changes would also be required to how the selected value is displayed. By default we could display the property for a key passed in but add ability to expose and allow the user to display based on selected values.

This would also open up the ability to display icons, X, Y + Z more in the selected value field. Giving developers more control on how their select controls look.

Describe any alternatives/workarounds you're currently using

Currently I find the matching value for the object and use a custom select value component to display selected values.

Example Code;

import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  computed,
  ContentChild,
  inject,
  Input,
  TemplateRef
} from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
import { BrnSelectService } from '@spartan-ng/ui-select-brain';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { NoSelectedTemplateDirective, SelectedTemplateDirective } from './selected-templates.directives';

@Component({
  selector: 'custom-hlm-select-value',
  standalone: true,
  template: `
    @if (!!value) {
      <ng-container
        *ngTemplateOutlet="selectedTemplate ?? defaultSelected; context: { selectedOptions: selectedOptionValues() }"></ng-container>
    } @else {
      <ng-container *ngTemplateOutlet="noSelectedTemplate ?? defaultNoSelected"></ng-container>
    }

    <ng-template #defaultNoSelected>
      {{ placeholder() }}
    </ng-template>

    <ng-template #defaultSelected>
      {{ value }}
    </ng-template>
  `,
  host: {
    '[id]': 'id()'
  },
  styles: [
    `
      :host {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 1;
        white-space: nowrap;
        pointer-events: none;
      }
    `
  ],
  imports: [NgTemplateOutlet],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomHlmSelectValueComponent {
  private readonly _selectService = inject(BrnSelectService);

  public readonly id = computed(() => `${this._selectService.id()}--value`);
  public readonly placeholder = computed(() => this._selectService.placeholder());
  public readonly selectedOptionValues = computed(() => {
    const selectedOptions = this._selectService.selectedOptions();
    if (!selectedOptions) {
      return null;
    }
    return (selectedOptions ?? []).filter((val) => !!val).map(({ value }) => value);
  });

  value: string | null = null;

  @ContentChild(NoSelectedTemplateDirective, { read: TemplateRef }) noSelectedTemplate?: TemplateRef<void>;
  @ContentChild(SelectedTemplateDirective, { read: TemplateRef }) selectedTemplate?: TemplateRef<{
    selectedItems: unknown | null
  }>;

  @Input()
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  transformFn: (values: (string | undefined)[]) => any = (values) => (values ?? []).join(', ');

  constructor() {
    const cdr = inject(ChangeDetectorRef);

    // In certain cases (when using a computed signal for value) where the value of the select and the options are
    // changed dynamically, the template does not update until the next frame. To work around this we can use a simple
    // string variable in the template and manually trigger change detection when we update it.
    toObservable(this._selectService.selectedOptions)
      .pipe(takeUntilDestroyed())
      .subscribe((value) => {
        if (value.length === 0) {
          this.value = null;
          cdr.detectChanges();
          return;
        }
        const selectedLabels = value.map((selectedOption) => selectedOption?.getLabel());

        if (this._selectService.dir() === 'rtl') {
          selectedLabels.reverse();
        }
        const result = this.transformFn(selectedLabels);
        this.value = result;
        cdr.detectChanges();
      });
  }
}

I would be willing to submit a PR to fix this issue

  • Yes
  • No
@CO97 CO97 added the enhancement New feature or request label Nov 8, 2024
@CO97
Copy link
Author

CO97 commented Nov 11, 2024

I am happy to take up this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant