@angular/forms#NG_VALUE_ACCESSOR TypeScript Examples

The following examples show how to use @angular/forms#NG_VALUE_ACCESSOR. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: boolean-value-accessor.directive.ts    From elements with MIT License 6 votes vote down vote up
@Directive({
  selector: 'ino-checkbox,ino-control-item[role="checkbox"],ino-switch',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: BooleanValueAccessorDirective,
      multi: true,
    },
  ],
})
export class BooleanValueAccessorDirective extends ValueAccessorDirective {
  constructor(el: ElementRef, private renderer: Renderer2) {
    super(el);
  }

  @HostListener('checkedChange', ['$event.detail'])
  _handleInoChange(value: any) {
    this.handleChangeEvent(value);
  }

  writeValue(value: any): void {
    this.el.nativeElement.checked = this.lastValue = value == null ? '' : value;
  }
}
Example #2
Source File: form-component-superclass.ts    From s-libs with MIT License 6 votes vote down vote up
/**
 * Use in the `providers` of a component that implements `ControlValueAccessor` to reduce some boilerplate.
 *
 * ```ts
 * @Component({ providers: [provideValueAccessor(MyFormControl)] }
 * class MyFormControl extends BaseFormControl {
 *   // ...
 * }
 * ```
 */
export function provideValueAccessor(type: Type<any>): Provider {
  return {
    provide: NG_VALUE_ACCESSOR,
    useExisting: type,
    multi: true,
  };
}
Example #3
Source File: text-value-accessor.directive.ts    From elements with MIT License 6 votes vote down vote up
@Directive({
  selector:
    'ino-autocomplete,ino-currency-input,ino-input,ino-textarea,ino-range,ino-select,ino-datepicker',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: TextValueAccessorDirective,
      multi: true,
    },
  ],
})
export class TextValueAccessorDirective extends ValueAccessorDirective {
  constructor(el: ElementRef) {
    super(el);
  }
  @HostListener('valueChange', ['$event.detail'])
  _handleInputEvent(value: string): void {
    this.handleChangeEvent(value);
  }

  @HostListener('inoBlur')
  _handleInoBlur() {
    this.handleBlurEvent();
  }
}
Example #4
Source File: switch.component.ts    From alauda-ui with MIT License 6 votes vote down vote up
@Component({
  selector: 'aui-switch',
  templateUrl: './switch.component.html',
  styleUrls: ['./switch.component.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  preserveWhitespaces: false,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SwitchComponent),
      multi: true,
    },
  ],
})
export class SwitchComponent extends CommonFormControl<boolean> {
  bem: Bem = buildBem(prefix);

  @Input()
  loading = false;

  onSwitch() {
    if (this.disabled) {
      return;
    }
    this.emitValue(!this.model);
  }

  onBlur() {
    if (this.onTouched) {
      this.onTouched();
    }
  }
}
Example #5
Source File: mood-radio.component.ts    From onchat-web with Apache License 2.0 6 votes vote down vote up
@Component({
  selector: 'app-mood-radio',
  templateUrl: './mood-radio.component.html',
  styleUrls: ['./mood-radio.component.scss'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => MoodRadioComponent),
    multi: true
  }]
})
export class MoodRadioComponent implements ControlValueAccessor {
  private _value: Mood;

  set value(value: Mood) {
    this._value = value;
    this.onValueChange(value);
  }

  get value(): Mood {
    return this._value;
  }

  readonly mood: typeof Mood = Mood;

  constructor() { }

  private onValueChange(value: Mood) { }

  writeValue(value: Mood): void {
    this._value = value;
  }

  registerOnChange(fn: SafeAny): void {
    this.onValueChange = fn;
  }

