trimble-oss / modus-web-components

This library provides Modus components as web components - reusable, encapsulated UI elements that are framework agnostic (can be implemented in any site).
https://modus-web-components.trimble.com/
MIT License
32 stars 66 forks source link

Select: Initial loading of modus-select elements in angular does not seem to work properly #2685

Open FromirDread opened 1 month ago

FromirDread commented 1 month ago

Prerequisites

Describe the issue

Hello, I have implemented the following directive to the best of my knowledge and belief.

If I get JSON for an element selected = true, this element should be selected for the user.

Currently I can only see that in the HTML Dom the value matches my expected selection (= document.getElementById) but the value visually still remains at the default value.

I guess, I have tried everything what cames into my mind. If there are some default example available, then please share then I'm maybe able to find some differences.

import {  Directive,  forwardRef,  ElementRef,  HostListener,  Input,  OnInit,  Output,  EventEmitter,  ChangeDetectorRef} from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import {DropDownFieldItem} from "../model/item/drop-down-field-item";
import {TranslateService} from "@ngx-translate/core";

@Directive({
  selector: 'modus-select',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ModusSelectDirective),
      multi: true,
    },
  ],
})
export class ModusSelectDirective implements ControlValueAccessor, OnInit {
  @Input() disabled: boolean | undefined;
  @Input() errorText: string | undefined;
  @Input() formControl: FormControl | undefined;
  @Input() helperText: string | undefined;
  @Input() label: string | undefined;
  @Input() required: boolean | undefined;
  @Input() size: 'medium' | 'large' = 'medium';
  @Input() validText: string | undefined;
  @Input() items: any[] | undefined;

  @Output() valueChange = new EventEmitter<string>();

  onChange: any = () => {};
  onTouched: any = () => {};

  private _value: string = '';

  get value() {
    return this._value;
  }

  set value(value) {
    if (value !== this._value) {
      this._value = value;
      this.onChange(this._value);
      this.onTouched();
      this.elementRef.nativeElement.value = value;
      this.cdr.detectChanges();
    }
  }

  setItems(items: any[]): void {
    if (this.isValidItemsArray(items)) {
      this.items = items;
    } else {
      console.warn('Invalid items array passed to ModusSelectDirective');
      this.items = undefined;
    }
  }

  constructor(private elementRef: ElementRef, private translationService: TranslateService,
              private cdr: ChangeDetectorRef) {}

  ngOnInit(): void {
    const modusSelect = this.elementRef.nativeElement as HTMLModusSelectElement;
    modusSelect.disabled = this.disabled ?? false;
    modusSelect.errorText = this.errorText!;
    modusSelect.helperText = this.helperText!;
    modusSelect.label = this.label!;
    modusSelect.options = [];
    modusSelect.optionsDisplayProp = 'display';
    modusSelect.required = this.required ?? false;
    modusSelect.size = this.size;
    modusSelect.validText = this.validText!;

    if(this.items != undefined && this.isValidItemsArray(this.items)){
      this.items.forEach((item: DropDownFieldItem) => {
        modusSelect.options.push({display: this.normalizeHyphens(this.translationService.instant(item.text))});

        if(item.selected){
          this.value = this.normalizeHyphens(this.translationService.instant(item.text));
        }
      });
    }

    if (!this.formControl) {
      this.formControl = new FormControl(null);
    }

    this.updateVisualSelection(this.value);
  }

  ....

  writeValue(value: string): void {
    if (value) {
      this.value = value;
    }
  }

  isValidItemsArray(items: any[]): items is DropDownFieldItem[] {
    return !!items && items.every(item => typeof item === 'object' && 'text' in item);
  }

  private updateVisualSelection(value: string): void {
    const modusSelectElement = this.elementRef.nativeElement;
    const matchingItem = this.items?.find(item => {
      return item.value === value;
    });
    if (matchingItem) {
      modusSelectElement.value = matchingItem.value;
      this.cdr.detectChanges();
    }
  }

  private normalizeHyphens(input: string): string {
    // Replace en dash, em dash, and hyphen-minus with a standard hyphen (-)
    return input.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212\u002D]/g, '-');
  }
}

Reduced test cases

No response

What operating system(s) are you seeing the problem on?

Windows

What browser(s) are you seeing the problem on?

Chrome

What is the issue regarding ?

@trimble-oss/modus-angular-components

What version of npm package are you using ?

^0.33.1-ng17

Priority

Medium

What product/project are you using Modus Components for ?

MDSYM / MYRA

What is your team/division name ?

DSYM, squad emerald

Are you willing to contribute ?

None

Are you using Modus Web Components in production ?

No, just starting to migrate

github-actions[bot] commented 1 month ago

Hello @FromirDread! Thanks for opening an issue. The Modus core team will get back to you soon (usually within 24-hours) and provide guidance on how to proceed. Contributors are welcome to participate in the discussion and provide their input on how to best solve the issue, and even submit a PR if they want to.

Please wait until the issue is ready to be worked on before submitting a PR, or you can reach out to the core team if it is time bound. For trivial things, or bugs that don't change the expected behaviors and UI, you can go ahead and make a PR.

prashanth-offcl commented 5 days ago

@prashanth-offcl to check this issue