source

지시문 @Input 필요

nicesource 2023. 4. 24. 23:30
반응형

지시문 @Input 필요

AngularJs에서는 필요한 지시 속성을 만들 수 있습니다.@Input을 사용한 Angular에서는 어떻게 해야 합니까?의사들한테는 그런 얘기 안 해

예.

@Component({
  selector: 'my-dir',
  template: '<div></div>'
})
export class MyComponent {
  @Input() a: number; // Make this a required attribute. Throw an exception if it doesn't exist.
  @Input() b: number;
}

공식 솔루션

Ryan Miglavs의 답변대로, Angular의 셀렉터를 스마트하게 사용하면 문제가 해결됩니다.

/** Note: requires the [a] attribute to be passed */
@Component({
  selector: 'my-dir[a]', // <-- use attribute selector along with tag to ensure both tag name and attribute are used to "select" element by Angular in DOM
});
export class MyComponent {
  @Input() a: number;
}

개인적으로 저는 대부분의 경우 이 솔루션을 선호합니다.왜냐하면 코딩하는 동안 추가적인 노력이 필요하지 않기 때문입니다.단, 다음과 같은 단점이 있습니다.

  • 던져진 오류에서 어떤 인수가 빠져 있는지 이해할 수 없다
  • 에러는 그 태그가 Angular에 의해 인식되지 않는 것처럼 그 자체를 혼란스럽게 하고 있다.인수만 누락되어 있다.

두는 모두 에 추가함으로써 될 수 있습니다.@Component위의 데코레이터와 같이 대부분의 에디터는 컴포넌트 이름에 대한 툴팁 정보와 함께 데코레이터를 표시합니다.각이 지다


대체 솔루션의 경우– 아래를 참조해 주십시오.단, 위에서 설명한 단점은 없습니다.


여기에서는, getters/setters에 관한 솔루션을 소개합니다.을 한 수 IMHO가 입니다.OnInit★★★★★★ 。

솔루션 #2

Component({
  selector: 'my-dir',
  template: '<div></div>',
});
export class MyComponent {
  @Input()
  get a() {
    throw new Error('Attribute "a" is required');
  }
  set a(value: number) {
    Object.defineProperty(this, 'a', {
      value,
      writable: true,
      configurable: true,
    });
  }
}

솔루션 3:

데코레이터가 있으면쉽게 할 수 있어요.앱에서 다음과 같은 데코레이터를 정의합니다.

function Required(target: object, propertyKey: string) {
  Object.defineProperty(target, propertyKey, {
    get() {
      throw new Error(`Attribute ${propertyKey} is required`);
    },
    set(value) {
      Object.defineProperty(target, propertyKey, {
        value,
        writable: true,
        configurable: true,
      });
    },
    configurable: true
  });
}

그리고 수업 후반부에는 다음과 같이 자신의 소유물에 마크를 붙이면 됩니다.

Component({
  selector: 'my-dir',
  template: '<div></div>',
});
export class MyComponent {
  @Input() @Required a: number;
}

설명:

아트리뷰트 If 성a되어 있습니다 property - " " 。a자신을 덮어쓰고 속성에 전달된 값이 사용됩니다. 않은 - init - property " - " - " property otherwise otherwise 。a클래스 또는 템플릿에서 오류가 발생합니다.

주의: Getters/setters는 Angular의 컴포넌트/서비스 등에서 잘 동작하며, 이렇게 사용하는 것이 안전합니다.그러나 Angular 이외의 순수 클래스에서 이 방법을 사용할 때는 주의해야 합니다.문제는 typecript가 어떻게 getter/setters를 ES5로 전치하는가 하는 것입니다.getters/setters는 다음과 같이 할당됩니다.prototype 모든 에서 동일한 합니다.이 경우 모든 클래스의 인스턴스에서 동일한 프로토타입 속성을 변환합니다. '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 이런 것을 얻을 수 있습니다.

const instance1 = new ClassStub();
instance1.property = 'some value';
const instance2 = new ClassStub();
console.log(instance2.property); // 'some value'