  registerOnTouched(fn: SafeAny): void { }
}
Example #6
Source File: datetime-input.ts    From angular-material-components with MIT License 5 votes vote down vote up
MAT_DATEPICKER_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => NgxMatDatetimeInput),
    multi: true
}
Example #7
Source File: warehouse-manage-form.component.ts    From ng-ant-admin with MIT License 5 votes vote down vote up
EXE_COUNTER_VALUE_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  multi: true,
  useExisting: forwardRef(() => WarehouseManageFormComponent)
}
Example #8
Source File: datetime-input.ts    From ngx-mat-datetime-picker with MIT License 5 votes vote down vote up
MAT_DATEPICKER_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => NgxMatDatetimeInput),
    multi: true
}
Example #9
Source File: markdown.component.ts    From ng-util with MIT License 5 votes vote down vote up
@Component({
  selector: 'nu-markdown',
  template: ``,
  exportAs: 'nuMarkdown',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NuMarkdownComponent),
      multi: true,
    },
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NuMarkdownComponent extends NuMarkdownBaseComponent implements ControlValueAccessor {
  private onChange = (_: string) => {};

  protected init(): void {
    this.ngZone.runOutsideAngular(() => {
      const options = {
        value: this._value,
        cache: {
          enable: false,
        },
        mode: 'sv',
        minHeight: 350,
        input: (value: string) => {
          this.ngZone.run(() => {
            this._value = value;
            this.onChange(value);
          });
        },
        ...this.config?.defaultOptions,
        ...this.options,
      };
      this._instance = new Vditor(this.el.nativeElement, options);
      this.ngZone.run(() => this.ready.emit(this._instance));
    });
  }

  private setDisabled(): void {
    if (!this.instance) {
      return;
    }
    if (this.disabled) {
      this.instance.disabled();
    } else {
      this.instance.enable();
    }
  }

  writeValue(value: string): void {
    this._value = value || '';
    if (this.instance) {
      this.instance.setValue(this._value);
    }
  }

  registerOnChange(fn: (_: string) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(_: () => void): void {}

  setDisabledState(_isDisabled: boolean): void {
    this.disabled = _isDisabled;
    this.setDisabled();
  }
}
Example #10
Source File: color-input.component.ts    From angular-material-components with MIT License 5 votes vote down vote up
MAT_COLORPICKER_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => NgxMatColorPickerInput),
  multi: true
}
Example #11
Source File: custom-inputs-fields-form.component.ts    From fyle-mobile-app with MIT License 5 votes vote down vote up
@Component({
  selector: 'app-custom-inputs-fields-form',
  templateUrl: './custom-inputs-fields-form.component.html',
  styleUrls: ['./custom-inputs-fields-form.component.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: CustomInputsFieldsFormComponent, multi: true }],
})
export class CustomInputsFieldsFormComponent implements OnInit, ControlValueAccessor, OnDestroy, OnChanges {
  @Input() customInputs: CustomInputs[];

  @Input() combinedCustomProperties: CombinedOptions;

  @Input() disableFormElements: boolean;

  onChangeSub: Subscription;

  customFieldsForm: FormGroup;

  customFields: CustomInputs[];

  constructor(private formBuilder: FormBuilder, private injector: Injector) {}

  ngOnInit() {
    this.customFieldsForm = this.formBuilder.group({
      fields: new FormArray([]),
    });
  }

  generateCustomForm() {
    const customFieldsFormArray = this.customFieldsForm?.controls?.fields as FormArray;
    customFieldsFormArray.clear();
    for (const customField of this.customInputs) {
      customFieldsFormArray.push(
        this.formBuilder.group({
          name: [customField.name],
          value: [customField.value],
        })
      );
    }
    customFieldsFormArray.updateValueAndValidity();
    this.customFields = this.customInputs.map((customField, i) => ({
      ...customField,
      control: customFieldsFormArray.at(i),
    }));
  }

  onTouched = () => {};

  ngOnDestroy(): void {
    this.onChangeSub.unsubscribe();
  }

  ngOnChanges() {
    if (this.customFieldsForm?.controls) {
      this.generateCustomForm();
    }
  }

  writeValue(value: any) {
    if (value) {
      this.customFieldsForm.controls.fields.patchValue(value);
    }
  }

  registerOnChange(onChange): void {
    this.onChangeSub = this.customFieldsForm.valueChanges.subscribe(onChange);
  }

  registerOnTouched(onTouched): void {
    this.onTouched = onTouched;
  }
}
Example #12
Source File: fs-value-accessor.directive.ts    From elements with MIT License 5 votes vote down vote up
@Directive({
  selector: 'ino-input-file,input[type=file]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: FsValueAccessorDirective,
      multi: true,
    },
  ],
})
export class FsValueAccessorDirective extends ValueAccessorDirective {
  constructor(el: ElementRef) {
    super(el);
  }

  @HostListener('changeFile', ['$event.detail'])
  _handleInputEvent(value: any): void {
    if (this.el.nativeElement.multiple) {
      this.handleChangeEvent(value.files);
    } else {
      this.handleChangeEvent(value.files[0]);
    }
  }

  writeValue(value: any): void {
    if (value instanceof FileList) {
      this.el.nativeElement.files = value;
    } else if (Array.isArray(value) && !value.length) {
      this.el.nativeElement.files = null;
    } else if (value === null) {
      this.el.nativeElement.files = null;
    } else {
      // Since we cannot manually construct a FileList instance, we have to ignore
      // any attempt to push a non-FileList instance into the input.
      if (console && console.warn && console.log) {
        console.warn(
          'Ignoring attempt to assign non-FileList to input[type=file].'
        );
        console.log('Value:', value);
      }
    }
  }
}
Example #13
Source File: knob.ts    From EDA with GNU Affero General Public License v3.0 5 votes vote down vote up
KNOB_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => Knob),
    multi: true
}
Example #14
Source File: select.component.ts    From xBull-Wallet with GNU Affero General Public License v3.0 5 votes vote down vote up
@Component({
  selector: 'app-select',
  templateUrl: './select.component.html',
  styleUrls: ['./select.component.scss'],
  providers: [
  {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SelectComponent),
    multi: true
  }
]
})
export class SelectComponent implements OnInit {
  @Input() options: ISelectOptions[] = [];
  @Input() disabled = false;
  @Input() title = 'Input';
  @Input() placeholder = 'Select a value...';
  @Input() mode: 'dark' | 'light' = 'dark';
  @Input() iconPath?: string;

  value = '';

  onChange: (quantity: any) => void = (quantity) => {};

  onTouched: () => void = () => {};

  constructor() { }

  ngOnInit(): void {
  }

  onInput(value: any): void {
    this.writeValue(value);
    this.onTouched();
    this.onChange(this.value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  writeValue(value: any): void {
    if (!!value) {
      this.value = value;
    } else {
      this.value = '';
    }
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

}
Example #15
Source File: account-select.component.ts    From digital-bank-ui with Mozilla Public License 2.0 5 votes vote down vote up
@Component({
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AccountSelectComponent), multi: true }],
  selector: 'ngx-account-select',
  templateUrl: './account-select.component.html',
})
export class AccountSelectComponent implements ControlValueAccessor, OnInit {
  formControl: FormControl;

