Как получить имя поля ввода из объекта Angular2 FormControl?

У меня есть приложение Angular 2, которое использует модуль ReactiveForms для управления формой, использующей настраиваемый валидатор. Валидатор получает объект FormControl. У меня есть несколько полей ввода, которые могли бы использовать один и тот же пользовательский валидатор, если бы я только знал имя поля, когда FormControl было передано валидатору.

Я не могу найти какой-либо метод или общедоступное свойство в FormControl, которое раскрывает имя поля ввода. Это достаточно просто, чтобы увидеть его ценность, конечно. Ниже показано, как я хотел бы его использовать:

public asyncValidator(control: FormControl): {[key: string]: any} {
  var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece

  return new Promise(resolve => {
      this.myService.getValidation(theFieldName, control.value)
        .subscribe(
          data => {
            console.log('Validation success:', data);
            resolve(null);
          },
          err => {
            console.log('Validation failure:', err);
            resolve(err._body);
          });
    });
  }

person Michael Oryl    schedule 01.11.2016    source источник


Ответы (6)


Чтобы расширить ответ Радима Кёлера. вот более короткий способ написания этой функции.

getControlName(c: AbstractControl): string | null {
    const formGroup = c.parent.controls;
    return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
}
person Chris    schedule 17.09.2017
comment
самый краткий ответ, но все же понятно, что происходит. у меня работает в Angular 4. спасибо - person FireDragon; 23.09.2017
comment
Не лучше ли построить карту AbstractControls для имен и искать в ней, чем генерировать список ключей, а затем искать список ключей для каждого поиска? - person ShadSterling; 25.06.2018
comment
Не работает: "Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ [key: string]: AbstractControl; } | AbstractControl[]'." - person lonix; 11.06.2019
comment
Это очень ресурсоемкая операция. Представьте, что каждый элемент управления в форме перебирает все компоненты в форме. Сложность формы рендеринга O(n²). Но делать это только изредка на одном поле — нормально. - person kvetis; 26.06.2020
comment
@Ionix: Используйте это: Object.keys(c.parent.controls).find(name => c === c.parent.get(name)) - person 321X; 07.08.2020
comment
const formGroup = c["_parent"].controls; return Object.keys(formGroup).find(name => c === formGroup[name]) || null; этот код работал у меня @4.1.5 версия машинописного текста. Работает для всех версий может быть..Но я не уверен - person Shailesh Bhat; 28.04.2021

Мы можем использовать свойство .parent, сегодня ["_parent"] (подробнее см. ниже):

export const getControlName = (control: ng.forms.AbstractControl) =>
{
    var controlName = null;
    var parent = control["_parent"];

    // only such parent, which is FormGroup, has a dictionary 
    // with control-names as a key and a form-control as a value
    if (parent instanceof ng.forms.FormGroup)
    {
        // now we will iterate those keys (i.e. names of controls)
        Object.keys(parent.controls).forEach((name) =>
        {
            // and compare the passed control and 
            // a child control of a parent - with provided name (we iterate them all)
            if (control === parent.controls[name])
            {
                // both are same: control passed to Validator
                //  and this child - are the same references
                controlName = name;
            }
        });
    }
    // we either found a name or simply return null
    return controlName;
}

и теперь мы готовы настроить определение нашего валидатора