체크인하다ngOnInit()(생성자가 실행될 때 아직 설정되지 않은 값) 속성에 값이 있는지 여부.

Component({
    selector: 'my-dir',
    template: '<div></div>'
})
export class MyComponent implements OnInit, OnChanges {
    @Input() a:number; // Make this a required attribute. Throw an exception if it doesnt exist
    @Input() b:number;

    constructor(){
    }

    ngOnInit() {
       this.checkRequiredFields(this.a);
    }

    ngOnChanges(changes) {
       this.checkRequiredFields(this.a);
    }

    checkRequiredFields(input) {
       if(input === null) {
          throw new Error("Attribute 'a' is required");
       }
    }
}

체크인 할 수도 있습니다.ngOnChanges(changes) {...}값이 로 설정되어 있지 않은 경우nullhttps://angular.io/docs/ts/latest/api/core/OnChanges-interface.html 도 참조해 주세요.

이를 위한 공식적인 Angular 방법은 컴포넌트의 셀렉터에 필요한 속성을 포함하는 것입니다.예를 들어 다음과 같습니다.

/** Note: requires the [a] attribute to be passed */
Component({
    selector: 'my-dir[a]', // <-- Check it
    template: '<div></div>'
})
export class MyComponent {
    @Input() a:number; // This property is required by virtue of the selector above
    @Input() b:number; // This property is still optional, but could be added to the selector to require it

    constructor(){

    }

    ngOnInit() {
        
    }
}