  @Input() title: string;

  @Input() required: boolean;

  @Input() type: AccountType;

  accounts: Observable<Account[]>;

  _onTouchedCallback: () => void = noop;

  private _onChangeCallback: (_: any) => void = noop;

  constructor(private accountingService: AccountingService) {}

  ngOnInit(): void {
    this.formControl = new FormControl('');

    this.accounts = this.formControl.valueChanges.pipe(
      distinctUntilChanged(),
      debounceTime(500),
      tap(name => this.changeValue(name)),
      filter(name => name),
      switchMap(name => this.onSearch(name)),
    );
  }

  changeValue(value: string): void {
    this._onChangeCallback(value);
  }

  writeValue(value: any): void {
    this.formControl.setValue(value);
  }

  registerOnChange(fn: any): void {
    this._onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouchedCallback = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    if (isDisabled) {
      this.formControl.disable();
    } else {
      this.formControl.enable();
    }
  }

  onSearch(searchTerm?: string): Observable<Account[]> {
    const fetchRequest: FetchRequest = {
      page: {
        pageIndex: 0,
        size: 5,
      },
      searchTerm: searchTerm,
    };

    return this.accountingService.fetchAccounts(fetchRequest, this.type).pipe(map((accountPage: AccountPage) => accountPage.accounts));
  }

}
Example #16
Source File: nas-model.directive.ts    From s-libs with MIT License 5 votes vote down vote up
constructor(
    @Self()
    @Inject(NG_VALUE_ACCESSOR)
    valueAccessors: ControlValueAccessor[],
  ) {
    this.#valueAccessor = valueAccessors[0];
  }
Example #17
Source File: date-picker.ts    From sba-angular with MIT License 5 votes vote down vote up
DATE_PICKER_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => BsDatePicker),
    multi: true
}
Example #18
Source File: checkbox.component.ts    From taiga-front-next with GNU Affero General Public License v3.0 5 votes vote down vote up
@Component({
  selector: 'tg-checkbox',
  templateUrl: './checkbox.component.html',
  styleUrls: ['./checkbox.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: TgCheckboxComponent,
      multi: true,
    },
  ],
})
export class TgCheckboxComponent implements ControlValueAccessor {

  constructor(
    private renderer: Renderer2
  ) {}

  @ViewChild('checkbox') public checkbox: ElementRef;
  @Input() labelPosition: LabelPosition = 'after';
  @Input() ariaLabel?: string | null = null;
  @Input() id = `tg-checkbox-${nextId++}`;

  // tslint:disable-next-line: variable-name
  onChange = (_isChecked: boolean) => {};
  onTouched = () => {};

  public writeValue(isChecked: boolean): void {
    this.onChange(isChecked);
  }

  public registerOnChange(fn: (isDisabled: boolean) => void): void {
    this.onChange = fn;
  }

  public registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  public setDisabledState(isDisabled: boolean): void {
    this.renderer.setProperty(this.checkbox.nativeElement, 'disabled', isDisabled);
  }

  public check = (event: any): void => {
    const isChecked: boolean = event.target.checked;
    this.writeValue(isChecked);
  }
}
Example #19
Source File: input.component.ts    From xBull-Wallet with GNU Affero General Public License v3.0 5 votes vote down vote up
@Component({
  selector: 'app-input',
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true
    }
  ]
})
export class InputComponent implements OnInit, ControlValueAccessor {

  @Input() mask = '';
  @Input() disabled = false;
  @Input() title = 'Input';
  @Input() type: 'text' | 'number' | 'password' = 'text';
  @Input() placeholder = 'Write here...';
  @Input() mode: 'dark' | 'light' = 'dark';
  @Input() iconPath?: string;

  @Output() enter: EventEmitter<InputEvent> = new EventEmitter<InputEvent>();

  value = '';

  control = new FormControl('');

  onChange: (quantity: any) => void = (quantity) => {};

  onTouched: () => void = () => {};

  ngOnInit(): void {
  }

  onInput(value: any): void {
    this.writeValue(this.control.value);
    this.onTouched();
    this.onChange(this.control.value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  writeValue(value: any): void {
    if (!!value) {
      this.control.setValue(value);
      this.value = value;
    } else {
      this.value = '';
    }
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  onEnter(event: any): void {
    this.enter.emit(event);
  }

}
Example #20
Source File: range-picker.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
@Component({
  selector: 'aui-range-picker',
  templateUrl: './range-picker.template.html',
  // provide tooltip class
  encapsulation: ViewEncapsulation.Emulated,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RangePickerComponent),
      multi: true,
    },
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./range-picker.style.scss'],
})
export class RangePickerComponent extends CommonFormControl<
  ConfigType[],
  Dayjs[]
> {
  @Input()
  clearable = true;

  @Input()
  clearText: string;

  @Input()
  format = 'YYYY-MM-DD';

  @Input()
  showFooter = true;

  @Input()
  showTime = true;

  @Input()
  disabledDate: DisabledDateFn = () => false;

  @Input()
  minDate: Dayjs;

  @Input()
  maxDate: Dayjs;

  @Input()
  disabledTime: { left: DisabledTimeFn; right: DisabledTimeFn } = {
    left: () => null,
    right: () => null,
  };

  @Input()
  weekStartDay = 0;

  @Input()
  size: ComponentSize;

  @Output()
  openChange = new EventEmitter<boolean>();

  value: [Dayjs, Dayjs];

  override valueIn(obj: [ConfigType, ConfigType]) {
    return obj?.map(i => dayjs(i));
  }

  override writeValue(obj: [Dayjs, Dayjs]) {
    super.writeValue(obj);
    this.value = obj;
    this.cdr.markForCheck();
  }

  clearValue() {
    this.value = null;
    this.emitValue(null);
  }
}
Example #21
Source File: textarea.component.ts    From xBull-Wallet with GNU Affero General Public License v3.0 5 votes vote down vote up
@Component({
  selector: 'app-textarea',
  templateUrl: './textarea.component.html',
  styleUrls: ['./textarea.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextareaComponent),
      multi: true
    }
  ]
})
export class TextareaComponent implements OnInit, ControlValueAccessor {
  @Input() disabled = false;
  @Input() title = 'Textarea';
  @Input() placeholder = 'Write here...';