public asyncValidator(control: FormControl): {[key: string]: any} {
  //var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece
  var theFieldName = getControlName(control); 
  ...

.parent позже, ["_parent"] сейчас

На данный момент (сегодня, сейчас) текущая версия:

2.1.2 (27 октября 2016 г.)< /а>

Но после этой проблемы: feat(forms): сделать "родительский" общедоступным свойством "AbstractControl"

И как уже было сказано здесь

2.2.0-beta.0 (2016 г. -10-20)

Функции

  • формы: сделать «родительский» общедоступным свойством «AbstractControl» (# 11855) (445e592)
  • ...

мы могли бы позже изменить ["_parent"] на .parent

person Radim Köhler    schedule 02.11.2016
comment
Это, безусловно, позволяет получить доступ к форме, но как тогда определить, какое из (многих) полей в форме представлено вашим исходным FormControl, которое было передано в валидатор? - person Michael Oryl; 02.11.2016
comment
Я не совсем уверен в вашем беспокойстве. Дело в том, что name элемента FormControl задается только его родителем FormGroup. Поскольку только такой родитель имеет MAP controls, а ключи представляют имя. И поскольку у каждого элемента управления есть информация о том, кто его .parent, мы можем получить эту информацию где угодно... даже внутри валидатора... просто вызвав приведенный выше (или скорректированный/оптимизированный) функция getControlName(controlPassedToValidtor). Другими словами, даже одного элемента управления, переданного в функцию валидатора, достаточно, чтобы получить его имя. Единственное условие: он должен быть потомком FormGroup - person Radim Köhler; 03.11.2016
comment
Проблема в том, что доступ к FormGroup дает мне доступ ко всем FormControl, которые он содержит, а также к имени каждого поля, которое они представляют. Но из этого списка FormControls, к которым у меня теперь есть доступ, как мне узнать, какой из них является FormControl, который был передан в валидатор. Мне нужно имя определенного поля. Если я что-то упустил, вы предоставляете список всех полей.... - person Michael Oryl; 03.11.2016
comment
Хм, как бы это объяснить. У нас есть элемент управления внутри валидатора. У нас есть отсылка к нему. У нас также есть словарь набор { Name1: control1, Name2: control2, ...}. Каждый элемент управления (1, 2, ...) является экземпляром некоторого FormControl (или другого FormGroup или FormArray). Итак, мы можем сравнить элемент управления, переданный валидатору, с каждым из них. Если ссылка та же control === parent.controls[name], мы получили совпадение. Такой родитель дочерний элемент совпадает с элементом управления, переданным валидатору. Помогать? или же ... ? Я имею в виду, у нас должен быть контроль, что мы и делаем... он был пройден... - person Radim Köhler; 03.11.2016
comment
Я расширил свой ответ, более подробно в комментариях, и в основном - показал, как внутри валидатора мы можем вызвать эту функцию и получить имя этого элемента управления... надеюсь, теперь понятно - person Radim Köhler; 03.11.2016
comment
Хорошо, я попробую. Спасибо. - person Michael Oryl; 03.11.2016
comment
Тем не менее (по состоянию на август 2017 года) нет лучшего способа, чем перебирать все элементы управления? - person gerleim; 30.08.2017

Начиная с Angular 4.2.x, вы можете получить доступ к родительскому элементу FormControl FormGroup (и его элементам управления), используя общедоступное свойство parent:

private formControl: FormControl;

//...

Object.keys(this.formControl.parent.controls).forEach((key: string) => {
  // ...
});
person Steve Brush    schedule 15.07.2017
comment
Здесь, в мае 2020 года, мне пришлось изменить this.formControl.parent.controls на this.formControl.value в предложенном выше коде. - person Nowdeen; 19.05.2020

У вас есть два варианта:

С помощью декоратора Attribute:

constructor(@Attribute('formControlName') public formControlName) {}

С помощью декоратора Input:

@Input() formControlName;

Конечно, чтобы использовать это, ваша проверка должна быть директивой.

person Bazinga    schedule 01.11.2016

Вы можете установить имя элемента управления в валидаторах:

this.form = this.fb.group({
     controlName: ['', 
         [
            Validators.required, 
            (c) => this.validate(c, 'controlName')
         ]
      ]
});

А потом:

validate(c: FormControl, name) {
    return name === 'controlName' ? {invalid: true} : null;
}
person Тарас Соколовский    schedule 19.11.2018
comment
вы только что спасли меня от большой боли... У меня был асинхронный валидатор в реактивных формах, который использовал имя элемента управления, чтобы перейти к службе и получить некоторую информацию для проверки. но изначально он всегда вызывался для нового бита FormControl, поэтому родитель был неопределенным, и если вы вернете из (null), чтобы обойти неопределенного родителя, он всегда будет устанавливать элемент управления в VAILD, даже если при следующем вызове есть ошибки ! - person Mr AH; 15.01.2019

Не совсем то, что вы хотите, но вы можете динамически создать валидатор, как в некоторых примерах.

как

typeBasedValidator(controlName: string): ValidatorFn {
  return(control: AbstractControl): {[key: string]: any} => {
     // Your code using controlName to validate
     if(controlName == "something") { 
       doSomething(); 
     } else { 
       doSomethingElse(); 
     }
  }
}

Затем используйте валидатор при создании формы, передав имя элемента управления, например

person dabicho    schedule 01.03.2017