이 방법의 장점은 개발자가 속성을 포함하지 않는 경우(a템플릿 내의 컴포넌트를 참조할 때 코드가 컴파일되지 않습니다.이는 런타임 안전 대신 컴파일 시 안전성을 의미하며, 이는 매우 좋습니다.

단, 개발자가 수신하는 에러 메시지는 「not a known element(알려진 요소가 아닙니다)」이며,my-dir

이 네거티브는 위와 같이 @Component 데코레이터 위에 장식적인 코멘트를 추가하면 부분적으로 개선될 수 있으며 대부분의 에디터에서는 컴포넌트 이름에 대한 툴팁 정보와 함께 표시됩니다.Angular 오류 출력에는 도움이 되지 않습니다.

ihor에서 언급한 데코레이터의 접근방식을 시도했는데, 이 접근방식은 인스턴스가 아닌 클래스(TS 컴파일 후 프로토타입)에 적용되기 때문에 문제가 발생했습니다.즉, 데코레이터는 컴포넌트의 모든 복사본에 대해 한 번만 실행되거나 적어도 여러 인스턴스에서 작동하도록 할 방법을 찾을 수 없었습니다.

selector 옵션에 대한 문서를 다음에 나타냅니다.실제로는 매우 유연한 CSS 스타일의 셀렉터 입력(스위트워드)이 가능합니다.

Github 기능 요청 스레드에서 이 권장 사항을 찾았습니다.

필수 필드를 선언하는 매우 간단하고 적응적인 방법

많은 답변들이 이미 이 공식 기술을 보여주고 있다.여러 개의 필수 파일을 추가하려면 어떻게 해야 합니까?그런 다음 다음을 수행합니다.

단일 필수 필드

@Component({
  selector: 'my-component[field1]',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})

여러 필드 및 모든 필드가 필요합니다.

@Component({
  selector: 'my-component[field1][field2][field3]',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})

여러 개의 필드가 필요하지만 하나 이상의 필드가 필요합니다.

@Component({
  selector: 'my-component[field1], my-component[field2], my-component[field3]',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})

html에서 사용하는 방법은 다음과 같습니다.

<my-component [field1]="value" [field2]="value" [field3]="value"></my-component>

라이브러리를 사용하여 를 검증하는 것은 어떻습니까?다음 솔루션:

  • 단시간에 실패한다(단순히 실패하는 것은@input컴포넌트에 의해 값이 처음 액세스 됩니다.)
  • 각도 양식에 이미 사용한 규칙을 재사용할 수 있습니다.

사용방법:

    export class MyComponent {

      @Input() propOne: string;
      @Input() propTwo: string;

      ngOnInit() {
        validateProps<MyComponent>(this, {
          propOne: [Validators.required, Validators.pattern('[a-zA-Z ]*')],
          propTwo: [Validators.required, Validators.minLength(5), myCustomRule()]
        })
      }
    }

유틸리티 기능:

    import { FormArray, FormBuilder, ValidatorFn, FormControl } from '@angular/forms';

    export function validateProps<T>(cmp: T, ruleset: {[key in keyof T]?: ValidatorFn[]} ) {
      const toGroup = {};
      Object.keys(ruleset)
        .forEach(key => toGroup[key] = new FormControl(cmp[key], ruleset[key]));
      const formGroup = new FormBuilder().group(toGroup);
      formGroup.updateValueAndValidity();
      const validationResult = {};
      Object.keys(formGroup.controls)
        .filter(key => formGroup.controls[key].errors)
        .forEach(key => validationResult[key] = formGroup.controls[key].errors);
      if (Object.keys(validationResult).length) {
        throw new Error(`Input validation failed:\n ${JSON.stringify(validationResult, null, 2)}`);
      }
    }

스택블리츠

다음과 같이 할 수 있습니다.

constructor() {}
ngOnInit() {
  if (!this.a) throw new Error();
}

@ Required @ihor로 만들 수 .this Object.definePropertythis는 데코레이터가 각 인스턴스에서 속성을 정의하도록 강제합니다.

export function Required(message?: string) {
    return function (target: Object, propertyKey: PropertyKey) {
        Object.defineProperty(target, propertyKey, {
            get() {
                throw new Error(message || `Attribute ${String(propertyKey)} is required`);
            },
            set(value) {
                Object.defineProperty(this, propertyKey, {
                    value,
                    writable: true
                });
            }
        });
    };
}

저는 이렇게 해야 했습니다.

ngOnInit() {
    if(!this.hasOwnProperty('a') throw new Error("Attribute 'a' is required");
}

참고로 @Output 디렉티브가 필요한 경우는, 다음과 같이 해 주세요.

export class MyComponent {
    @Output() myEvent = new EventEmitter(); // This a required event

    ngOnInit() {
      if(this.myEvent.observers.length === 0) throw new Error("Event 'myEvent' is required");
    }
}

다음은 덜 복잡하고 이해하기 쉬운 또 다른 TypeScript 데코레이터 기반 접근법입니다.또한 구성 요소 상속도 지원합니다.


// Map of component name -> list of required properties
let requiredInputs  = new Map<string, string[]>();

/**
 * Mark @Input() as required.
 *
 * Supports inheritance chains for components.
 *
 * Example:
 *
 * import { isRequired, checkRequired } from '../requiredInput';
 *
 *  export class MyComp implements OnInit {
 *
 *    // Chain id paramter we check for from the wallet
 *    @Input()
 *    @isRequired
 *    requiredChainId: number;
 *
 *    ngOnInit(): void {
 *      checkRequired(this);
 *    }
 *  }
 *
 * @param target Object given by the TypeScript decorator
 * @param prop Property name from the TypeScript decorator
 */
export function isRequired(target: any, prop: string) {
  // Maintain a global table which components require which inputs
  const className = target.constructor.name;
  requiredInputs[className] = requiredInputs[className] || [];
  requiredInputs[className].push(prop);
  // console.log(className, prop, requiredInputs[className]);
}

/**
 * Check that all required inputs are filled.
 */
export function checkRequired(component: any) {

  let className = component.constructor.name;
  let nextParent = Object.getPrototypeOf(component);

  // Walk through the parent class chain
  while(className != "Object") {

    for(let prop of (requiredInputs[className] || [])) {
      const val = component[prop];
      if(val === null || val === undefined) {
        console.error(component.constructor.name, prop, "is required, but was not provided, actual value is", val);
      }
    }

    className = nextParent.constructor.name;
    nextParent = Object.getPrototypeOf(nextParent);
    // console.log("Checking", component, className);
  }
}


언급URL : https://stackoverflow.com/questions/35528395/make-directive-input-required

반응형