Angular: لا يتم كتابة النماذج التفاعلية بقوة

تم إنشاؤها على ٣٠ ديسمبر ٢٠١٦  ·  90تعليقات  ·  مصدر: angular/angular

[x] feature request
  • الإصدار الزاوي: 2

من المفترض أن يتم استخدام النماذج التفاعلية في النماذج المعقدة ، لكن عنصر التحكم valueChanges هو Observable<any> ، والتي تتعارض تمامًا مع الممارسات الجيدة للتعليمات البرمجية المعقدة.

يجب أن تكون هناك طريقة لإنشاء عناصر تحكم نموذج مكتوبة بقوة.

forms feature high

التعليق الأكثر فائدة

مرحبًا ، أردت مشاركة تحديث من فريق Angular: نسمعك أن هذه نقطة ألم كبيرة. سنبدأ العمل على نماذج مكتوبة بقوة أكبر قريبًا ، والتي ستتضمن النظر في العلاقات العامة الحالية ومراجعة جميع تعليقاتك مرة أخرى. شكرًا لكم جميعًا على الوقت الذي قضيته في ترك أفكاركم!

ال 90 كومينتر

ذات الصلة # 11279

لا علاقة لهذا بـ # 11279.

من فضلك اشرح كيف أنها غير مرتبطة؟
ما تريده هو أن يكون التحكم المجرد عامًا بشكل صحيح؟ هذه هي الطريقة الوحيدة التي يمكن أن تحتوي بها valueChanges على نوع ليس Observable<any> ولن تكون هناك طريقة أخرى لاستنتاج النوع.
وهو بالضبط ما يطلبه # 5404 ، مما يعني أن هذا مرتبط بـ # 11279
إذا كانت هناك طريقة أخرى يمكن تنفيذها دون جعل AbstractControl عامًا ، فيرجى توضيح ذلك.

استخدام get<Type> كما في # 11279 هو حل خاطئ بالتأكيد. إذا كان TypeScript يحتوي على شيء مثل Java Unbounded Wildcard ، فسيستخدمه get وليس any . ربما يمكن عمل شيء بنفس الطريقة بواجهة فارغة؟

هناك أيضًا https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html keyof . قد تكون ميزات TypeScript 2.1 مثيرة جدًا للدراسة لتنفيذ عناصر تحكم النموذج المكتوبة بشدة.

الطريقة الحالية ، لسوء الحظ ، لا يمكنني استخدامها في التطبيقات الكبيرة وأحتاج إلى تصميم شيء فوقها.

لقد لاحظت للتو أنه في TS 2.2 (https://github.com/Microsoft/TypeScript/wiki/Roadmap#22-f February-2017) خططوا لأنواع عامة افتراضية (https://github.com/Microsoft/TypeScript/ القضايا / 2175) بمجرد حصولنا على هذا ، أعتقد أنه قد يكون من الجيد إعادة النظر في هذه المشكلة حيث يمكننا جعل AbstractControl عام مثل AbstractControl<T = any> حيث T هو نوع القيمة التي تم إرجاعها بواسطة valueChanges والتي ستكون Observable<T> . لن يكون من الجيد القيام بذلك حاليًا لأنه سيكون تغييرًا هائلاً ولكن مع الأدوية الجنيسة الافتراضية ، ما لم أسيء فهمها ، فلن يكون تغييرًا جذريًا.

تحديث صغير على هذا ، يبدو أنه تم نقل Generics الافتراضية إلى TS2.3 . لذلك ، بدعم TS 2.1 من Angular مع الإصدار 4.0 ، لن يمر وقت طويل بعد ذلك قبل أن يتمكنوا من دعم TS 2.3 وهو الآن الوقت الذي يجب أن ننتظر فيه مرة أخرى.

TypeScript 2.3 مع الأنواع العامة الافتراضية موجود هنا بالفعل ، هل لدينا أي خطط عندما يكون دعم TS 2.3 في angular جاهزًا؟

desfero في انتظار # 16707 لترقية البنية إلى TS2.3

سيود +1 رؤية هذه الميزة. أي شخص يعمل على ذلك؟

16828

محظور حاليًا على https://github.com/Microsoft/TypeScript/issues/16229

تحديث صغير على هذا:
حسب تعليقي هنا: https://github.com/angular/angular/pull/16828#issuecomment -337034655
لا يمكن تطبيق الأدوية الجنيسة في Forms API الحالية دون تغيير التغييرات.
لذلك هناك حاجة إلى أي من التغييرات الفاصلة
أو إعادة كتابة أشكال كاملة

لذلك تبين أن تعليقي السابق كان غير صحيح.
لقد تمكنت من تنفيذ ذلك على Forms API الحالية كما ترون هنا # 20040

Toxicable الذي لا يزال rpbeukes ، لديه طريقة

تضمين التغريدة

get ("شخص") على سبيل المثال ، لا يستخدم الرمز بحد ذاته

ليس لدي فكرة عما تقصده بهذا ، ما هو الرمز الذي تشير إليه هنا؟
في تطبيقي يمكنك أن تفعل شيئًا مثل

let g = new FormGroup({
  'name': new FormControl('Toxicable'),
  'age': new FormControl(22),
})

g.get('name') //AbstractControl<string>
g.get('age') //AbstractControl<number>

get (obj.person) بدون استخدام السلسلة

هذا يفتقر إلى القدرة على اجتياز FormGroups متعددة.
في حين أن طريقي غير قادر على استنتاج الأنواع في هذا السيناريو ، فإن فكرة العلاقات العامة الخاصة بي هي إضافة أنواع عامة دون تغيير التغييرات أو تقديم أي واجهة برمجة تطبيقات جديدة (بصرف النظر عن الأدوية العامة)

Toxicable أفهم أن التغيير يعني عدم تحطيم الأشياء ، وليس محاولة انتقاد الحل الذي

class PersonDetails {
  name: string;
  age: number;
}
let g = new FormGroup<PersonDetails>({
  name: new FormControl('Toxicable'),
  age: new FormControl(22),
})

g.get(name) //AbstractControl<string>
g.get(age) //AbstractControl<number>

سيكونون جميعا مراجع ضيقة. يقوم حل التعديل التحديثي بذلك بطريقة مبتذلة قليلاً ولكنه يحل هذه المشكلة أيضًا.

Toxicable thanx للعلاقات العامة. نتطلع إلى استخدامه :)

أنا أتفق مع howiempt ، إذا تمكنا من الحصول على شيء كهذا ، فستكون الجائزة الأولى:

g.get(x => x.name) //AbstractControl

مرة أخرى ، لا أعرف حقًا مدى جدوى ذلك ضمن النطاق الأكبر للأشياء.
أنا أثق في حكمك.

استمروا في العمل الجيد وشكرًا على الاستجابة السريعة.

أعتقد أن هذه الطريقة في الوصول إلى عناصر التحكم الأخرى لا تتعلق بإضافة الأدوية الجنيسة.
لا تتردد في فتح قضية أخرى حول هذا الموضوع

لا أعتقد حقًا أن الحصول على مجموعة نوع الإرجاع "مكتوب بقوة" حقًا ، يبدو أنه نصف التنفيذ المطلوب ، ولكنه خطوة في الاتجاه الصحيح.

مرحبًا ، لقد أصدرت https://github.com/Quramy/ngx-typed-forms لحل هذه المشكلة. يرجى التحقق من ذلك 😄

Quramy لقد حاولت استخدام الحزمة الخاصة بك منذ عدة أسابيع وكما أتذكر ، فهي لا تفعل الكثير من الإنفاذ :(

+1. لا يمكن احتساب عدد الحالات عندما كنت أتمنى أن يتم تنفيذه.

نفس.
الأشكال المتفاعلة الزاويّة هي إحدى الميزات التي تتفوق حقًا على أي إطار عمل آخر. سيؤدي جعل النماذج التفاعلية مطبوعة بقوة إلى الارتقاء بالمستوى التالي ، مما يزيد من اتساع الفجوة في المنافسة :)

يمكن القيام بذلك باستخدام أنواع التعيين الشرطي والتكرار ..... تم دمج أنواع التعيين الشرطي في الكتابة المطبوعة. إذا تم نشر هذا ، فلدينا فرصة لجعل النماذج المكتوبة القوية ممكنة

لا استطيع الانتظار (

Quramy للأسف لا يفرض حل جميع الوسائط إلى FormBuilder . لا يمكن أيضًا استخدام FormGroup<T> و FormControll<T> و FormArray<T> بشكل مباشر ، لأنها مجرد واجهات لا تمتد الحدث AbtractControl<T> . هذا لم يكن كافيا لمشروعنا الحالي.

باستخدام نماذج ngx-المكتوبة بقوة ، قمت الآن بإصدار مشروع نماذج مكتوبة قوي بنفسي
إنه يتعارض قليلاً مع التوافق مع الإصدارات السابقة ، لأنه لا يستخدم الأدوية الجنيسة الافتراضية. لذلك فهو يفرض عليك أن تمنح عناصر التحكم الخاصة بك نوعًا من أي نوع ، ولكنه يضيف الكثير من أمان الأنواع إلى جميع الأجزاء الأخرى وهو متوافق مع واجهة برمجة التطبيقات (API) مع التنفيذ الحالي لـ @ angular / Forms.
ربما يكون هذا بديل صالح حتى يتم تنفيذ هذه الميزة في Angular.

+1 هذه ميزة قوية ..

يجب تنفيذه في أسرع وقت ممكن)

الطريقة التي من المفترض أن يتم ترميزها بها

يوجد تطبيق مثير للاهتمام في هذا البرنامج مفتوح المصدر (AGPL)

https://github.com/concentricsky/badgr-ui/blob/master/src/app/common/util/typed-forms.ts

لقد استخدمت هذا كحل مؤقت:
آمل أن يساعد

import { FormControl, AbstractControl } from "@angular/forms";
export class NumberControl extends FormControl{
    _value: Number;
    get value(){ 
        return this._value;
    }
    set value(value){
        this._value = Number(value);
    }
}

هذا شيء فكرت فيه عدة مرات في المشاريع التي عملت عليها ، لكنني لم أستخدم وكلاء جافا سكريبت بما يكفي لمعرفة تأثير الأداء على أي شيء يلاحظ هذه القيم.

لقد قمت ببساطة بإنشاء حل بديل مخصص على مستوى FormBuilder:

import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
type ExtraProperties = { [key: string]: any } | null;
interface IModelConstructor { new(...args: any[]): any }

