فاتحة
دعنا نتحدث عن الأشكال التفاعلية في الزاوي ، وتعلم لعناصر التحكم المخصصة كيفية إنشاء واستخدام والتحقق من صحتها. تفترض المقالة أنك معتاد بالفعل على الإطار الزاوي ، لكنك ترغب في الغوص أكثر في تفاصيله. أتمنى الخير ، دعنا نبدأ.
رد الفعل وأشكال قوالب يحركها
بضع كلمات للتأكد من أننا على نفس الصفحة. تحتوي الزاويّة على نوعين من الأشكال:
النماذج المدفوعة بالقالب والنماذج التفاعلية .
النماذج التي تحركها القوالب هي
نماذج تستند إلى
الربط ثنائي الاتجاه (مرحبًا ، angularjs). نحدد الحقل في الفصل (على سبيل المثال ، اسم المستخدم) ، في html في علامة الإدخال التي نربطها [(value)] = "اسم المستخدم" ، وعندما تتغير قيمة الإدخال ، تتغير قيمة اسم المستخدم. في عام 2011 ، كان هذا السحر لعنة! حسنا ، ولكن هناك فارق بسيط. بهذه الطريقة ، سيكون بناء الأشكال المعقدة أمرًا صعبًا.
أشكال التفاعل هي أداة مناسبة لبناء أشكال بسيطة ومعقدة. يخبروننا "إنشاء مثيل لفئة النموذج (
FormGroup ) ، وتمرير مثيلات عناصر التحكم (
FormControl ) إليها واعطائها فقط إلى html ، وسأقوم بالباقي." حسنًا ، دعنا نجرب:
class UsefullComponent { public control = new FormControl(''); public formG = new FormGroup({username: control}); }
<form [formGroup]="formG"> <input type="text" formControlName="username"> </form>
نتيجة لذلك ، حصلنا على شكل رد فعل ، مع لعبة ورق و ... حسنا ، أنت تفهم ، مع كل وسائل الراحة. على سبيل المثال ،
سيمنح formG.valueChanges لنا
ملاحظة (دفق) من تغييرات النموذج. يمكنك أيضًا إضافة عناصر تحكم جديدة وحذف عناصر التحكم الموجودة وتغيير قواعد التحقق من الصحة والحصول على قيمة النموذج (
formG.value ) وغير ذلك الكثير. وكل هذا يعمل مع مثيل واحد من
formG .
ومن أجل عدم إنشاء مثيلات للفئات المذكورة أعلاه يدويًا في كل مرة ، قدم لنا
المطورون الزاويون FormBuilder مناسبًا بمساعدة إعادة كتابة المثال أعلاه على أنه
class UsefullComponent { public formG: FormGroup; constructor(private fb: FormBuilder) { this.formG = fb.group({ name: ''
ضوابط مخصصة
يخبرنا Angular "بإخوانه ، إذا لم يكن لديك ما يكفي من عناصر التحكم الأصلية (الإدخال والتاريخ والرقم وغيرها) ، أو إذا كنت لا تحبهم على الإطلاق لشيء ما ، فإليك أداة بسيطة ولكنها قوية للغاية لإنشاء أدوات التحكم الخاصة بك." يمكن استخدام عناصر التحكم المخصصة حتى مع رد الفعل ، حتى مع النماذج التي تعتمد على القالب ، ويتم تنفيذها باستخدام توجيهات (يفاجأ!). مع العلم أن المكونات هي توجيهات مع قالب (ما تحتاجه) ، نكتب:
import { Component, Input } from '@angular/core'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent { @Input() value = 0; up() { this.value++; } down() { this.value - ; } }
مثل أي توجيه ، يجب الإعلان عن ذلك في الوحدة النمطية (بحيث لا تنمو المقالة ، سنحذف هذه الفروق الدقيقة).
الآن يمكننا استخدام المكون الذي تم إنشاؤه:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control></counter-control> ` }) class ParentComponent {}
كل شيء يعمل بالطبع ، ولكن أين هي الأشكال؟! على الأقل يحركها القالب ....
لا داعي للذعر ، كل شيء سيكون بعد الاجتماع
ControlValueAccessor .
التحكم في قيمة الموصل
لكي تتفاعل الآليات الزاوية مع عنصر التحكم المخصص الخاص بك ، فإنك تحتاج إلى عنصر التحكم هذا لتطبيق واجهة معينة. تسمى هذه الواجهة
ControlValueAccessor . هذا مثال جيد إلى حد ما على تعدد الأشكال من OOP ، عندما يقوم كائننا (في هذه الحالة ، التحكم) بتنفيذ واجهة ، وتتفاعل الكائنات الأخرى (الأشكال الزاوية) مع كائننا من خلال هذه الواجهة.
بفضل
ControlValueAccessor ، لدينا طريقة واحدة للعمل مع عناصر التحكم ، والتي ، بالمناسبة ، لا تستخدم فقط لإنشاء عناصر تحكم مخصصة. الزاوي تحت غطاء محرك السيارة يستخدم أيضا هذه الواجهة. من اجل ماذا؟ وفقط لإلقاء نظرة موحدة سلوك الضوابط الأصلية. على سبيل المثال ، في الإدخال ، يتم احتواء القيمة في سمة القيمة ، في خانة الاختيار ، يتم تحديد القيمة من خلال السمة المحددة. وبالتالي ، يكون لكل نوع من عناصر التحكم ControlValueAccessor الخاص به: DefaultValueAccessor للمدخلات و textarea و CheckboxControlValueAccessor لخانات الاختيار و
RadioControlValueAccessor لأزرار الراديو ، وهكذا.
ولكن ماذا تفعل الزاوي باستخدام
ControlValueAccessor ؟ كل شيء بسيط للغاية ، فهو يكتب القيمة من النموذج إلى DOM (العرض) ، كما يرفع حدث التحكم في التغيير إلى
FormGroup والتوجيهات الأخرى.
الآن وقد علمنا حول
ControlValueAccessor ، يمكننا تطبيقه على سيطرتنا.
لنلقِ نظرة على واجهته:
interface ControlValueAccessor { writeValue(value: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void }
WriteValue (القيمة: أي) - يتم استدعاؤها عند تعيين المصدر (
FormControl الجديد ('I am default value') ) أو قيمة جديدة أعلى
control.setValue ('' I am setted value ') .
registerOnChange (fn: any) - هي الطريقة التي تحدد المعالج الذي يجب استدعاؤه عندما تتغير القيمة (fn عبارة عن رد اتصال يخطر النموذج الذي تغيرت القيمة في عنصر التحكم هذا).
registerOnTouched (fn: any) - يحدد رد الاتصال الذي يتم استدعاؤه في حدث
الضبابية (يتم وضع علامة على التحكم
باللمس )
import { Component, Input } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent implements ControlValueAccessor { @Input() value = 0; onChange(_: any) {} up() { this.value++; } down() { this.value - ; } writeValue(value: any) { this.value = value; } registerOnChange(fn) { this.onChange = fn; } registerOnTouched() {} }
لكي يكون النموذج الأصلي على دراية بالتغييرات في عنصر التحكم ، نحتاج إلى استدعاء أسلوب
onChange لكل تغيير في قيمة القيمة. من أجل عدم كتابة مكالمة
onChange في كل طريقة (صعودا وهبوطا) ، فإننا ننفذ حقل القيمة من خلال getters و setters:
في الوقت الحالي ، لا تعرف الزاوي أن المكون الذي يقوم بتطبيق
ControlValueAccessor يجب اعتباره عنصر تحكم مخصص ، دعنا
نخبره عن ذلك:
import { Component, Input, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ … providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterControlComponent), multi: true }] }) class CounterControlComponent implements ControlValueAccessor { … }
في هذا القسم من التعليمة البرمجية ، نقول
للزاوية "أرغب في تسجيل عنصر تحكم مخصص جديد (
توفر: NG_VALUE_ACCESSOR ) ، استخدم مثيل مثيل المكون (في هذه اللحظة الذي سيتم فيه تهيئة
مكوننا ) للمكون (
useExisting: forwardRef (() => CounterControlComponent )"
)) .
multi: true يشير إلى أنه يمكن أن يكون هناك العديد من التبعيات بمثل هذا الرمز المميز (
NG_VALUE_ACCESSOR ).
ليس فقط خلق
حان الوقت ل zayuzat سيطرتنا المخصصة. لا تنس أن تضيف
FormsModule / ReactiveFormsModule إلى واردات الوحدة النمطية حيث يتم استخدام عنصر التحكم هذا.
استخدم في النماذج التي تحركها القوالب
كل شيء بسيط هنا ، باستخدام الربط ثنائي الاتجاه عبر
ngModel ، نحصل على تغيير في العرض عندما يتغير النموذج والعكس:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control [(ngModel)]="controlValue"></counter-control> ` }) class ParentComponent { controlValue = 10; }
استخدام في أشكال رد الفعل
كما هو مذكور في بداية المقالة ، قم بإنشاء مثيل للنموذج التفاعلي عبر
FormBuilder ،
وقم بتقديم html واستمتع:
import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; @Component({ selector: 'parent-component', template: ` <form [formGroup]="form"> <counter-control formControlName="counter"></counter-control> </form> ` }) class ParentComponent implements OnInit { form: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.form = this.fb.group({ counter: 5 }); } }
الآن هذا هو شكل رد الفعل الكامل مع عناصر التحكم المخصصة لدينا ، في حين أن جميع الآليات تعمل بنفس الطريقة مع الضوابط الأصلية (هل ذكرت تعدد الأشكال؟). حتى لا يتم تحميل المقالة الحالية ، سنتحدث عن التحقق من أخطاء التحكم المخصصة وعرضها في المقالة التالية.
المواد:
مقال عن الضوابط المخصصة من Pascal Precht.
من الاحواض الاشكال في الزاوي.
سلسلة من المقالات حول rxjs.