Флажок Angular ControlValueAccessor всегда true при использовании SVG - PullRequest
3 голосов
/ 24 апреля 2019

У меня есть собственный компонент-флажок, который в настоящее время использует некоторые значки значков материалов для отображения.Я использую компонент в нескольких местах, но у меня возникают проблемы с одним конкретным экземпляром.

У меня есть сетка с флажками вдоль левой стороны, чтобы выбрать несколько строк одновременно.Когда <i> используется для отображения значка из шрифта, эти флажки работают правильно.Когда я переключаюсь на использование SVG, эти флажки не работают.Я могу выбрать только один за раз, и я не могу отменить выбор одного, который выбран.Если я отмечу флажок, он проверяет, как вы ожидаете.Если я нажму на него снова, ничего не произойдет - флажок останется отмеченным.Если я установлю другой флажок, первый флажок не будет установлен.

Это оригинальный шаблон компонента флажка:

<label class="my-checkbox" [class.pointer]="!disabled">
  <input
      type="checkbox"
      class="form-control norowclick"
      [class.indeterminateinput]="indeterminate"
      [checked]="checked"
      [(ngModel)]="value"
      (blur)="onBlur()"
      [disabled]="disabled"
  >
  <i class="material-icons md-18 indeterminate norowclick">indeterminate_check_box</i>
  <i class="material-icons md-18 checked norowclick">check_box</i>
  <i class="material-icons md-18 unchecked norowclick">check_box_outline_blank</i>
</label>

Это новый шаблон с использованием angular-svg-icon.При рендеринге есть элемент <svg-icon> с элементом <svg> в качестве единственного дочернего элемента. Это единственное изменение, внесенное в любой код, именно это приводит к его поломке.

<label class="my-checkbox" [class.pointer]="!disabled">
  <input
      type="checkbox"
      class="form-control norowclick"
      [class.indeterminateinput]="indeterminate"
      [checked]="checked"
      [(ngModel)]="value"
      (blur)="onBlur()"
      [disabled]="disabled"
  >
  <svg-icon name="indeterminate_check_box" class="icon-18 indeterminate norowclick"></svg-icon>
  <svg-icon name="check_box" class="icon-18 checked norowclick"></svg-icon>
  <svg-icon name="check_box_outline_blank" class="icon-18 unchecked norowclick"></svg-icon>
</label>

Это код компонента флажка:

@Component({
  selector: 'my-checkbox',
  templateUrl: './my-checkbox.component.html',
  styleUrls: ['./my-checkbox.component.less'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => MyCheckboxComponent),
      multi: true
    }
  ]
})
export class MyCheckboxComponent implements OnInit, ControlValueAccessor {
  @Input() formControlName: string;
  @Input() checked: boolean = false;
  @Input() indeterminate: boolean = false;
  @Input() disabled: boolean = false;
  public control: AbstractControl;

  private innerValue: any = '';
  private controlContainer: ControlContainer;

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

  constructor(
    @Optional()
    @Host()
    @SkipSelf()
    private _controlContainer: ControlContainer
  ) {
    this.controlContainer = _controlContainer;
  }

  ngOnInit() {
    this._getFormControl();
  }

  private _getFormControl() {
    if (this.controlContainer) {
      if (this.formControlName) {
        this.control = this.controlContainer.control.get(this.formControlName);
      }
    }
  }

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

  set value(v: any) {
    if (v !== this.innerValue) {
      this.indeterminate = false;
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  onBlur() {
    this.onTouchedCallback();
  }

  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

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

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

Столбец сетки отображается как шаблон.При передаче функции checkSecret(), $event.target.checked всегда имеет значение true в новой версии.

<ng-template #checkBoxTemplate let-value="value" let-item="item">
  <div class="rowCheck">
    <span *ngIf="!item['isFolder']" [class.rowHoverInline]="!hasSelections()">
      <my-checkbox class="norowclick" [checked]="item['isSelected']" (change)="checkSecret(item, $event)"></my-checkbox>
    </span>
  </div>
</ng-template>

1 Ответ

2 голосов
/ 07 мая 2019

Я не уверен, но подозреваю что-то странное с состоянием ввода и управлением событиями.Вот попытка быстро исправить, что я абсолютно не проверял.

<label class="my-checkbox" [class.pointer]="!disabled">
  <input
      type="checkbox"
      class="form-control norowclick"
      [class.indeterminateinput]="indeterminate"
      [checked]="checked"
      (change)="onInputChange($event)" <-- CHANGE HERE ->
      (blur)="onBlur()"
      [disabled]="disabled"
  >
  <i class="material-icons md-18 indeterminate norowclick">indeterminate_check_box</i>
  <i class="material-icons md-18 checked norowclick">check_box</i>
  <i class="material-icons md-18 unchecked norowclick">check_box_outline_blank</i>
</label>

@Component({
  selector: 'my-checkbox',
  templateUrl: './my-checkbox.component.html',
  styleUrls: ['./my-checkbox.component.less'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => MyCheckboxComponent),
      multi: true
    }
  ]
})
export class MyCheckboxComponent implements OnInit, ControlValueAccessor {
  @Input() formControlName: string;
  @Input() checked: boolean = false;
  @Input() indeterminate: boolean = false;
  @Input() disabled: boolean = false;
  public control: AbstractControl;

  private innerValue: any = '';
  private controlContainer: ControlContainer;

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

  constructor(
    @Optional()
    @Host()
    @SkipSelf()
    private _controlContainer: ControlContainer
  ) {
    this.controlContainer = _controlContainer;
  }

  ngOnInit() {
    this._getFormControl();
  }

  private _getFormControl() {
    if (this.controlContainer) {
      if (this.formControlName) {
        this.control = this.controlContainer.control.get(this.formControlName);
      }
    }
  }

 // CHANGE HERE 
 onInputChange(event) {
    const newValue: boolean = event.target.checked;
    if (newValue !== this.innerValue) {
      this.indeterminate = false;
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  onBlur() {
    this.onTouchedCallback();
  }

  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

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

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

Кроме того, я хочу сказать вам, что (из моего понимания) вы должны использовать [(ngModel)] при вызове вашего метода доступа к пользовательскому значению элемента управления.например:

<my-checkbox class="norowclick" [(ngModel)]="item['isSelected']"></my-checkbox>

или, если вы действительно хотите поймать изменение ngModel, чтобы сделать что-то еще, кроме обновления значения:

<my-checkbox class="norowclick" [ngModel]="item['isSelected']" (ngModelChange)="checkSecret(item, $event)"></my-checkbox>

Тогда переопределенное writeValue можно использовать для обновления вашеговнутренняя модель (innerValue), перехватывающая события изменения из родительского компонента.

Чтобы отправить изменения модели из вашего ControlValueAccessor обратно в ваш родительский компонент, вы используете обратный вызов, зарегистрированный в registerOnChange, я думаю, что вы все сделали правильно.

Также у меня есть ощущение, что переменные checked и innerValue могут быть отчасти избыточными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...