  value = '';

  onChange: (quantity: any) => void = (quantity) => {};

  onTouched: () => void = () => {};

  constructor() { }

  ngOnInit(): void {
  }

  onInput(value: any): void {
    this.writeValue(value);
    this.onTouched();
    this.onChange(this.value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  writeValue(value: any): void {
    if (!!value) {
      this.value = value;
    } else {
      this.value = '';
    }
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

}
Example #22
Source File: rating.component.ts    From Angular-Cookbook with MIT License 5 votes vote down vote up
@Component({
  selector: 'app-rating',
  templateUrl: './rating.component.html',
  styleUrls: ['./rating.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RatingComponent),
      multi: true,
    },
  ],
})
export class RatingComponent implements OnInit, ControlValueAccessor {
  value = 2;
  hoveredRating = 2;
  isMouseOver = false;
  @Input() disabled = false;

  constructor() {}

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

  ngOnInit(): void {}
  onRatingMouseEnter(rating: number) {
    if (this.disabled) return;
    this.hoveredRating = rating;
    this.isMouseOver = true;
  }
  onRatingMouseLeave() {
    this.hoveredRating = null;
    this.isMouseOver = false;
  }

  selectRating(rating: number) {
    if (this.disabled) return;
    this.value = rating;
    this.onChange(rating);
  }

  registerOnChange(fn: any) {
    this.onChange = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  writeValue(value: number) {
    this.value = value;
  }
}
Example #23
Source File: checkbox-group.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
@Component({
  selector: 'aui-checkbox-group',
  templateUrl: './checkbox-group.component.html',
  styleUrls: ['./checkbox-group.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: false,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckboxGroupComponent),
      multi: true,
    },
  ],
})
export class CheckboxGroupComponent<T> extends CommonFormControl<T[]> {
  private _trackFn: TrackFn<T>;

  @Input()
  direction: 'row' | 'column' = 'row';

  @Input()
  get trackFn() {
    return this._trackFn;
  }

  set trackFn(val) {
    if (val !== this._trackFn) {
      this._trackFn = val;
    }
  }

  @ContentChildren(forwardRef(() => CheckboxComponent), {
    descendants: true,
  })
  checkboxes: QueryList<CheckboxComponent<T>>;

  onCheckboxChange(checkbox: CheckboxComponent<T>) {
    if (this.onTouched) {
      this.onTouched();
    }
    const values = this.checkboxes
      .filter(item => (item === checkbox ? !item.model : item.model))
      .map(item => item.label);
    this.emitValue(values);
  }

  onCheckboxBlur() {
    if (this.onTouched) {
      this.onTouched();
    }
  }
}
Example #24
Source File: password-input.component.ts    From ReCapProject-Frontend with MIT License 5 votes vote down vote up
@Component({
  selector: 'app-password-input',
  templateUrl: './password-input.component.html',
  styleUrls: ['./password-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => PasswordInputComponent),
      multi: true,
    },
  ],
  host: {
    '(change)': 'onChange($event.target.value)',
    '(input)': 'onChange($event.target.value)',
    '(blur)': 'onTouched()',
  },
})
export class PasswordInputComponent implements OnInit, ControlValueAccessor {
  @Input() id: string = 'password-input';
  @Input() label: string = 'Enter your password';
  @Input() invalidFeedback: string = 'Please enter your password.';
  passwordHidden: boolean = true;
  value: string = '';
  onChange: any = () => {};
  onTouched: any = () => {};
  disabled = false;

  @Input() errors: any;
  @Input() touched: any;
  @Input() dirty: any;

  constructor() {}

  ngOnInit(): void {}

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

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  togglePasswordHidden() {
    this.passwordHidden = !this.passwordHidden;
  }

  isPasswordHidden(): string {
    return this.passwordHidden ? 'password' : 'text';
  }

  isPasswordHiddenIcon(): string {
    return this.passwordHidden ? 'fa-eye-slash' : 'fa-eye text-primary';
  }
}
Example #25
Source File: validation-message.component.ts    From ngx-admin-dotnet-starter with MIT License 5 votes vote down vote up
@Component({
  selector: 'ngx-validation-message',
  styleUrls: ['./validation-message.component.scss'],
  template: `
      <div class="warning">
          <span class="caption status-danger"
             *ngIf="showMinLength"> Min {{ label }} length is {{ minLength }} symbols </span>
          <span class="caption status-danger"
             *ngIf="showMaxLength"> Max {{ label }} length is {{ maxLength }} symbols </span>
          <span class="caption status-danger" *ngIf="showPattern"> Incorrect {{ label }} </span>
          <span class="caption status-danger" *ngIf="showRequired"> {{ label }} is required</span>
          <span class="caption status-danger" *ngIf="showMin">Min value of {{ label }} is {{ min }}</span>
          <span class="caption status-danger" *ngIf="showMax">Max value of {{ label }} is {{ max }}</span>
      </div>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NgxValidationMessageComponent),
      multi: true,
    },
  ],
})
export class NgxValidationMessageComponent {
  @Input()
  label: string = '';

  @Input()
  showRequired?: boolean;

  @Input()
  min?: number;

  @Input()
  showMin?: boolean;

  @Input()
  max?: number;

  @Input()
  showMax: boolean;

  @Input()
  minLength?: number;

  @Input()
  showMinLength?: boolean;

  @Input()
  maxLength?: number;

  @Input()
  showMaxLength?: boolean;

  @Input()
  showPattern?: boolean;
}
Example #26
Source File: np-tags.component.ts    From np-ui-lib with MIT License 4 votes vote down vote up
@Component({
  selector: "np-tags",
  templateUrl: "./np-tags.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NpTagsComponent),
      multi: true,
    },
  ],
})
export class NpTagsComponent
  implements ControlValueAccessor, AfterViewInit, AfterContentInit, OnDestroy {
  private static controlCount = 1;

  @Input() displayKey: string;
  @Input() valueKey: string;
  @Input() searchResult: BehaviorSubject<any[]>;
  @Input() isServerSide: boolean;
  @Input() forceToSelect: boolean;
  @Input() itemTemplate: TemplateRef<any>;
  @Input() searchDelay = 1000;
  @Input() maxResultLimit: number;
  @Input() minSearchCharLimit: number;
  @Input() maxSelectLimit: number;
  @Input() orderBy: string;
  @Input() orderDir: string;
  @Input() placeholder = "";
  @Input() readOnly: boolean;
  @Input() autoFocus: boolean;
  @Input() tabIndex: number;
  @Input() styleClass: string;
  @Input() inputId: string = `np-tags_${NpTagsComponent.controlCount++}`;

  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Output() onSearch: EventEmitter<any> = new EventEmitter();
  @Output() onCreateNewTag: EventEmitter<any> = new EventEmitter();
  @Output() onFocus: EventEmitter<any> = new EventEmitter();
  @Output() onBlur: EventEmitter<any> = new EventEmitter();

  @ViewChild("templatePortalContent") templatePortalContent: TemplateRef<any>;
  @ViewChild("control") inputViewChild: ElementRef;

  selected: any[];
  subscription: Subscription;
  options: any[];
  displayValue: string;
  searchTimeout: any;
  isLoading: boolean = false;
  innerValue: any[];
  isDisabled: boolean = false;
  focused: boolean = false;

  private templatePortal: TemplatePortal<any>;
  private overlayRef: OverlayRef;
  private onChangeCallback: (_: any) => void = () => { };
  private onTouchedCallback: () => void = () => { };

  constructor(
    public overlay: Overlay,
    private viewContainerRef: ViewContainerRef,
    private overlayPositionBuilder: OverlayPositionBuilder,
    private elementRef: ElementRef,
    private utility: NpUtilityService
  ) { }

  ngAfterContentInit(): void {
    this.subscription = this.searchResult.subscribe((data) => {
      if (
        this.maxResultLimit &&
        this.maxResultLimit > 0 &&
        data &&
        data.length > this.maxResultLimit
      ) {
        this.options = data.splice(0, this.maxResultLimit);
      } else {
        this.options = data;
      }
      if (this.isServerSide) {
        this.isLoading = false;
        if (data) {
          if (!this.overlayRef.hasAttached()) {
            this.overlayRef.attach(this.templatePortal);
          }
        }
      }
    });
  }

  ngAfterViewInit(): void {
    const positionStrategy = this.overlayPositionBuilder
      .flexibleConnectedTo(this.elementRef)
      .withPositions(TopBottomOverlayPositions);
    this.overlayRef = this.overlay.create({
      positionStrategy,
      hasBackdrop: true,
      backdropClass: "np-tags-backdrop",
      scrollStrategy: this.overlay.scrollStrategies.reposition(),
      panelClass: this.styleClass,
    });
    this.templatePortal = new TemplatePortal(
      this.templatePortalContent,
      this.viewContainerRef
    );
    this.overlayRef.backdropClick().subscribe(() => this._close());
  }

  ngOnDestroy(): void {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }

  get value(): any[] {
    return this.innerValue ? this.innerValue : null;
  }

  set value(v: any[]) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
      this.onChange.emit(v);
    }
  }

  writeValue(v: any): void {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.selected = Object.assign([], v);
    }
  }

  registerOnChange(fn: any): void {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouchedCallback = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  focus(): void {
    this.inputViewChild.nativeElement.focus();
  }

  _close(): void {
    this.displayValue = null;
    this.overlayRef.detach();
    this.inputViewChild.nativeElement.focus();
  }

  _onInput($event: any): void {
    if (!$event.target.value) {
      return;
    }
    this.displayValue = $event.target.value.trim();
    if (this.isDisabled || this.readOnly || !this.isServerSide) {
      return;
    }
    if (this.minSearchCharLimit && this.minSearchCharLimit > 0) {
      if (
        this.displayValue === undefined ||
        this.displayValue === null ||
        this.displayValue.length < this.minSearchCharLimit
      ) {
        return;
      }
    }
    this.isLoading = true;
    if (this.searchTimeout) {
      clearTimeout(this.searchTimeout);
    }
    const searchKeyword = this.displayValue;
    this.searchTimeout = setTimeout(() => {
      this.onSearch.emit(searchKeyword);
    }, this.searchDelay);
  }

  _selectValue(val: any): void {
    if (this._isSelected(val)) {
      this._removeTag(val);
      return;
    }
    if (
      this.maxSelectLimit > 0 &&
      this.value &&
      this.value.length === this.maxSelectLimit
    ) {
      return;
    }
    const currentVal = this.valueKey ? val[this.valueKey] : val;
    if (!this.value) {
      this.value = [currentVal];
      this.selected = [val];
    } else {
      this.value.push(currentVal);
      this.selected.push(val);
      this.onChangeCallback(this.value);
      this.onChange.emit(this.value);
    }
  }

  _open(): void {
    if (this.isServerSide || this.isDisabled || this.readOnly) {
      return;
    }
    if (!this.overlayRef.hasAttached()) {
      this.overlayRef.attach(this.templatePortal);
    }
  }

  _createNewTag(): void {
    if (
      this.maxSelectLimit > 0 &&
      this.value &&
      this.value.length === this.maxSelectLimit
    ) {
      return;
    }
    if (this.options === undefined || this.options === null) {
      this.options = [];
    }
    let newObj: any;
    if (this.displayKey) {
      newObj = {};
      newObj[this.displayKey] = this.displayValue;
      if (this.valueKey) {
        newObj[this.valueKey] = this.displayValue;
      }
    } else {
      newObj = this.displayValue;
    }
    this.options.push(newObj);
    this._selectValue(newObj);
    this.onCreateNewTag.emit(newObj);
    this.displayValue = null;
  }

  _getValueFromTag(val: any): void {
    return this.displayKey ? val[this.displayKey] : val;
  }

  _removeTag(tag: any): void {
    const idx = this.selected.findIndex((element) => this.utility.isEqual(element, tag));
    if (idx > -1) {
      if (this.value.length === 1) {
        this.value = null;
        this.selected = null;
      } else {
        this.value.splice(idx, 1);
        this.selected.splice(idx, 1);
        this.onChangeCallback(this.value);
        this.onChange.emit(this.value);
      }
    }
  }

  _isSelected(tag: any): boolean {
    return this.selected && (this.selected.findIndex((element) => this.utility.isEqual(element, tag))) > -1;
  }

  _onKeydown(event: KeyboardEvent): void {
    if (event.key === "Tab" || event.key === "Escape") {
      this._close();
    }
    if (event.key === "ArrowDown") {
      this._open();
      event.preventDefault();
    }
    if (
      event.key === "Backspace" &&
      this.value &&
      this.value.length > 0 &&
      (this.displayValue === undefined ||
        this.displayValue === null ||
        this.displayValue.length === 0)
    ) {
      if (this.value.length > 1) {
        this.value.pop();
        this.selected.pop();
        this.onChangeCallback(this.value);
        this.onChange.emit(this.value);
      } else {
        this.value = null;
        this.selected = null;
      }
    }
    if (
      event.key === "Enter" &&
      !this.forceToSelect &&
      this.displayValue &&
      this.displayValue.length > 0
    ) {
      this._createNewTag();
    }
  }

  _trackBy(index: number): number {
    return index;
  }

  _getDisplayValue(): string {
    return this.displayValue || "";
  }

  _onBlur($event: any): void {
    this.focused = false;
    this.onTouchedCallback();
    this.onBlur.emit($event);
  }

  _onFocus($event: any): void {
    this.focused = true;
    this.onFocus.emit($event);
  }
}
Example #27
Source File: component.ts    From alauda-ui with MIT License 4 votes vote down vote up
@Component({
  selector: 'aui-date-picker-panel',
  templateUrl: './template.html',
  styleUrls: ['./style.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DatePickerPanelComponent),
      multi: true,
    },
  ],
})
export class DatePickerPanelComponent extends CommonFormControl<Dayjs> {
  @Input()
  clearable = true;

  @Input()
  clearText: string;

  @Input()
  set type(type: DatePickerType) {
    this.navRange = getNavRangeByType(type);
    this._type = type;
  }

  get type() {
    return this._type;
  }

  private _type: DatePickerType;

  @Input()
  showTime = false;

  @Input()
  disabledDate: (date: Dayjs, type: DateNavRange) => boolean = () => false;

  @Input()
  disabledTime: DisabledTimeFn = () => null;

  @Input()
  weekStartDay = 0;

  @Input()
  showFooter = true;

  @Input()
  footerTemplate: TemplateRef<unknown>;

  @Input()
  extraFooter: TemplateRef<unknown>;

  @Input()
  minDate: Dayjs;

  @Input()
  maxDate: Dayjs;

  @Output()
  confirm = new EventEmitter<Dayjs>();

  @Output()
  clear = new EventEmitter<void>();

  private _cacheSelectedDate: Dayjs;
  private _cacheDisabledTimeFn: ReturnType<DisabledTimeFn>;

  getDisabledTimeFn(
    selectedDate: Dayjs,
    type: keyof ReturnType<DisabledTimeFn>,
  ) {
    if (selectedDate !== this._cacheSelectedDate) {
      this._cacheDisabledTimeFn = this.disabledTime(selectedDate);
      this._cacheSelectedDate = selectedDate;
    }
    return this._cacheDisabledTimeFn?.[type];
  }

  navRange: DateNavRange;

  get currentNavType() {
    return getTypeByNavRange(this.navRange);
  }

  anchor: Dayjs;

  selectedDate: Dayjs;

  selectedTime: TimePickerModel;

  DateNavRange = DateNavRange;
  DatePickerType = DatePickerType;

  override writeValue(obj: Dayjs) {
    super.writeValue(obj);
    this.selectedDate = obj;
    this.selectedTime = getTimePickerModel(obj);
    this.anchor = obj || dayjs();
    this.cdr.markForCheck();
  }

  panelValueChange(value: Dayjs) {
    this.selectedDate = updateDate(
      this.selectedDate || dayjs(),
      value,
      this.currentNavType,
    );
    this.anchor = this.selectedDate;
    this.selectedDate = updateDateByTimeModel(
      this.selectedDate,
      this.selectedTime,
    );
    if (this.type === this.currentNavType) {
      this.confirmValue(this.selectedDate, !this.showTime);
    }
    const nextNavRange = nextNavRangeType(
      this.navRange,
      getNavRangeByType(this.type),
    );
    if (this.navRange !== nextNavRange) {
      this.navRange = nextNavRange;
    }
  }

  confirmValue(value?: ConfigType, closeAfterConfirm = true) {
    this.emitValue(value ? dayjs(value) : this.selectedDate);
    closeAfterConfirm && this.confirm.next(null);
  }

  timeDateChange(time: TimePickerModel) {
    if (!this.selectedDate) {
      return;
    }
    this.selectedDate = updateDateByTimeModel(this.selectedDate, time);
    this.emitValue(this.selectedDate);
  }

  setToday() {
    this.confirmValue(dayjs(), true);
  }
}
Example #28
Source File: np-switch.component.ts    From np-ui-lib with MIT License 4 votes vote down vote up
@Component({
  selector: "np-switch",
  templateUrl: "./np-switch.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NpSwitchComponent),
      multi: true,
    },
  ],
})
export class NpSwitchComponent implements ControlValueAccessor {
  private static controlCount = 1;

  @Input() trueLabelText: string;
  @Input() falseLabelText: string;
  @Input() readOnly: boolean;
  @Input() autoFocus: boolean;
  @Input() tabIndex: number;
  @Input() styleClass: string;
  @Input() inputId: string = `np-switch_${NpSwitchComponent.controlCount++}`;

  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Output() onFocus: EventEmitter<any> = new EventEmitter();
  @Output() onBlur: EventEmitter<any> = new EventEmitter();

  @ViewChild("control") inputViewChild: ElementRef;

  innerValue: boolean;
  isDisabled: boolean = false;
  focused: boolean = false;
  private onChangeCallback: (_: any) => void = () => { };
  private onTouchedCallback: () => void = () => { };

  get value(): boolean {
    return this.innerValue;
  }

  set value(v: boolean) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
      this.onChange.emit(v);
    }
  }

  writeValue(v: boolean): void {
    if (v !== this.innerValue) {
      this.innerValue = v;
    }
  }

  registerOnChange(fn: any): void {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouchedCallback = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  focus(): void {
    this.inputViewChild.nativeElement.focus();
  }

  _onClickSwitch($event: any): void {
    if (this.isDisabled || this.readOnly) {
      return;
    }
    this.value = $event.target.checked;
  }

  _onBlur($event: any): void {
    this.focused = false;
    this.onTouchedCallback();
    this.onBlur.emit($event);
  }

  _onFocus($event: any): void {
    this.focused = true;
    this.onFocus.emit($event);
  }

  _getInputId(): string {
    return this.inputId + "_input";
  }
}
Example #29
Source File: component.ts    From alauda-ui with MIT License 4 votes vote down vote up
@Component({
  selector: 'aui-date-range-picker-panel',
  templateUrl: './template.html',
  styleUrls: ['./style.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DateRangePickerPanelComponent),
      multi: true,
    },
  ],
})
export class DateRangePickerPanelComponent extends CommonFormControl<Dayjs[]> {
  @Input()
  clearable = true;

  @Input()
  clearText: string;

  @Input()
  showTime = true;

  @Input()
  showFooter = true;

  @Input()
  disabledDate: (date: Dayjs, navRange: DateNavRange) => boolean = () => false;

  @Input()
  disabledTime: { left: DisabledTimeFn; right: DisabledTimeFn } = {
    left: () => null,
    right: () => null,
  };

  @Input()
  weekStartDay = 0;

  @Input()
  minDate: Dayjs;

  @Input()
  maxDate: Dayjs;

  @Output()
  clear = new EventEmitter<void>();

  @Output()
  confirm = new EventEmitter<void>();

  bem = bem;
  Side = Side;
  DatePickerType = DatePickerType;
  leftDateRange = DateNavRange.Month;
  rightDateRange = DateNavRange.Month;

  FOOTER_DATE_FORMAT = 'YYYY-MM-dd';

  leftAnchor = dayjs();
  rightAnchor = dayjs().add(1, MONTH);

  get maxHeaderAvail() {
    return minDate(this.rightAnchor, this.maxDate);
  }

  get minHeaderAvail() {
    return maxDate(this.minDate, this.leftAnchor);
  }

  // 用于存放 range data 序列,数量为 2 时清除
  rangeValue: Dayjs[] = [];

  // 用于组装匹配日期序列
  matchValues: Dayjs[];

  startTime: TimePickerModel;
  endTime: TimePickerModel;

  override writeValue(obj: Dayjs[]) {
    super.writeValue(obj);
    this.rangeValue = obj || [];
    this.matchValues = [...this.rangeValue];
    this.startTime = getTimePickerModel(obj?.[0]);
    this.endTime = getTimePickerModel(obj?.[1]);
    if (obj?.length === 2) {
      this.reorder(sortDates(obj));
    }
    this.cdr.markForCheck();
  }

  calendarRangeChange(type: DateNavRange, side: Side) {
    if (side === Side.Left) {
      this.leftDateRange = type;
    } else {
      this.rightDateRange = type;
    }
  }

  // range 组件范围受控
  getDateDisabledFn(side: Side, constrainValue: Dayjs) {
    return composeDisabledDateFn((date: Dayjs, navRange: DateNavRange) => {
      if (navRange === DateNavRange.Month) {
        return false;
      }
      if (navRange === DateNavRange.Decade) {
        return date[side === Side.Left ? 'isAfter' : 'isBefore'](
          constrainValue,
          YEAR,
        );
      }
      return !date[side === Side.Left ? 'isBefore' : 'isAfter'](
        constrainValue,
        MONTH,
      );
    }, this.disabledDate);
  }

  private getDisabledTimeCachedFn(side: Side) {
    let cacheSelectedDate: Dayjs;
    let cacheDisabledTimeFn: ReturnType<DisabledTimeFn>;
    return (value: Dayjs, key: keyof ReturnType<DisabledTimeFn>) => {
      if (value !== cacheSelectedDate) {
        cacheDisabledTimeFn = (this.disabledTime?.[side] || (() => null))(
          value,
        );
        cacheSelectedDate = value;
      }
      return cacheDisabledTimeFn?.[key];
    };
  }

  leftDisabledTimeFn = this.getDisabledTimeCachedFn(Side.Left);
  rightDisabledTimeFn = this.getDisabledTimeCachedFn(Side.Right);

  selectPickerPanel(value: Dayjs, side: Side) {
    const navRange =
      side === Side.Left ? this.leftDateRange : this.rightDateRange;
    const type = getTypeByNavRange(navRange);
    const dateValue = updateDate(
      side === Side.Left ? this.leftAnchor : this.rightAnchor,
      value,
      type,
    );
    const nextRange = nextNavRangeType(navRange, DateNavRange.Month);

    if (side === Side.Left) {
      this.leftAnchor = dateValue;
      this.leftDateRange = nextRange;
    } else {
      this.rightAnchor = dateValue;
      this.rightDateRange = nextRange;
    }

    if (navRange !== DateNavRange.Month) {
      return;
    }

    if (this.leftAnchor.isSame(this.rightAnchor, MONTH)) {
      this.rightAnchor = this.rightAnchor.add(1, MONTH);
    }

    this.rangeValue =
      this.rangeValue.length === 2
        ? [dateValue]
        : sortDates([...this.rangeValue, dateValue]);
    this.matchValues = [...this.rangeValue];
    this.syncTime();
    if (this.rangeValue.length === 2) {
      this.reorder(this.rangeValue);
      this.confirmValue(this.rangeValue, !this.showTime);
    }
  }

  reorder(sortedDate: Dayjs[]) {
    if (!sortedDate[0].isSame(sortedDate[1], MONTH)) {
      this.leftAnchor = updateDate(
        this.leftAnchor,
        sortedDate[0],
        DatePickerType.Day,
      );
      this.rightAnchor = updateDate(
        this.leftAnchor,
        sortedDate[1],
        DatePickerType.Day,
      );
    }
  }

  hoverItem(date: Dayjs) {
    if (this.rangeValue.length === 1) {
      this.matchValues[1] = date;
    }
  }

  confirmValue(value: Dayjs[], closeThen = true) {
    this.emitValue(value);
    closeThen && this.confirm.next();
  }

  timeChange(time: TimePickerModel) {
    if (!time) {
      return;
    }
    this.syncTime();
    if (!this.rangeValue?.length) {
      const date = updateDateByTimeModel(dayjs(), time);
      this.rangeValue = [date, date];
      this.matchValues = [...this.rangeValue];
      this.startTime = this.endTime = time;
    }
    this.emitValue(this.rangeValue);
  }

  syncTime() {
    if (this.showTime && !!this.rangeValue?.length) {
      const startDate = this.rangeValue[0]
        ? updateDateByTimeModel(this.rangeValue[0], this.startTime)
        : null;
      const endDate = this.rangeValue[1]
        ? updateDateByTimeModel(this.rangeValue[1], this.endTime)
        : null;
      if (!this.startTime && startDate) {
        this.startTime = getTimePickerModel(this.rangeValue[0]);
      }
      if (!this.endTime && endDate) {
        this.endTime = getTimePickerModel(this.rangeValue[1]);
      }
      this.rangeValue = [startDate, endDate].filter(i => !!i);
    }
  }
}