Angular'da ngDefaultControl nedir?


117

Hayır, bu yinelenen bir soru değil. Gördüğünüz gibi, SO ve Github'da bu yönergeyi [(ngModel)]yönergesi olan ve bir formda bulunmayan bir etikete eklememi öngören tonlarca soru ve sorun var . Eklemezsem bir hata alıyorum:

ERROR Error: No value accessor for form control with unspecified name attribute

Tamam, bu özelliği oraya koyarsam hata ortadan kalkar. Fakat bekle! Kimse ne yaptığını bilmiyor! Ve Angular'ın belgesi bundan hiç bahsetmiyor. İhtiyacım olmadığını bildiğim halde neden bir değer erişimcisine ihtiyacım var? Bu öznitelik değer erişimcilerine nasıl bağlanır? Bu direktif ne işe yarar? Değer erişimci nedir ve onu nasıl kullanırım?

Ve neden herkes hiç anlamadığı şeyleri yapmaya devam ediyor? Sadece bu kod satırını ekleyin ve işe yarıyor, teşekkürler, iyi programlar yazmanın yolu bu değil.

Ve sonra. Bir tane değil, okumak iki açısal içinde biçimleri hakkında büyük kılavuzlar ve yaklaşık bir bölüm ngModel:

Ve ne var biliyor musun? Değer erişimcilerinden veya ngDefaultControl. Nerede?


1
> "Ve neden herkes hiç anlamadığı şeyleri yapmaya devam ediyor?" - Evet! kesinlikle! biraz daha ünlem işareti kullanabilirdi ;-)
Guss

Yanıtlar:


200

[ngDefaultControl]

Üçüncü şahıs kontrolleri ControlValueAccessor, açısal formlarla işlev görmesini gerektirir . Polimer gibi birçoğu yerel öğe <paper-input>gibi davranır <input>ve bu nedenle DefaultValueAccessor. Bir ngDefaultControlöznitelik eklemek, bu yönergeyi kullanmalarına izin verecektir.

<paper-input ngDefaultControl [(ngModel)]="value>

veya

<paper-input ngDefaultControl formControlName="name">

Dolayısıyla, bu zarafetin tanıtılmasının ana nedeni budur.

Angular2'nin alfa sürümlerindeng-default-control öznitelik olarak adlandırıldı .

Yani ngDefaultControliçin seçicileri biridir DefaultValueAccessor yönergesi:

@Directive({
  selector:
      'input:not([type=checkbox])[formControlName],
       textarea[formControlName],
       input:not([type=checkbox])[formControl],
       textarea[formControl],
       input:not([type=checkbox])[ngModel],
       textarea[ngModel],
       [ngDefaultControl]', <------------------------------- this selector
  ...
})
export class DefaultValueAccessor implements ControlValueAccessor {

Bunun anlamı ne?

Bu, kendi değer erişimcisine sahip olmayan öğeye (polimer bileşeni gibi) bu niteliği uygulayabileceğimiz anlamına gelir. Yani bu öğe davranışını alacak DefaultValueAccessorve bu öğeyi açısal formlarla kullanabiliriz.

Aksi takdirde, kendi uygulamanızı sağlamanız gerekir. ControlValueAccessor

ControlValueAccessor

Açısal belge durumları

Bir ControlValueAccessor, Angular form API'si ile DOM'daki yerel bir öğe arasında bir köprü görevi görür.

Aşağıdaki şablonu basit angular2 uygulamasında yazalım:

<input type="text" [(ngModel)]="userName">

inputYukarıdakilerin nasıl davranacağını anlamak için bu öğeye hangi direktiflerin uygulandığını bilmemiz gerekir. Burada açısal, hatayla ilgili bazı ipuçları verir:

İşlenmemiş Promise reddi: Şablon ayrıştırma hataları: Bilinen bir 'input' özelliği olmadığı için 'ngModel'e bağlanamaz.

Tamam, bu kadar açık ve cevap alabilirsiniz: ithalat FormsModuleadresinden Müşteri @NgModule:

@NgModule({
  imports: [
    ...,
    FormsModule
  ]
})
export AppModule {}

Biz onu ve tüm işleri amaçlandığı gibi ithal ettik. Ama kaputun altında neler oluyor?

FormsModule , bizim için aşağıdaki yönergeleri dışa aktarır:

@NgModule({
 ...
  exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}

görüntü açıklamasını buraya girin

Biraz araştırdıktan sonra, üç direktifin bizim için uygulanacağını keşfedebiliriz. input

1) NgControlStatus

@Directive({
  selector: '[formControlName],[ngModel],[formControl]',
  ...
})
export class NgControlStatus extends AbstractControlStatus {
  ...
}

2) NgModel