@Injectable({
    providedIn: 'root'
})
export class TypedFormBuilder {
    array = this.formBuilder.array;
    control = this.formBuilder.control;
    group = this.formBuilder.group;

    constructor(private formBuilder: FormBuilder) {}

    typedGroup<TGroupModel extends object>(ModelType: IModelConstructor, extraProps?: ExtraProperties): FormGroup & TGroupModel {
        const formGroup: any = this.group({
            ...new ModelType(),
            ...extraProps
        });

        return new Proxy<FormGroup & TGroupModel>(formGroup, {
            get: (target: FormGroup & TGroupModel, propName: string | number | symbol) => {
                if (propName in target) {
                    return target[propName];
                } else {
                    const property = target.get(propName as string);
                    return property && property.value;
                }
            },
            set: (target: FormGroup & TGroupModel, propName: string | number | symbol, value: any, _: any) => {
                if (propName in target) {
                    target[propName] = value;
                } else {
                    target.setValue({ [propName]: value });
                }

                return true;
            }
        });
    }
}

هذا الحل بالتأكيد ليس مصقولًا بشكل فائق ، وربما يكون من الأفضل أن تقوم FormGroup بإنشاء وصول إلى القيم الموجودة على خاصية (مثل myGroup.fields ، حيث تكون "الحقول" هي النوع المقدم). لكن هذا يوفر كتابة قوية. لتستخدمها:

generateFormGroup() {
    this.theGroup = builder.typedGroup<MyModel>(MyModel);
    this.theGroup.someProperty = 5;
}

لقد جمعت الخبرة في الأشهر الماضية باستخدام النماذج المكتوبة ، باستخدام نماذج النماذج الخاصة بي في مشروعي الحالي. إنه يوفر قيمة كبيرة عند العمل في مشروع واحد مع ثلاثة فرق مطورين وقد تحول الجميع لأنه كان رخيصًا حقًا للقيام بذلك.

لكني أريد أن أناقش ، ما إذا كان هو القرار الصحيح فقط لوضع الأدوية الجنيسة على واجهة برمجة التطبيقات الحالية. أثناء إنشاء الأنواع لجميع عناصر التحكم في النموذج ، وجدت الكثير من حالات الحافة والأشياء التي يستحيل كتابتها أو يصعب كتابتها ، لأنني أعتقد أن الكتابة الثابتة لم تكن ممكنة في ذلك الوقت ، وبالتالي لم تكن واحدة من أكبر المخاوف.
للأسف ، هذا يستهدف الوظيفة الرئيسية بـ AbstractControl#value ، والتي يجب أن تكون شيئًا مثل DeepPartial<T> ، أو AbstractControl#get مع تطبيقات مختلفة لكل فئة فرعية.
كما أن التوافق مع الإصدارات السابقة يفقد أيضًا بعض أمان النوع الناتج عن السقوط في الحالات مع أنواع any .
ربما يعتبر التفكير في واجهة برمجة تطبيقات جديدة للنماذج التفاعلية خيارًا أيضًا لهذه المشكلة؟

لذا ، هذا ما انتهى بي الأمر بفعله بينما يحدث الحل الفعلي.

إخلاء المسئولية ... لقد بدأت للتو في Angular ، لكنني على دراية تامة بـ Typescript ، لذا فأنا لا أفهم تمامًا الأشكال التفاعلية ... هذا ما انتهى به الأمر لي ، لكن بالطبع لم يكتمل تمامًا ، لقد كتبت FormGroup ، لكن أنا متأكد من أنه سيتعين كتابة المزيد من الأشياء لأنني أعرف المزيد عن النماذج ...

import { FormGroup } from '@angular/forms';

export class FormGroupTyped<T> extends FormGroup {
  public get value(): T {
    return this.value;
  }
}

وبعد ذلك يمكنني استخدامه على هذا النحو

import { FormGroupTyped } from 'path/to/your/form-group-typed.model';

interface IAuthForm {
  email: string;
  password: string;
}

const authForm: FormGroupTyped<IAuthForm> = fb.group({
  email: ['', [Validators.required]],
  password: ['', [Validators.required]],
});

const formValues: IAuthForm = this.authForm.value;
const email: string = formValues.email; 
const invalidKeyVar: string = formValues.badBoy; // [ts] Property 'badBoy' does not exist on type 'IAuthForm'. [2339]
const invalidTypeVar: number = formValues.password; // [ts] Type 'string' is not assignable to type 'number'. [2322]

Toxicable لول كان يبحث في هذه المشكلة بالضبط! هاها

cafesanu إليك بعض التحسينات على FormGroup التي كتبتها للتحقق من المُنشئ.

import { AbstractControl, AbstractControlOptions, AsyncValidatorFn, FormGroup, ValidatorFn } from '@angular/forms';

export class FormGroupTyped<T> extends FormGroup {
  readonly value: T;
  readonly valueChanges: Observable<T>;

  constructor(controls: { [key in keyof T]: AbstractControl; },
              validatorOrOpts?: ValidatorFn | Array<ValidatorFn> | AbstractControlOptions | null,
              asyncValidator?: AsyncValidatorFn | Array<AsyncValidatorFn> | null) {
    super(controls, validatorOrOpts, asyncValidator);
  }

  patchValue(value: Partial<T> | T, options?: {
    onlySelf?: boolean;
    emitEvent?: boolean;
  }): void {
    super.patchValue(value, options);
  }

  get(path: Array<Extract<keyof T, string>> | Extract<keyof T, string>): AbstractControl | never {
    return super.get(path);
  }
}

استعمال:

export class SearchModel {
  criteria: string;
}

const searchForm = new FormGroupTyped<SearchModel >({
  criteria: new FormControl('', Validators.required) // OK
  badBoy: new FormControl('', Validators.required)  // TS2345: Object literal may only specify known properties, and 'badBoy' does not exist in type '{ criteria: AbstractControl; }'.
});

لقد كتبت غلافًا صغيرًا حول FromControl يسمح بإنشاء نوع البيانات ديناميكيًا بناءً على المكالمات إلى المنشئ: https://github.com/concentricsky/badgr-ui/blob/develop/src/app/common/util/ أشكال مكتوبة

يضمن النوع الذي تم إنشاؤه ديناميكيًا أن شكل النموذج يطابق توقعاتك ، بدلاً من الاضطرار إلى التصريح المسبق عن نوع الواجهة ، ثم نأمل في إجراء المكالمات الصحيحة لإنشاء النموذج.

أود أن أرى شيئًا مشابهًا لهذا في Angular في وقت ما.

يبدو الاستخدام كالتالي:

// Create a typed form whose type is dynamically constructed by the builder calls
const group = new TypedFormGroup()
    .add("firstName", typedControl("", Validators.required))
    .add("lastName", typedControl("", Validators.required))
    .add(
        "address",
        typedGroup()
            .add("street", typedControl("2557 Kincaid"))
            .add("city", typedControl("Eugene"))
            .add("zip", typedControl("97405"))
    )
    .addArray(
        "items",
        typedGroup()
            .addControl("itemName", "")
            .addControl("itemId", 0)
    )
;

// All these are type checked:
group.value.address.street.trim();
group.controls.firstName.value;
group.untypedControls.firstName.value;
group.value.items[0].itemId;

مرحبًا بكم جميعًا في الأيام الثلاثة الماضية ، كنت d.ts gist لتحديد تعريف أكثر strit-ier لفئات ReactiveForms لإنشاء واجهة مكتوبة جديدة متوافقة مع فئة الزاوية الأصلية.
أعتقد أنه قد يكون حلاً / حلاً ممكنًا لمشكلتك 😉

//BASIC TYPES DEFINED IN @angular/forms + rxjs/Observable
type FormGroup = import("@angular/forms").FormGroup;
type FormArray = import("@angular/forms").FormArray;
type FormControl = import("@angular/forms").FormControl;
type AbstractControl = import("@angular/forms").AbstractControl;
type Observable<T> = import("rxjs").Observable<T>;

type STATUS = "VALID" | "INVALID" | "PENDING" | "DISABLED"; //<- I don't know why Angular Team doesn't define it https://github.com/angular/angular/blob/7.2.7/packages/forms/src/model.ts#L15-L45)
type STATUSs = STATUS | string; //<- string is added only becouse Angular base class use string insted of union type https://github.com/angular/angular/blob/7.2.7/packages/forms/src/model.ts#L196)

//OVVERRIDE TYPES WITH STRICT TYPED INTERFACES + SOME TYPE TRICKS TO COMPOSE INTERFACE (https://github.com/Microsoft/TypeScript/issues/16936)
interface AbstractControlTyped<T> extends AbstractControl {
  // BASE PROPS AND METHODS COMMON TO ALL FormControl/FormGroup/FormArray
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

interface FormControlTyped<T> extends FormControl {
  // COPIED FROM AbstractControlTyped<T> BECOUSE TS NOT SUPPORT MULPILE extends FormControl, AbstractControlTyped<T>
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}
interface FormGroupTyped<T> extends FormGroup {
  // PROPS AND METHODS SPECIFIC OF FormGroup
  //controls: { [P in keyof T | string]: AbstractControlTyped<P extends keyof T ? T[P] : any> };
  controls: { [P in keyof T]: AbstractControlTyped<T[P]> };
  registerControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): AbstractControlTyped<T[P]>;
  registerControl<V = any>(name: string, control: AbstractControlTyped<V>): AbstractControlTyped<V>;
  addControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): void;
  addControl<V = any>(name: string, control: AbstractControlTyped<V>): void;
  removeControl(name: keyof T): void;
  removeControl(name: string): void;
  setControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): void;
  setControl<V = any>(name: string, control: AbstractControlTyped<V>): void;
  contains(name: keyof T): boolean;
  contains(name: string): boolean;
  get<P extends keyof T>(path: P): AbstractControlTyped<T[P]>;
  getRawValue(): T & { [disabledProp in string | number]: any };
  // COPIED FROM AbstractControlTyped<T> BECOUSE TS NOT SUPPORT MULPILE extends FormGroup, AbstractControlTyped<T>
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