@Directive({
  selector: '[ngModel]:not([formControlName]):not([formControl])',
  providers: [formControlBinding],
  exportAs: 'ngModel'
})
export class NgModel extends NgControl implements OnChanges, 

3) DEFAULT_VALUE_ACCESSOR

@Directive({
  selector:
      `input:not([type=checkbox])[formControlName],
       textarea[formControlName],
       input:not([type=checkbox])formControl],
       textarea[formControl],
       input:not([type=checkbox])[ngModel],
       textarea[ngModel],[ngDefaultControl]',
  ,,,
})
export class DefaultValueAccessor implements ControlValueAccessor {

NgControlStatusyönerge sadece zaman değiştirdiği sınıfları gibi ng-valid, ng-touched, ng-dirtyve biz burada atlayabilirsiniz.


DefaultValueAccesstorNG_VALUE_ACCESSORsağlayıcılar dizisinde belirteç sağlar :

export const DEFAULT_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DefaultValueAccessor),
  multi: true
};
...
@Directive({
  ...
  providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {

NgModelyönerge NG_VALUE_ACCESSOR, aynı konak öğesinde bildirilen yapıcı belirtecine enjekte eder .

export NgModel extends NgControl implements OnChanges, OnDestroy {
 constructor(...
  @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {

Bizim durumumuzda NgModelenjekte edecek DefaultValueAccessor. Ve şimdi NgModel yönergesi paylaşılan setUpControlişlevi çağırıyor :

export function setUpControl(control: FormControl, dir: NgControl): void {
  if (!control) _throwError(dir, 'Cannot find control with');
  if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');

  control.validator = Validators.compose([control.validator !, dir.validator]);
  control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]);
  dir.valueAccessor !.writeValue(control.value);

  setUpViewChangePipeline(control, dir);
  setUpModelChangePipeline(control, dir);

  ...
}

function setUpViewChangePipeline(control: FormControl, dir: NgControl): void 
{
  dir.valueAccessor !.registerOnChange((newValue: any) => {
    control._pendingValue = newValue;
    control._pendingDirty = true;

    if (control.updateOn === 'change') updateControl(control, dir);
  });
}

function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
  control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor !.writeValue(newValue);

    // control -> ngModel
    if (emitModelEvent) dir.viewToModelUpdate(newValue);
  });
}

Ve işte köprü iş başında:

görüntü açıklamasını buraya girin

NgModelkontrolü (1) kurar ve dir.valueAccessor !.registerOnChangeyöntemi çağırır . ControlValueAccessorgeri aramayı onChange(2) özelliğinde depolar ve inputolay gerçekleştiğinde bu geri aramayı başlatır (3) . Ve son olarak updateControlişlev, geri arama içinde çağrılır (4)

function updateControl(control: FormControl, dir: NgControl): void {
  dir.viewToModelUpdate(control._pendingValue);
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, {emitModelToViewChange: false});
}

açısal çağrıların API'yi oluşturduğu yer control.setValue.

Bu, nasıl çalıştığının kısa bir versiyonu.


Ben sadece çift ​​yönlü ciltleme yaptım @Input() ngModelve @Output() ngModelChangebunun yeterli olduğunu düşündüm. Bu, aynı şeyi tamamen farklı bir şekilde yapmaya benziyor. Belki de alanıma isim vermemeliyim ngModel?
Gherman

3
Bu bileşeni açısal formlarla kullanmazsanız, kendi iki yönlü bağlamanızı oluşturabilir @Input() value; @Output() valueChange: EventEmitter<any> = new EventEmitter();ve sonra kullanabilirsiniz[(value)]="someProp"
yurzui

2
Ben de aynen öyle yapıyordum. Ama "değerimi" olarak adlandırdım ngModelve Angular bana bir hata göndermeye ve ControlValueAccessor ile sormaya başladı.
Gherman

Vue ve React'te ngDefaultControl'e eşdeğer olan var mı? Demek istediğim, kontrol değeri erişimcisini kullanarak açısal olarak özel bir girdi bileşeni yaptım ve bunu Açısal elemanlarda bir ağ bileşeni olarak sardım. Aynı projede, bunun açısal formlarla çalışmasını sağlamak için ngDefaultControl kullanmak zorunda kaldım. Ama bunların Vue ve React'te çalışmasını sağlamak için ne yapmalıyım? Ayrıca yerel JS'de mi?
Kavinda Jayakody

Özel bileşenimde ngDefaultControl kullanıyorum ancak bir sorunla uğraşıyorum. FormBuilder görünümümün (özel giriş bileşeni) içinde formControl için varsayılan değeri ayarladığımda güncellenmiyor, yalnızca model. Neyi yanlış yapıyorum?
Igor Janković
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.