interface FormArrayTyped<T> extends FormArray {
  // PROPS AND METHODS SPECIFIC OF FormGroup
  controls: AbstractControlTyped<T>[];
  at(index: number): AbstractControlTyped<T>;
  push<V = T>(ctrl: AbstractControlTyped<V>): void;
  insert<V = T>(index: number, control: AbstractControlTyped<V>): void;
  setControl<V = T>(index: number, control: AbstractControlTyped<V>): void;
  getRawValue(): T[];
  // COPIED FROM AbstractControlTyped<T[]> BECOUSE TS NOT SUPPORT MULPILE extends FormArray, AbastractControlTyped<T[]>
  readonly value: T[];
  valueChanges: Observable<T[]>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T>[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T>[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

اختبار الاستخدام في stackblitz - يرجى تنزيل الكود وتشغيله في VSCode المحلي. لا أعرف لماذا في stackblitz ، لا تكون أخطاء setValue و pathValue صحيحة ...

في موضوع Twitter هذا ، أناقش مع

نرحب جدا بأي تعليقات واقتراحات ومساعدة!

يسمى الحل الخاص بي @ng-stack/forms . إحدى الميزات المثيرة للاهتمام هي الاكتشاف التلقائي لأنواع النماذج.

لذلك ، لا داعي للقيام بذلك في المكونات الخاصة بك:

get userName() {
  return this.formGroup.get('userName') as FormControl;
}

get addresses() {
  return this.formGroup.get('addresses') as FormGroup;
}

الآن إفعل هذا:

// Note here form model UserForm
formGroup: FormGroup<UserForm>;

get userName() {
  return this.formGroup.get('userName');
}

get addresses() {
  return this.formGroup.get('addresses');
}

لمزيد من المعلومات ، راجع @ ng-stack / Forms

مرحبًا ، لقد عملنا مع zakhenry مؤخرًا على شيئين:

  • تحسين أنواع النماذج
  • تحسين طريقة إدارة النماذج الفرعية (داخل المكونات الفرعية)

نحن لا نغطي جميع الأنواع كما فعل @ dmorosinotto ولكن لدينا بعض الأمان الذي سيساعد بالتأكيد عند إعادة بناء المعامل (لكل من ts و html: tada :).

إذا أراد أي شخص إلقاء نظرة على lib: ngx-sub-form

مرحبًا @ maxime1992 لقد ألقيت نظرة على Controls<T> ولكنه لا يقيد AbstractControl to <T[K]>
هل لي ان اسألك لماذا؟ هل تتركه "بدون كتابة" لأنك تشعر بالحاجة إلى تغيير النوع في وقت التشغيل باستخدام شيء مثل أساليب setControl أو registerControl لإعادة تعريف وتغيير FormControl وربما النوع المرتبط به؟
نظرًا لأن TypedForms.d.ts الخاص بي أكثر صرامة و "يفرض" AbstractControlTyped to the type T<P> لكنني لا أعرف ما إذا كان هذا النوع من الاختيار يفرض / يعطل شيئًا ما في ReactiveForms API الأصلي مسموح به وربما يستخدمه شخصا ما...

اي فكرة؟ أي حالة حقيقية للنظر فيها؟
قد تساعدني أي تعليقات على هذا في اتخاذ قرار بشأن كيفية تغيير التعريفات التي قمت بإنشائها والعلاقات العامة التي أعمل عليها ...
شكرا

ملاحظة: العمل الرائع على النموذج الفرعي ngx فكرة استخدام ControlValueAccessor للتعامل مع النماذج الفرعية هي شيء كنت أجربه أيضًا 😉

بالنسبة لـ @ ng-stack / Forms ، تمت إضافة دعم التحقق المكتوب.

الفصول الدراسية FormControl ، FormGroup ، FormArray وجميع طرق FormBuilder
قبول "نموذج التحقق من الخطأ" كمعامل ثاني لعامل عام:

class ValidationModel {
  someErrorCode: { returnedValue: 123 };
}
const control = new FormControl<string, ValidationModel>('some value');
control.getError('someErrorCode'); // OK
control.errors.someErrorCode; // OK
control.getError('notExistingErrorCode'); // Error: Argument of type '"notExistingErrorCode"' is not...
control.errors.notExistingErrorCode; // Error: Property 'notExistingErrorCode' does not exist...

بشكل افتراضي ، يتم استخدام نوع خاص يسمى ValidatorsModel .

const control = new FormControl('some value');
control.getError('required'); // OK
control.getError('email'); // OK
control.errors.required // OK
control.errors.email // OK
control.getError('notExistingErrorCode'); // Error: Argument of type '"notExistingErrorCode"' is not...
control.errors.notExistingErrorCode // Error: Property 'notExistingErrorCode' does not exist...

ValidatorsModel على قائمة بالخصائص المستخرجة من typeof Validators وأنواع المرتجعات المتوقعة:

class ValidatorsModel {
  min: { min: { min: number; actual: number } };
  max: { max: { max: number; actual: number } };
  required: { required: true };
  requiredTrue: { required: true };
  email: { email: true };
  minLength: { minlength: { requiredLength: number; actualLength: number } };
  maxLength: { requiredLength: number; actualLength: number };
  pattern: { requiredPattern: string; actualValue: string };
}

@ dmorosinotto شكرًا جزيلاً لك لكني لست قادرًا على الاستخدام
Validators.compose([Validators.required, Validators.maxLength(20), Validators.minLength(3)])

مرحبًا youssefsharief ، هل يمكنك أن تعطيني مزيدًا من التفاصيل حول مشكلتك مع المدققين؟ ما نوع الخطأ / المشكلة التي وجدتها باستخدام .d.ts؟

إذا كان بإمكانك نشر نموذج من التعليمات البرمجية أو ملف stackblitz ، فسأشاهده وسأحاول المساعدة في حل المشكلة إذا وجدت حلاً 😉

ل @ng-stack/forms أضاف دعمًا لـ input[type="file"] .

انظر أيضا مثال على stackblitz

لقد استخدمت أيضًا الأسلوب "الكلاسيكي" باستخدام FormData لتحميل الملفات ، ولكن هناك العديد من الأرباح:

  • استرداد اسم إدخال النموذج تلقائيًا ، على سبيل المثال <input type="file" name="someName"> -> formControl.value مع someName اسم الحقل؛
  • دعم السمة multiple مع تعيين اسم الحقل بشكل صحيح (لاحظ userpic[] هنا)
<input type="file" multiple name="userpic" [formControl]="formControl">

""
formData.append ('userpic []'، myFileInput.files [0]) ،
formData.append ('userpic []'، myFileInput.files [1]) ،

- `Validators` have four static methods for files
```ts
import { Validators } from '@ng-stack/forms';

Validators.fileRequired;
Validators.fileMaxSize(1024);
Validators.filesMaxLength(10);
Validators.filesMinLength(2);

KostyaTretyak مما رأيته حتى الآن يبدو أن الحزمة الخاصة بك تمثل حلاً رائعًا لمشكلات الكتابة الحالية في النماذج التفاعلية الزاويّة. هل فكرت يومًا في المساهمة في Angular نفسها بدلاً من تنفيذ التنفيذ بنفسك؟

نحن تقريبًا في الزاوية 8 وما زلنا لا نملك أشكالًا مكتوبة خارج الصندوق في بيئة TypeScript 100٪ والتي تبدو غريبة جدًا بالنسبة لي.

kroeder ، أرى فقط أن الإصدار الحالي تم إنشاؤه منذ أكثر من عامين (تقريبًا بعد إصدار Angular 2 مباشرة) ، وأرى أنه تم إنشاء طلب سحب مشابه منذ 1.5 عام دون دمج ...

ولكن إذا كان @ng-stack/forms مشهورًا ومتوافقًا مع Angular 8+ ، فربما أقوم بإنشاء طلب السحب في المستقبل.

KostyaTretyak يبدو رائعًا :) لقد كنت أستخدم https://github.com/no0x9d/ngx-strongly-typed-forms التي تم ذكرها سابقًا في هذا الموضوع وتم إنشاؤها بواسطة @ no0x9d كيف تختلف مكتبتك عن ذلك؟

ZNS ، لا أعرف ، ولكن بعد مراجعة سريعة ، أعتقد أنه لا يوجد عام للتحقق من الصحة في ngx-strongly-typed-forms ، بالإضافة إلى اكتشاف الأنواع المناسبة لعناصر تحكم النموذج تلقائيًا . ربما انا على خطأ.

تضمين التغريدة كما ذكر أعلاه ، أنا مؤلف نماذج ngx-المكتوبة بقوة .
لقد أجريت مراجعة سريعة لمجموعة الميزات @ng-stack/forms وأعتقد أن هناك بعض الاختلافات الصغيرة.

هناك فرق تصوري بين جميع الحلول أو الحلول تقريبًا وبين مشروعي.
في معظم الحالات ، يتم تمديد Angular FormControl الأصلي أو تغليفه بواسطة وكيل. يتم تجاوز بعض التوابع بتوقيعات أنواع أخرى وتفويضها للوظيفة الأصلية.
يقدم هذا طبقة جديدة ذات تأثير مهم على الأداء ، ولكن الأهم من ذلك هو الكود الذي يجب صيانته ويمكن أن يؤدي إلى أخطاء في مشروعك.
في رأيي ، هذا غير ضروري لإجراء فحوصات ثابتة في وقت الترجمة. جزء وقت التشغيل الوحيد هو NgModule الذي يوفر نموذج FormBuilder الخاص بي المكتوب ، والذي هو في الواقع Angular FormBuilder الأصلي. كل شيء آخر هو مجرد رمز Angular.

في المقارنة المباشرة ، ليس لدي نموذج ValidationModel والتحويل من الكائنات إلى FormGroups و Arrays إلى FormArrays ، لكنني أجريت بعض التغييرات ذات الرأى على AbstractControl#get لجعلها أكثر أمانًا وكتبت وسيطات مدقق.

مع بعض الإضافات الصغيرة ، يمكن أن يكون الرمز الخاص بي متوافقًا مع الإصدارات السابقة ويمكنني إنشاء طلب سحب. لكن طلب سحب مماثل لا معنى له لفترة طويلة.
ولكن إذا كانت هناك جهود لإدخال هذا في Angular ، فسأكون سعيدًا بتوحيد قوانا. على الجانب الآخر ، أود أن أرى واجهة برمجة تطبيقات جديدة للنماذج المصممة بشكل أفضل ليتم كتابتها بدقة. انظر تعليقي للحصول على التفاصيل.

سيود +1 رؤية هذه الميزة. أي شخص يعمل على ذلك؟

اهتزت للفريق الزاوي؟

لا يهتمون بالمطورين. لديهم تراكم خاص بهم ، مع لعبة ورق وميزات رهيبة -5٪ -تقليص حجم الحزمة.

@ Lonli-Lokli

لا يهتمون بالمطورين.

الانتقاد سهل. هل تهتم أكثر بالمطورين الآخرين؟
لم أرَ PR منك لتحسين النماذج ولا أي نوع من التعليقات البناءة أو RFC لجعل الأمور تتحرك إلى الأمام: man_shrugging:

لديهم الأعمال المتأخرة الخاصة بهم

مستحيل: خائف :!
يعطي الناس الأولوية للأشياء التي تحتاجها الشركة _ [من يدفع لهم] _؟
يا له من عار!
image

ميزات رائعة - 5٪ - تقليل حجم الحزمة.

من الواضح أنك تتحدث عن Ivy والفرق الصغير جدًا (حاليًا) في حجم الحزمة.
Ivy قيد التجربة حاليًا وعليك الاشتراك. يا لها من مفاجأة أن الأشياء ليست مثالية بعد! : التفكير:
نعم ، قيل إن Ivy سيساعد في تقليل حجم الحزمة ويسمح للأدوات بالقيام بهز شجرة أفضل على التطبيقات. ونأمل أن يأتي ذلك! في الوقت الحالي ، هم يعملون عليها فقط للتأكد من أنها لا تكسر أي شيء ويمكن أن تساعد لاحقًا في الحصول على معلومات تصحيح أخطاء أفضل ، وتجميع متزايد على أساس مكون بدلاً من أساس الوحدة ، وهز الشجرة. لكن الأدوات اللازمة للحصول على تلك الشجرة ستنجح لاحقًا.

لذا يرجى محاولة التحلي بالاحترام وتجنب توجيه تهم إلى الأشخاص الذين يوفرون لك إطار عمل مفتوح المصدر مجانًا. الأمور ليست مثالية ، فالعمل الضخم جار ، نعم يبدو أن بعض المشكلات متروكة ، لكن إعادة البناء هذه كانت ضرورية ولن يكون هناك أي وقت جيد لصنع شيء بهذا الحجم ، كان يجب أن يحدث في وقت ما.

أنا الآن خارج هذا النقاش لأنني لا أريد احتكار هذا الموضوع بالحديث عن أشياء غير منتجة. * يطير بعيدا *

تضمين التغريدة
هناك الكثير من العلاقات العامة الأخرى (386 حتى الآن) ، هل تعتقد أن واحدًا آخر سيغير أي شيء؟
عند الحديث عن هذه المشكلة ، لا تزال هذه العلاقات العامة ذات الصلة (https://github.com/angular/angular/pull/20040) غير مدمجة.

بالحديث عن إعادة البناء ، تم ذكر Ivy منذ عام. أعلم أن أي شخص يمكن أن يعالجها هي ميزة رئيسية للمطورين ، لكنني شخصياً أفضل أن أرى أن الإصلاحات مهمة لأكبر عدد ممكن من المطورين.
https://github.com/angular/angular/issues؟utf8=٪E2٪9C٪93&q=is٪3Aissue+is٪3Aopen+sort٪3Areactions-٪2B1-desc

ما زلت آمل أن يكون Angular للمطورين ، وليس للتسويق ، وآمل أن أرى المزيد من المشكلات مغلقة بناءً على رد فعل المجتمع. هذا سؤالي ما هي الأولويات هنا.

من الواضح أنه يمكن معالجة هذه المشكلة من خلال تحديث لواجهة برمجة التطبيقات الحالية ، ولكن ، بشكل منفصل ، قمت بإنشاء اقتراح لتحسين ReactiveFormsModule لمعالجة عدد من المشكلات المعلقة بها ، بما في ذلك هذه المشكلة.

تتضمن بعض المشكلات الأخرى التي تمت معالجتها إمكانية الاشتراك في التحديثات على أي خاصية والقدرة على التحقق من صحة عنصر تحكم بشكل غير متزامن عبر خدمة (بدلاً من asyncValidator ). يمكنك معرفة المزيد في # 31963. نرحب بالتعليقات!

كما وعدت من قبل ، قمت بإنشاء طلب سحب . يحتوي هذا PR على جزء فقط من ميزة @ng-stack/forms (بدون: التحقق من الصحة ، والتحكم التلقائي في الكشف عن النموذج ودعم الإدخال [ملف]).

مرحبًا ، أردت مشاركة تحديث من فريق Angular: نسمعك أن هذه نقطة ألم كبيرة. سنبدأ العمل على نماذج مكتوبة بقوة أكبر قريبًا ، والتي ستتضمن النظر في العلاقات العامة الحالية ومراجعة جميع تعليقاتك مرة أخرى. شكرًا لكم جميعًا على الوقت الذي قضيته في ترك أفكاركم!

أوه!!!!! الخروج!

هذه أخبار جيدة جدًا ، فريق Angular ، شكرًا!
بمجرد إصدار الإصدار ، سأقوم بإهمال مساعد الأشكال الزاوية الآمنة .

نعم !!!!

أنا متحمس جدا!! شكرا لك فريق Angular !!

هل يمكننا التوقف مع رد الفعل غير المرغوب فيه؟
استخدم الرموز التعبيرية لردود الفعل ، لأنها مخصصة لـ https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/ - شكرًا.

كما أكد فريق Angular العمل على نموذج رد الفعل المكتوب بقوة. أرغب في مشاركة أسباب تطبيقي التي تستخدم بشكل كبير نوع infer لاستخراج نوع القيمة من أجل الحصول على تجربة تطوير مكتوبة ثابتة وسلسة.

النهج الأول: بدأ بنوع القيمة

عندما بدأت في تصميم FormGroup، لقد استخدمت نوع قيمة بسيطًا بديهيًا مثل T .

FormGroup<{ id: string, username: string | null }>

لكنني وجدت أنه عندما يتعين علي التعامل مع بنية نموذج الجدول المعقدة ، فمن الصعب جدًا إجراء ربط آمن من النوع في Angular HTML.

type User = { id: string, username: string | null }
const table: FormArray<User> = new FormArray([{ id: '0', username: 'gaplo917'}])

const row: FormGroup<User> = table.at(0) as FormGroup<User> // you must do a type cast

// NO way to get type-safe binding in Angular HTML
<input [formControl]="table.at(i).controls.id" />

يتطلب التنفيذ أعلاه من المطورين عمل نوع مخصص مملة وعرضة للخطأ. IMHO ، هذا يفقد تمامًا سبب استخدام النموذج التفاعلي المكتوب بشدة.

الطريقة الثانية: بدأت بنوع التحكم

لأن استخدام نوع قيمة بسيط لا يعمل بسلاسة. خطرت لي فكرة أخرى لاستخدام KeyValueControlكـ T واستخدم infer استخراج نوع القيمة من KeyValueControlبشكل متكرر.

export type KeyValueControl<V> = {
  [key in keyof V]: V[key] & AbstractControl
}

export type InferTypedForm<Type> = Type extends TypedFormControl<infer X>
  ? X
  : Type extends Array<TypedFormControl<infer X1>>
  ? X1[]
  : Type extends Array<TypedFormGroup<infer X2>>
  ? Array<InferTypedFormGroup<X2>>
  : Type extends TypedFormGroup<infer X3>
  ? InferTypedFormGroup<X3>
  : Type extends TypedFormArray<infer X4>
  ? Array<InferTypedForm<X4>>
  : never

export type InferTypedFormGroup<T> = { [key in keyof T]: InferTypedForm<T[key]> }

export type InferTypedFormArray<T> = InferTypedForm<T[]>

export class TypedFormGroup<T extends KeyValueControl<T>> extends FormGroup {
  readonly controls: T
  readonly valueChanges: Observable<InferTypedFormGroup<T>>
  readonly statusChanges: Observable<FormStatus>
  readonly value: InferTypedFormGroup<T>
  ...
}

نتيجة ل،

interface Foo {
  first: TypedFormControl<string | null>
  last: TypedFormControl<string | null>
}

const table: FormArray<FormGroup<Foo>>

const row: FormGroup<Foo> = table.at(0) // typescript compile OK!

// type-safe binding in Angular HTML, all auto-complete (`id`, `username`) finally works!
<input [formControl]="table.at(0).controls.id" />

عرض حي

Edit gaplo917/angular-typed-form-codesandbox

لقطات حية تجريبية من IDE

هذه تجربة كاملة تلقائية حقيقية بأشكال معقدة.
Screenshot 2020-06-12 at 19 02 10

النماذج المطبوعة الزاوية

100٪ متوافق مع وحدات النموذج التفاعلي الحالية
https://github.com/gaplo917/angular-typed-forms

التعليقات والتحسينات مرحب بها آمل أن تتمكن النماذج التفاعلية شديدة الكتابة القادمة من التعامل مع نماذج النماذج المعقدة مثل الجدول والنماذج الفرعية المتداخلة.

IgorMinar ، فريق Angular Core ، وأعضاء المجتمع الزاوي .

يركز هذا التعليق الطويل بشكل كامل على تصريحين أبرزهما مؤلف البطاقة " ضد الممارسات " و " المطبوع بشدة ".

أود أن أقترح بدلاً من النهج القائم على الواجهة للنموذج التفاعلي المكتوب بشدة ، يجب أن نختار النهج المستند إلى الفصل ولكن ليس بالطريقة المذكورة في مجموعات / نماذج ng . لا نوصي أيضًا بتغيير قاعدة التعليمات البرمجية الخاصة بـ Angular Ractive Forms ، لأنه يمكننا تحقيق نموذج مكتوب بقوة دون تغيير قاعدة التعليمات البرمجية من خلال عدة طرق. اسمحوا لي أن أصف بالتفصيل التحديات عالية المستوى التي أراها في النهج القائم على الواجهة ، والنهج المدفوع بالفئة أكثر سهولة من الآخرين ، كما أننا نحصل على كائن FormGroup مكتوب بقوة. في كلتا الحالتين ، يكون كائن FormGroup الخاص بنا مكتوبًا بقوة ، ولا نفقد قوة TypeScript Type في النهج المستند إلى الفصل.

اقتراحي

نظرًا لأننا جميعًا على دراية بممارسات OOP ، يمنحنا الفصل مزيدًا من المرونة وإمكانية صيانة الكود. بعض الفوائد التي أسلط الضوء عليها أدناه:

  1. افصل كودنا.
  2. رمز أقل مقارنة بالنهج الحالي بالإضافة إلى النهج المستند إلى الواجهة.
  3. يمكننا استخدام Custom Decorators في الممتلكات.
  4. الكود قابل للقراءة ، ويمكن صيانته ، وقابل للتوسيع.
  5. باستخدام هذا الأسلوب ، لا يمكننا كتابة منطق الأعمال في القالب ، مثل وضع *ngIf مع شروط متعددة لإظهار رسائل الخطأ. أعتقد أن القوالب ليست مخصصة لكتابة منطق الأعمال.
  6. المزيد ...

اسمح لي بتحويل رمز الواجهة المذكور أعلاه إلى Class وقم بتطبيق ديكور التحقق على خصائص Class . نحن هنا نتبع ممارسات مبدأ المسؤولية الفردية . ألق نظرة على الكود أدناه:

image

دعنا نفكر في بعض الحالات ونقارنها بالواجهة وطريقة الكتابة القوية المعتمدة على الفئة التي تساعدنا على فهم الاختلاف في كل منهما.

1. إنشاء FormGroup
نحن هنا نستخدم نفس مثيل FormBuilder ونفس الطريقة group . لكن اسم وحدة الاستيراد سيكون مختلفًا مثل ReactiveTypedFormsModule بدلاً من ReactiveFormsModule . لنقم بإنشاء FormGroup :
image

حسب الكود أعلاه يأتي السؤال ،

هل سيعمل الأسلوب الحالي بعد استيراد ReactiveTypedFormsModule ؟
نعم ، ستعمل ، ولن يتغير شيء بعد استيراد ReactiveTypedFormsModule .

دعنا نرى بسرعة الحالات الأخرى وننهي هذا المنشور.

2. قم بتغيير قيمة FormControl
بدلاً من استدعاء طريقة setValue ، يمكننا تعيين القيمة مباشرة على الخاصية Class . سيتم تلقائيًا تعيين قيمة FormControl .

image

3. تنفيذ منطق الأعمال بناءً على تغييرات قيمة FormControl
بدلاً من الاشتراك في FormControl ValueChanges ، استخدم قوة طريقة setter في TypeScript.

image

4. تحويل قيمة الإدخال
نحن نركز على مكتوب بشدة ولكن ماذا عن تلك القيم التي تأتي من عنصر تحكم الإدخال مثل التاريخ نحصل على القيمة بتنسيق String لكننا نتوقع Date التنسيق في رمز TS ، للتغلب على هذه المشكلة ، نقوم بإنشاء توجيه أو طريقة لتحويل القيمة كما هو مطلوب. لن أعرض هنا الكود الحالي لأنه أخرق بعض الشيء لأنه يتعين علينا إنشاء التوجيه والقيام بأشياء بلاه بلاه ....

@toDate() // this will convert the value before assigning the value in 'dob' property.
dob:Date;

4. قم بتغيير قيمة FormGroup FormControl المتداخلة
يمكننا تعيين القيمة مباشرة في الخاصية المعنية بدلاً من الحصول على كائن FormGroup المتداخل واستدعاء طريقة SetValue .

image

5. إضافة FormGroup في FormArray المتداخلة
لا مزيد من القول ، ألق نظرة على الكود أدناه 😄.

image

مرجع كائن FormGroup في قالب HTML

كود بسيط 😄. لن يتم تغيير أي شيء في قالب HTML ، لكنك ستحصل على المزيد في قالب HTML أيضًا. يرجى الرجوع إلى الكود أدناه

<form [formGroup]="user.formGroup">
   <input type="text" formControlName="firstName"/>
   <small >{{user.formGroup.controls.firstName.errorMessage}}</small>
<!--Nested FormGroup-->
   <div [formGroup]="user.address.formGroup">...</div>
<!--Nested FormArray - Skills-->
   <div *ngFor="let skill of user.skills">
       <div [formGroup]="skill.formGroup">...</div>
   </div
</form>

رابط Stackblitz : اكتب بقوة نموذج العمل التفاعلي
مثال على جيثب : نموذج تفاعلي مكتوب بشدة

ajayojha ،

  1. النفقات العامة المصاحبة لأنواع TypeScript سيئة.
  2. التحقق من وقت التشغيل بمساعدة المصممين - إنه جيد.
  3. لماذا تحتاج setValue() و valueChanges() إذا كان هناك محددات / حاصلون؟

ماذا اعتقد:

  1. كتابة أنواع TypeScript مثل كتابة الاختبارات الثابتة. قد ينشئ شخص ما تطبيقات بدون اختبارات لأنهم يعتقدون أنها غير ضرورية ، لكنها ممارسة سيئة.
  2. التحقق من وقت التشغيل بمساعدة المصممين - يمكن أن يكون فكرة جيدة ، موافق.
  3. بالإضافة إلى setValue() هناك أيضًا patchValue() ، reset() ، والتي تعمل أيضًا مع قيمة النموذج. سيؤدي استبدال setValue() بأداة ضبط إلى جعل الشفرة غير متسقة. بالإضافة إلى ذلك ، عندما يتعين علينا كتابة المحددات لكل خاصية من خصائص نموذج النموذج ، فسيتم إضافة المزيد من النفقات العامة للتعليمات البرمجية بالإضافة إلى زيادة الأداء في حالة استخدام الأحرف. يعتبر خلط أسماء خصائص نموذج النموذج مع خصائص التحكم في النموذج فكرة سيئة في رأيي.

شكرًا ، KostyaTretyak على مخاوفك بشأن تعليقي ، وسأكون سعيدًا بالإجابة على نفس السؤال ، يرجى الاطلاع أدناه على تعليقاتي وفقًا لذلك :).

  1. النفقات العامة المصاحبة لأنواع TypeScript سيئة.

فقط لمعلوماتك ، يتم كتابة كائن مجموعة النموذج بقوة. الواجهات جيدة ولكنها ليست مناسبة لكل منطقة ولا أقول أن أنواع TypeScript سيئة ، ولا أعتقد في مكان ما أشرت فيه إلى أن الأنواع سيئة. شاغلي الوحيد هو الواجهة لأننا نلغي ممارسات تصميم البرامج باستخدام نهج الواجهة أو حتى يمكنني القول إننا نستخدم نهج الواجهة في المكان الخطأ ونهجنا المقترح هو Class. بقدر ما فهمت من خلال نهج Class ، فإننا لا نتنازل عن فائدة أنواع TypeScript التي نحصل عليها في الواجهة أو حتى أقول إننا نحصل على أكثر من نهج الواجهة من حيث قابلية القراءة وقابلية التوسع وقابلية الصيانة.

هل نستخدم الممارسة الصحيحة للواجهة للنموذج التفاعلي المكتوب بشدة؟

اسمحوا لي أن أصف أكثر قليلاً من حيث الواجهة هي الممارسة السيئة (حسب رأيي) للنموذج التفاعلي المكتوب بشدة.

تعد أنواع TypeScript جيدة ، لكن لا يُقترح أنه في كل مكان يتعين علينا خلط أي شيء لا يتوافق مع ممارسات البرامج مثلما ذكرت بوضوح مشاكل الواجهة. فقط للتفكير في مخاوفي البارزة على الواجهة. اسمحوا لي أن أشارك حالتي ، في أحد تطبيقات مؤسستي التي تحتوي على أكثر من 6k + مكون. إذا كنت أستخدم نهج الواجهة ، فسوف يسألني فريق التطوير أسئلة جيدة قبل إجراء التغييرات:

  • ما هي الواجهة التي يتعين علينا استخدامها ، حيث يتم استخدام نفس الكيان في مكونات متعددة بخصائص مختلفة. إذن هل نحتاج إلى إنشاء مكون الواجهة المنفصلة؟ إذا كانت الإجابة بنعم ، فاقرأ الحالة الثانية.
  • إذا اتبعنا النهج أعلاه ، فما هو النهج الذي يجب أن أستخدم فيه كلتا خصائص الواجهة في مكون واحد أو أكثر. لحل هذه المشكلة ، يمكنني إنشاء واجهة أخرى وتوسيعها. هل هذا جيد لإنشاء المزيد والمزيد من الملفات لمجرد اهتزاز النموذج التفاعلي النوع بقوة؟ ماذا عن الصيانة ؟، ليس لدي إجابة ، باستثناء القول بأن Angular Team يقدم هذا الحل لذا فهو جيد :) (إذا اختار Angular Team نهج الواجهة).
  • إذا اتبعت نهج أ + ب ، ففي بعض مكوناتي تتطلب خصائص قليلة ليس كلها ، إذن؟ لدي ثلاثة حلول لأقدمها لفريق التطوير الخاص بي.

    • أنشئ واجهة جديدة وانسخ / الصق الخصائص المطلوبة في الواجهة المنشأة حديثًا. هذا هو أقذر نهج في عالم البرمجيات. هذا يخلق الكثير من المشاكل عندما يتم تغيير أي خاصية واحدة على جانب الخادم ، ومن ثم يصعب تتبع المناطق على حد سواء في عدد الواجهات لتغيير اسم الخاصية.

    • قم بتعيين الخاصية nullable. إذا كنت أقوم بتعريف الخاصية nullable فلماذا يجب علي اتباع نهج "B"؟. مرة أخرى ليس لدي إجابة :( لإعطاء فريق التطوير الخاص بي.

    • لا تنشئ واجهة أخرى ، واستخدم نوع الأداة المساعدة "الجزئي" ، واجعل كل خاصية اختيارية. من خلال القيام بذلك ، فإننا نفقد الفائدة الفعلية للواجهة. هذا أيضا ضد الممارسات. إذا كان علي اتباع هذا ، فلماذا يجب علي اتباع نهج "أ" ، مرة أخرى لا توجد إجابة :).

    • إذا كنت أقوم بإلغاء كل / بعض الخصائص ، فماذا عن قابلية قراءة الكود وكيف يمكنني الحكم على عدد الخصائص المطلوبة لتمرير القيمة إلى الخادم. ثم لا بد لي من التحقق في المكون المعني والحصول على لمحة. قلق المقروئية الرئيسية.

الآن فقط للتفكير في الحالات المذكورة أعلاه من منظور أكبر ومقارنتها بأنواع TypeScript مع واجهة على النماذج التفاعلية لـ Strongly Typed. أعتقد أن كل نهج جيد سيوفر وقت التطوير ، وفي هذا النهج آسف للقول إنني لا أرى أي فائدة وفقًا لمبدأ وممارسات تصميم البرامج.

  1. التحقق من وقت التشغيل بمساعدة المصممين - إنه جيد.

أتفق مع تعليقك على " إنه جيد " ، أسلوب الديكور الذي لا يمكننا تحقيقه في نهج الواجهة. أعتقد أن هذه هي أقوى ميزة في TypeScript ، فلماذا لا يمكننا استخدام نفس الشيء في نهج النموذج التفاعلي وإعطاء فريق التطوير التحكم الكامل في خصائص الكائن الخاصة بهم.

  1. لماذا تحتاج setValue () إذا كان هناك محددات؟

أين قلت إنني بحاجة إلى "setValue ()"؟ لا أحتاج إلى setValue ولم أعرض في المثال الذي أستدعي فيه طريقة setValue في Class Driven Approach. يرجى تصحيح لي إذا كنت مخطئا.

  1. كتابة أنواع TypeScript مثل كتابة الاختبارات الثابتة. قد ينشئ شخص ما تطبيقات بدون اختبارات لأنهم يعتقدون أنها غير ضرورية ، لكنها ممارسة سيئة.

أنا لا أقول أن أنواع TypeScript تشبه كتابة اختبارات ثابتة. لكنني لا أتفق مع تغييرات الالتزام في الفئات الأساسية للشكل التفاعلي ، وأعتقد أنه يمكننا تحقيق نفس الشيء دون لمس تعريف الفصل. هنا ، يمكننا استخدام القوة الفعلية للواجهة التي لا نستخدمها وفقًا للالتزامات حتى الآن ، فهل هذه ممارسة جيدة أن المنطق يعمل لفترة طويلة ونقوم بإضافة الأنواع العامة عن طريق تعيين القيمة الافتراضية لـ ' أي'؟
أعتقد أنه يمكننا تحقيق الشيء نفسه دون لمس الأصناف الأساسية للنموذج التفاعلي. لا أعرف لماذا لا نستفيد من الواجهة في هذا بدلاً من تغيير تعريف الفئة الأساسية وكذلك تغيير المواصفات.

  1. التحقق من وقت التشغيل بمساعدة المصممين - يمكن أن يكون فكرة جيدة ، موافق.

من الجيد معرفة أن كلانا يمثل نفس الصفحة في هذه :).

  1. بالإضافة إلى setValue () هناك أيضًا patchValue () و reset () والتي تعمل أيضًا مع قيمة النموذج. سيؤدي استبدال setValue () فقط بجهاز ضبط إلى جعل الرمز غير متسق. بالإضافة إلى ذلك ، عندما يتعين علينا كتابة المحددات لكل خاصية من خصائص نموذج النموذج ، فسيتم إضافة المزيد من عبء التعليمات البرمجية بالإضافة إلى زيادة الأداء. يعتبر خلط أسماء خصائص نموذج النموذج مع خصائص التحكم في النموذج فكرة سيئة في رأيي.

اسمحوا لي أن أصف النقطة أعلاه في ثلاثة أقسام استدعاء طريقة ، وأداء واضعة ، وخلط خصائص نموذج النموذج.

طريقة الاتصال: كما هو متوقع ، أثناء كتابة هذا المنشور كنت أفكر في أن شخصًا ما قد يقترح علي استخدام طريقة "patchValue" أو "إعادة التعيين". مرة أخرى ، أود أن أقول في حالة العالم الحقيقي يستخدم فريق التطوير في الغالب طريقة "setValue" بدلاً من patchValue أو طرق أخرى (هذه هي تجربتي وفقًا لمراجعة Angular Application Code و Stackoverflow Posts setValue vs patchValue). تركيزي هو فقط استدعاء طريقة تعيين القيمة ، بغض النظر عن الطريقة التي ندعو إليها.

أداء الواضع : أوافق على بيان المُحددين الذي يُنشئ زيادة في الأداء. إذا كانت هذه هي الحالة ، فسأقول إنه يتعين علينا الإصلاح أولاً في Angular Project لأنه لربط النموذج التفاعلي ، يستخدم Angular Framework طريقة setter في فئة Control Value Accessor والعديد من التوجيهات الأخرى ، وهذا يؤدي إلى زيادة الأداء دون استخدام نهج الطبقة. شيء آخر هو نفس النهج الذي نستخدمه أيضًا في مكونات متعددة مع مشاكل الأداء. لذا ، لا أعتقد أن هذا مصدر قلق كبير. الآن فيما يتعلق بالأداء ، يرجى المقارنة مع النهج الحالي وطريقة الإعداد (هذا اختياري ، يمكن لفريق التطوير الاختيار إذا رغبوا في ذلك مثل ChangeDetectionStrategy في Angular. يرجى الرجوع إلى المثال على موقع وثائق rxweb للاختيار هذه الحالة. احكم على أن عدد الوظائف التي تستدعي عند الاشتراك تتغير القيمة ثم بعد تعيين القيمة أو استدعاء طريقة setter مباشرة. أعتقد أن هذا أكثر بديهية من حيث تقليل تنفيذ الكود مقارنة بالتغييرات في القيمة ، وانخفاض حجم البناء الحزمة وإمكانية قراءة الكود والعديد من الأشياء الجيدة الأخرى.

خلط الخصائص : إذن ما هو رأيك ، هل تقوم بتعيين اسم خاصية FormControl مختلف عن اسم خاصية إرجاع الخادم. إذا كانت الإجابة بنعم ، فسأقول إن هذه مشكلة كبيرة في الكود لأنه في كل مرة يتعين علي تغيير اسم الخاصية قبل نشرها على الخادم ، آسف ولكني لا أفضل ذلك في جميع أنحاء التطبيق. إذا كنت أعتبر رأيك جيدًا في نموذج الطلب الخاص بي الذي يحتوي على متوسط ​​40+ حقلاً ، فيجب أن أقوم بتعيين كل قيمة خاصية يدويًا ، فقط للتفكير في الكود الموجود في المكون لمجرد اهتزاز تعيين القيمة وحجم بناء المنتج. هل هذا رأي أفضل من النهج الطبقي؟
تعال الآن إلى الحل المقترح ، فنحن لا نخلط بين شيئين في واحد. تختلف خصائص FormControl وتختلف خصائص الفئة عن نوع البيانات المعني. إذا كنت ترغب في تغيير اسم الخاصية مثل اسم خاصية FormControl مختلف عن خاصية البيانات ، فيمكنك ذلك ، يرجى الرجوع إلى وثائق حزمة النموذج التفاعلي rxweb. لذلك لا توجد مشكلة لأن شعورك بالسوء (خلط اسم الخاصية بأسماء التحكم في النموذج) له حل في النهج المقترح.

آمل أن أكون قد أجبت على جميع مخاوفك ، فلا تتردد في مشاركتها إذا كان لديك أي مخاوف أخرى بشأن هذا :).

كما قلت في تعليقي السابق ، ليست هناك حاجة لتغيير الفئات الأساسية للنموذج التفاعلي لأنه يمكننا تحقيق نفس الأشياء باستخدام قوة ممارسات مبدأ فصل الواجهة. إليك حل النموذج التفاعلي من نوع End-to-End @ rxweb / types . يعمل هذا بشكل جيد مع نهج الواجهة بالإضافة إلى نهج الفئة :).

كيف تبدو الكود بعد التنفيذ؟

Stackblitz: فتح
Github : مثال على شكل رد فعل من النوع القوي للواجهة

شخص ما لديه أي اقتراح لا تتردد في مشاركته.

إذن ، الإصدار 10 من Angular Now متاح ، هذا إصدار رئيسي ويبدو أن النماذج التفاعلية لن يتم كتابتها بقوة حتى الإصدار 11 على الأقل من Angular. لذلك ، سيتعين علينا الانتظار حتى الخريف على الأقل لتنفيذ هذه الميزة.

لدي سؤال (أو اقتراح؟) فيما يتعلق بالطريقة التي تم بها بناء نموذج النموذج في معظم الاقتراحات / العلاقات العامة التي رأيتها هنا.

عند النظر إلى معظم المكتبات والعلاقات العامة التي تحاول جعل نوع النماذج التفاعلية آمنًا ، يمكنك أن ترى أنها تنشئ نموذجًا يبدو كالتالي:

interface Address {
  name: Name;  
}
interface Name {
  firstName: string;
  lastName: string;
}

ثم يتم "ترجمة" هذا إلى مكان مثل هذا:

const myForm = new FormGroup<Address>({
  name: new FormGroup<Name>({
    firstName: new FormControl('John'),
    lastName: new FormControl('Doe'),
  })
})

لذلك ، بكلمات مبسطة: "إذا كان كائنًا ، فقم ببناء FormGroup له. إذا كان عبارة عن مصفوفة ، فقم ببناء FormArray. وإذا كانت قيمة أولية ، فقم بإنشاء FormControl."

لكن هناك مشكلة واحدة: لا يمكنك استخدام الكائنات في FormControls بعد الآن.

الحلول التي رأيتها حتى الآن: بعض المكتبات ببساطة لا تدعم هذا. وتستخدم بعض المكتبات نوعًا من "الاختراق" لإنشاء تلميح بأنك تريد بالفعل استخدام FormControl بدلاً من FormGroup.

سؤالي / اقتراحي: ما الذي سيتحدث ضد التحديد الصريح لنموذج النموذج على النحو التالي؟

interface Address {
  name: FormGroup<Name>;  
}
interface Name {
  firstName: FormControl<string>;
  lastName: FormControl<string>;
}

const myForm = new FormGroup<Address>({
  name: new FormGroup<Name>({
    firstName: new FormControl('John'),
    lastName: new FormControl('Doe'),
  })
})

هذا له ميزة كبيرة أنه يمكنك الآن وضع الكائنات في FormControls. ولا يتطلب الأمر أي نوع من "الاختراق" للقيام بذلك :)

لقد قمت بإنشاء Codesandbox لهذا الغرض ، حتى تتمكن من تجربته بنفسك: https://codesandbox.io/s/falling-grass-k4u50؟file=/src/app/app.component.ts

MBuchalik ، نعم ، هذا هو أول قرار واضح يتبادر إلى الذهن عندما تبدأ العمل على "نماذج مطبوعة قوية". لقد بدأت أيضًا بهذا ، ولكن له عيبًا كبيرًا - الحاجة إلى إنشاء نموذجين: أحدهما لعناصر تحكم النموذج ، والآخر - لقيم النموذج.

من ناحية أخرى ، وبقدر ما أفهمه ، سيسمح لنا هذا الحل بتنفيذ "نماذج مكتوبة قوية" دون كسر chage ، ولن نضطر إلى انتظار إصدار الإصدار الرئيسي التالي من Angular. من الضروري هنا العمل عمليًا مع مثل هذا الحل لتقييم ما إذا كان يحتوي على أوجه قصور أكثر أهمية من الحاجة إلى إنشاء نموذجين.

MBuchalik لقد شاركت نفس الآراء معك وطرحت نفس السؤال على العلاقات العامة وأجاب أحد المساهمين الزاوي ( KostyaTretyak ).

يمكنك إلقاء نظرة على المناقشة في العلاقات العامة:
https://github.com/angular/angular/pull/37389#discussion_r438543624

TLDR ؛

فيما يلي مشكلتان:
إذا اتبعنا نهجك ، فسنحتاج إلى إنشاء نموذجين مختلفين - لعناصر تحكم النموذج وقيم النموذج.
من الصعب قراءة نموذج ضوابط النموذج.

وقد نفذت هذه الفكرة لمشاريعنا منذ نصف عام والتي تم استخدامها بالفعل في الإنتاج. إن تجربة الإكمال التلقائي الثابتة الكاملة لنوع عناصر التحكم في Angular HTML تعزز حقًا إنتاجية مطورينا المبتدئين. (مع تمكين fullTemplateTypeCheck )

لقد شاركت "لماذا أذهب على هذا النحو" في التعليق السابق في هذا الموضوع:
https://github.com/angular/angular/issues/13721#issuecomment -643214540

عرض Codesanbox: https://codesandbox.io/s/github/gaplo917/angular-typed-form-codesandbox/tree/master/؟fontsize=14&hidenavigation=1&theme=dark

شكرًا على أفكاركمKostyaTretyak و @ gaplo917! 👍

إذا فهمتها بشكل صحيح ، يمكننا تلخيصها على النحو التالي.

إذا أردنا استخدام نموذج واحد فقط ، فيمكن استخدام حل مثل الحل المقدم من KostyaTretyak . لكن الجانب السلبي هو أنه لا يمكننا الآن استخدام الكائنات في FormControls بعد الآن. (أعلم أن هناك "اختراقًا" يسمح بذلك. ولكن نموذجنا ليس "نظيفًا" مرة أخرى ؛ لذلك سنحتاج مرة أخرى إلى نموذجين.)

إذا أردنا أن نكون قادرين على استخدام الكائنات في FormControls ، فمن المحتمل أنه لا توجد طريقة (!) للتغلب على استخدام نهج مثل الأسلوب الذي قمت بتوضيحه (أو @ gaplo917). الجانب السلبي هو أنك تحتاج بشكل أساسي إلى نموذجين. أو على الأقل استخدم بعض الأنواع المساعدة "لاستخراج" نموذج قيمة النموذج.

لذلك ، نحتاج الآن فقط إلى التفكير فيما إذا كانت الكائنات في FormControls يجب أن تكون ممكنة أم لا. هذا من شأنه أن يجيب ببساطة على السؤال المتعلق بأي من الطريقتين هو الأسلوب الذي يجب تحديده. أم هل فاتني شيء؟

شكرًا على أفكاركمKostyaTretyak و @ gaplo917! 👍

إذا فهمتها بشكل صحيح ، يمكننا تلخيصها على النحو التالي.

إذا أردنا استخدام نموذج واحد فقط ، فيمكن استخدام حل مثل الحل المقدم من KostyaTretyak . لكن الجانب السلبي هو أنه لا يمكننا الآن استخدام الكائنات في FormControls بعد الآن. (أعلم أن هناك "اختراقًا" يسمح بذلك. ولكن نموذجنا ليس "نظيفًا" مرة أخرى ؛ لذلك سنحتاج مرة أخرى إلى نموذجين.)

إذا أردنا أن نكون قادرين على استخدام الكائنات في FormControls ، فمن المحتمل أنه لا توجد طريقة (!) للتغلب على استخدام نهج مثل الأسلوب الذي قمت بتوضيحه (أو @ gaplo917). الجانب السلبي هو أنك تحتاج بشكل أساسي إلى نموذجين. أو على الأقل استخدم بعض الأنواع المساعدة "لاستخراج" نموذج قيمة النموذج.

لذلك ، نحتاج الآن فقط إلى التفكير فيما إذا كانت الكائنات في FormControls يجب أن تكون ممكنة أم لا. هذا من شأنه أن يجيب ببساطة على السؤال المتعلق بأي من الطريقتين هو الأسلوب الذي يجب تحديده. أم هل فاتني شيء؟

MBuchalik في رأيي ، إذا كنت تثق في برنامج التحويل البرمجي Typescript وتعتمد بشدة على ميزة "استدلال النوع" ، فلن تحتاج إلى امتلاك نموذجين. يحتوي نظامنا الداخلي على أكثر من 60 نموذجًا ، بعضها معقد جدًا ومتداخل بمستوى عمق 3 FormArray-FormGroup-FormArray ولا نحتاج أيضًا إلى نموذج واضح لنوع القيمة.

لا يوجد سوى نوعين من نماذج البيانات للعب بها وهما:

  • طلب بيانات API / نموذج الاستجابة
  • نموذج FormControl

99.9٪ من الوقت ، نحن

  1. قم بإنشاء تغليف لكل شكل معقد
  2. تحويل البيانات البعيدة -> بيانات النموذج
  3. تحويل بيانات النموذج -> الحمولة البعيدة

مقتطف الشفرة التالي هو الرسم التوضيحي:

interface FooApiData {
   id: string
   age: number
   dob: string | null
   createdAt: string
}

interface FooFormControlType {
  id: TypedFormControl<string>
  age: TypedFormControl<number>

  // calendar view required JS date form control binding
  dob: TypedFormControl<Date | null>
} 

interface FooApiUpdateRequest {
   id: string
   dob: string | null
   age: number
}

class FooForm extends TypedFormGroup<FooFormControlType> {
    constructor(private fb: TypedFormBuilder, private initialValue: FooApiData) {
      super({
          id: fb.control(initialValue.id, Validators.required),
          dob: fb.control(initialValue.dob === null ? new Date(initialValue.dob) : null),
          age: fb.number(initialValue.age, Validators.required)
       })
   }
   toRequestBody(): FooApiUpdateRequest {
       const typedValue = this.value
       return {
          id: typedValue.id,
          dob: typedValue.dob !== null ? moment(typedValue.dob).format('YYYYMMDD') : null,
          age: typedValue.age
       }
   }
}

const apiData = apiService.getFoo()

const form = new FooForm(new TypedFormBuilder(), apiData)

// assume some UI changes the form value
function submit() {
   if(form.dirty && form.valid){
     const payload = form.toRequestBody()
     apiService.updateFoo(payload)
   }
}

ملاحظة: هذه بنية تدفق بيانات مبنية على آرائها يمكننا الاستمتاع ببرمجة الكتابة بأمان في كتابة نصية

إذا أردنا استخدام نموذج واحد فقط ، فيمكن استخدام حل مثل الحل المقدم من KostyaTretyak . لكن الجانب السلبي هو أنه لا يمكننا الآن استخدام الكائنات في FormControls بعد الآن. (أعلم أن هناك "اختراقًا" يسمح بذلك. ولكن نموذجنا ليس "نظيفًا" مرة أخرى ؛ لذلك سنحتاج مرة أخرى إلى نموذجين.)

هنا لا يزال من الضروري تقدير عدد المرات التي نحتاج فيها إلى استخدام كائن مقابل FormControl . أعتقد أنه يمكن تقديرها في مكان ما بنسبة 5-30٪. بمعنى ، إذا استخدمنا حلولًا بنموذج واحد ، فيمكننا تغطية 70-95٪ من حالات استخدام FormControl . بالنسبة للباقي - ما عليك سوى تقديم تلميح عن TypeScript كنوع إضافي (انظر Control<T> ، ليس من الصحيح تسميتها "النموذج الثاني"):

interface FormModel {
  date: Control<Date>;
}

هل يمكن تسمية النوع Control<T> بالاختراق؟ - نعم ، ربما يكون اختراقًا ، لكنه ليس اختراقًا تقريبيًا. لا أعرف بأية حالات لا يعمل فيها هذا النوع بالشكل المنشود أو له آثار جانبية.

أوه ، لقد تذكرت الآثار الجانبية مع Control<T> عندما نحتاج إلى استخدام مكتبة خارجية لنموذج قيمة النموذج. في مثل هذه الحالات ، هناك حاجة حقًا إلى نموذجين:

import { FormBuilder, Control } from '@ng-stack/forms';

// External Form Model
interface ExternalPerson {
  id: number;
  name: string;
  birthDate: Date;
}

const formConfig: ExternalPerson = {
  id: 123,
  name: 'John Smith',
  birthDate: new Date(1977, 6, 30),
};

interface Person extends ExternalPerson {
  birthDate: Control<Date>;
}


const fb = new FormBuilder();
const form = fb.group<Person>(formConfig); // `Control<Date>` type is compatible with `Date` type.

const birthDate: Date = form.value.birthDate; // `Control<Date>` type is compatible with `Date` type.

لكن في هذا الكود ، يكون الحمل هنا فقط:

interface Person extends ExternalPerson {
  birthDate: Control<Date>;
}

بفضل ArielGueta ، أصبحت مشكلة خطيرة في النوع Control<T> معروفة الآن. وهذا يعني أنني لن أحاول حتى تنفيذ Control<T> في طلبات السحب المستقبلية لـ Angular ، كما خططت من قبل.

بفضل ArielGueta ، أصبحت مشكلة خطيرة في النوع Control<T> معروفة الآن. وهذا يعني أنني لن أحاول حتى تنفيذ Control<T> في طلبات السحب المستقبلية لـ Angular ، كما خططت من قبل.

KostyaTretyak هذا ليس صحيحًا. توضح المشكلة الحرجة فقط أن تنفيذ "ControlType" الخاص بك غير صحيح.

لا توجد مشكلة في تنفيذ "نوع التحكم" الكامل.

عرض توضيحي مباشر: https://codesandbox.io/s/lucid-bassi-ceo6t؟file=/src/app/demo/forms/type -test.ts

Screenshot 2020-07-01 at 00 35 11

وهذا يعني أنني لن أحاول حتى تنفيذ Controlفي طلبات السحب المستقبلية لـ Angular ، كما خططت من قبل.

حسنًا ، هذا يعني أن العلاقات العامة الخاصة بك (والعناصر المتتالية) لن تدعم على الأرجح الكائنات في FormControls؟

MBuchalik ، حاليًا (Angular v10) ، إذا كان لدينا نموذج النموذج التالي:

interface FormModel {
  date: Date;
}

وإذا أردنا في المكون الخاص بنا الوصول إلى قيمة الخاصية date ، فنحن بحاجة إلى القيام بما يلي:

get date() {
  return this.formGroup.get('date') as FormControl;
}
// ...
this.date.value as Date;

يوفر طلب السحب الحالي الخاص بي عامًا لقيمة النموذج ، لكنه لا يوفر نوعًا لعنصر التحكم في النموذج:

get date() {
  return this.formGroup.get('date') as FormControl<Date>;
}
// ...
this.date.value; // Here Date type

@ gaplo917 ، MBuchalik ، لقد جربت مسودة العلاقات العامة .

أشك بشدة في أنه في الوقت الحالي يجب اقتراح هذه الحلول ليتم تنفيذها في Angular. وهذا يعني أنه في الوقت الحالي سيتعين علينا استخدام الأدوية الجنيسة لقيم النموذج فقط ، وليس لأنواع التحكم في النموذج.

لكنهم جميعًا لا يعملون بشكل مثالي

لقد أمضيت بضع ساعات فقط في الرسم التوضيحي الخاص بي ، لذلك لم أتوقع أن يكون مثاليًا ؛) هل يمكنك إعطاء أمثلة على أشياء لا تعمل بشكل جيد؟ (خاصة فيما يتعلق بالأشياء التي لا يمكن إصلاحها بسهولة من وجهة نظرك؟)

راجع للشغل ، أحد الاقتراحات المتعلقة بالتوافق العكسي: من وجهة نظري ، من الصعب نسبيًا جعل التطبيق متوافقًا تمامًا مع الإصدارات السابقة. لهذا السبب ، يمكننا القيام بما يلي: نحن لا نغير فئات FormControl و FormGroup و FormArray على الإطلاق. بدلاً من ذلك ، نقوم بإنشاء عناصر جديدة ترثهم (ربما اتصل بهم StrictFormControl<T> و StrictFormGroup<T> أو ما تريد). هؤلاء هم الذين نجعل الكتابة آمنة. الفائدة: نحن على يقين 100٪ من عدم حدوث تغيير جذري. :)

بضع ساعات في الرسم التوضيحي الخاص بي ، لذلك لم أتوقع أن يكون مثاليًا ؛)

لقد عملت مع هذا الحل لبضعة أيام ، وأرى مدى صعوبة العمل مع النماذج.

  1. بادئ ذي بدء ، النفقات العامة الهامة والحاجة إلى وجود نموذجين.
  2. ثانيًا ، هذا الحل ليس أفضل من حيث الموثوقية من الحل الخاص بي بنوع Control<T> ، لأنه بنفس الطريقة من الضروري استخراج قيمة نموذج النموذج بشكل متكرر.
  3. العمل باستخدام عناصر تحكم النموذج المتداخلة. إذا كان لدينا نموذج النموذج التالي:
interface FormModel {
  one: FormGroup<{two: FormControl<string>}>;
}

وإذا حصلنا على formGroup.controls.one.value ، فإن TypeScript تقدم تلميحًا بنوع مشروط ، وليس بنوع {two: string} (كما يجب أن يكون). لذلك قيمة صعبة القراءة من IDE.

وإذا حصلنا على formGroup.controls.one.value ، فإن TypeScript تقدم تلميحًا بالنوع الشرطي ، وليس النوع {two: string} (كما ينبغي). لذلك قيمة صعبة القراءة من IDE.

حسنًا ، لذلك فقط للتأكد من أنني فهمت كل شيء بشكل صحيح. إذا كنت تستخدم تطبيقي وكتب ما يلي:

interface FormModel {
  one: FormGroup<Two>;
}
interface Two {
  two: FormControl<string>;
}

const myForm = new FormGroup<FormModel>({
  one: new FormGroup<Two>({
    two: new FormControl('')
  })
});

(جعلها أكثر طولًا ؛))

إذا بحثت الآن عن myForm.controls.one.value فسيبدو كالتالي:

grafik

إذن أنت تقول ، في هذا المثال ، "اثنان" لا ينبغي أن يكون اختياريًا؟ أعتقد أن هذه ليست الطريقة الصحيحة لكتابة قيمة النموذج. تتضمن قيمة النموذج فقط الحقول غير المعطلة. لذا ، من وجهة نظري ، يجب أن يكون جزئيًا متكررًا. لا يمكنك معرفة وقت الترجمة ما هي الحقول التي سيتم تعطيلها وأيها لن يتم تعطيله.

إذن أنت تقول ، في هذا المثال ، "اثنان" لا ينبغي أن يكون اختياريًا؟

لما؟ رقم.

اختباري للحل الخاص بك:

interface FormModel {
  one: FormGroup<{two: FormControl<string>}>;
}

let formGroup: FormGroup<FormModel>;
const some = formGroup.controls.one.value;

ما أراه على codeandbox بعد تمرير الماوس على value :

(property) FormGroup<FormGroupControls<{ two: FormControl<string>; }>>.value: PartialFormGroupValue<FormGroupControls<{
    two: FormControl<string>;
}>>

هنا يشير PartialFormGroupValue إلى النوع الشرطي PartialFormValue .

آه ، حسنًا ، أعتقد أنني فهمت ذلك. لذا تقصد أن الكتابة يصعب قراءتها ، أليس كذلك؟ اعتقدت في الأصل أنك تتحدث عن خطأ أو عن شيء من هذا القبيل.

حسنًا ، ستظل معظم IDEs تقدم اقتراحات للخصائص المتاحة بمجرد متابعة الكتابة. لذلك لا أرى أي مشاكل كبيرة هنا حقًا. (بالطبع ، سيكون من الأفضل أن تقرأ إذا كانت تقول فقط {two?: string} . لكنني لا أعتقد أن هذا مهم للغاية. هذا على الأقل رأيي.)

إذا قمت بتطبيق Control<T> ، فكيف ستزيله من كتابة قيمة النموذج دون القيام بأي شيء كما فعلت؟ وكيف تجعل قيمة النموذج جزئية متكررة بدون استخدام نوع مساعد؟

إذا قمت بتنفيذ التحكم الخاص بك، كيف يمكنك إزالته بعد ذلك من كتابة قيمة النموذج دون القيام بأي شيء كما فعلت؟ وكيف تجعل قيمة النموذج جزئية متكررة بدون استخدام نوع مساعد؟

في هذه الحالة ، الحل ليس أفضل:

(property) FormGroup<FormGroup<{ two: FormControl<string>; }>>.value: ExtractGroupValue<FormGroup<{
    two: FormControl<string>;
}>>

أعطيت هذا المثال لأنك طلبت ذلك:

هل يمكنك إعطاء أمثلة على أشياء لا تعمل بشكل جيد؟ (خاصة فيما يتعلق بالأشياء التي لا يمكن إصلاحها بسهولة من وجهة نظرك؟)

بالمناسبة ، لقد أصلحت المشكلة الحرجة مع Control<T> .

لحل مشكلات ربط HTML مع Angular 10 و [formControl] ، هذا هو المسار الذي سلكته:

كما هو مذكور في مشكلة أخرى (https://github.com/angular/issues/36405#issuecomment-655110082) ، بالنسبة لنماذجي ، أقوم عمومًا بإنشاء فئات تمتد FormGroup لتسهيل إعادة الاستخدام والاختبار . باستخدام هذا الهيكل ، تمكنت من حل المشكلة في الوقت الحالي عن طريق تحديث الكود الخاص بي من شيء مثل هذا:

class UserFormGroup extends FormGroup {
  constructor() {
    super({
      id: new FormControl(null, Validators.required),
      name: new FormControl(null, Validators.required),
    });
}

الى هذا:

// everything will extend these two
export class EnhancedFormGroup<T extends { [key: string]: AbstractControl }> extends FormGroup
{
  controls!: T;
}
export class EnhancedFormArray<T extends AbstractControl> extends FormArray 
{
  controls!: T[];
}

// reworked form from above
function formDefinition() {
   return {
      id: new FormControl(null, Validators.required),
      name: new FormControl(null, Validators.required),
    };
}

class UserFormGroup extends EnhancedFormGroup<ReturnType<typeof formDefinition>> {
  constructor() {
    super(formDefinition());
}

عند هذه النقطة ، سيُظهر form.controls نوعه بشكل صحيح على أنه { id: FormControl, name: FormControl } ، يتم ربطه بشكل صحيح في HTML ، ويمكن تجميعه بشكل صحيح إذا كان النموذج أكثر تعقيدًا مع مجموعات أو مصفوفات متداخلة.

استخدام الدالة formDefinition ليس جميلًا ، لكنه كان الحل الأنظف الذي يمكنني التوصل إليه لمنع الازدواجية بين تعريف النموذج والمنشئ.

أعتقد أنه يمكنك تحديث FormGroup للحصول على تعريف النوع العام أعلاه دون إدخال تغييرات عاجلة (حسنًا ، قد لا يكون هذا صحيحًا بالنسبة للنماذج التي تضيف / تزيل عناصر التحكم ديناميكيًا على ما أعتقد ؛ لن تظهر في controls نوع

تعديل
يبدو أنه من الأسهل إذا لم تكن بحاجة إلى إنشاء فئات تعمل على توسيع FormGroup ؛ يمكنك إنشاء وظيفة مساعد تحل المشكلة العامة:

function createEnhancedFormGroup<T extends { [key: string]: AbstractControl }>(controls: T) {
  return new EnhancedFormGroup<T>(controls);
}

const form = createEnhancedFormGroup({
  id: new FormControl(null, Validators.required),
  name: new FormControl(null, Validators.required),
});

تحرير 2
... أو يمكنك خبزه في فئة FormGroup نفسها (ربما FormBuilder ؟)

export class EnhancedFormGroup<T extends { [key: string]: AbstractControl }> extends FormGroup
{
  controls!: T;

  static create<T extends { [key: string]: AbstractControl }>(controls: T) {
    return new EnhancedFormGroup<T>(controls);
  }
}

const form = EnhancedFormGroup.create({
  id: new FormControl(null, Validators.required),
  name: new FormControl(null, Validators.required),
});

تحرير 3
لقد وسعت الأمثلة أعلاه لتشمل الكتابة على value s وأنشأت مقالًا لتلخيص كل شيء:

https://medium.com/youngers-consulting/angular-typed-reactive-forms-22842eb8a181

تم وضع علامة على هذا الآن في خارطة الطريق للتطوير المستقبلي: https :

pauldraper هل يمكن أن تشرح ما الذي تغير مقارنة بخريطة الطريق قبل شهرين تقريبًا؟ التغيير الوحيد الذي أراه هو صياغة العنوان. لكنها لا تزال في قسم "المستقبل". تمامًا كما كان قبل شهرين.

MBuchalik ربما كان هناك لمدة شهرين.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات