programing

지시문에서 호스트 구성 요소에 액세스하는 방법은 무엇입니까?

subpage 2023. 9. 21. 20:22
반응형

지시문에서 호스트 구성 요소에 액세스하는 방법은 무엇입니까?

마크업이 다음과 같습니다.

<my-comp myDirective></my-comp>

지시문에서 컴포넌트 인스턴스에 접근할 수 있는 방법이 있습니까?

으로 의 할 수 .MyComponentMyDirective, 이상적으로 위의 HTML에 아무것도 추가하지 않습니다.

그냥 주사를 놓으시면 됩니다.

class MyDirective {
  constructor(private host:MyComponent) {}

심각한 한계는 구성 요소의 종류를 미리 알아야 한다는 것입니다.

https://github.com/angular/angular/issues/8277 도 참조
종류를 미리 모를 때를 대비한 몇 가지 방법도 제공합니다.

사용자 지정 구성 요소에서 속성 지시어를 사용하려면 해당 구성 요소를 추상 클래스에서 추상 클래스 유형을 구성 요소 유형으로 'forwardRef' 확장할 수 있습니다.이렇게 하면 (지침 내에서) 추상 클래스에서 angular의 DI를 선택할 수 있습니다.

추상 클래스:

export abstract class MyReference { 
  // can be empty if you only want to use it as a reference for DI
}

사용자 지정 구성요소:

@Component({
  // ...
  providers: [
    {provide: MyReference, useExisting: forwardRef(() => MyCustomComponent)}
  ],
})
export class MyCustomComponent extends MyReference implements OnInit {
// ...
}

지시:

@Directive({
  selector: '[appMyDirective]'
})
export class CustomDirective{

  constructor(private host:MyReference) {
    console.log(this.host);
    // no accessing private properties of viewContainerRef to see here... :-)
  }

}

이렇게 하면 추상 클래스를 확장하는 모든 구성 요소에서 지시문을 사용할 수 있습니다.

물론 이는 사용자 자신의 구성요소에서만 작동합니다.

지시사항은 구성요소에 적용할 수 있는 일반적인 지시사항이 될 수 있습니다.그래서, 그 경우, 컨스트럭터에 부품을 주입하는 것은 불가능할 것이고, 그래서 여기에 그와 같은 방법을 위한 다른 한 가지 방법이 있습니다.

합니다.ViewContainerRefn로

constructor(private _viewContainerRef: ViewContainerRef) { }

그 다음에 그걸 사용하도록 해요.

let hostComponent = this._viewContainerRef["_data"].componentView.component;

이것은 깃허브 이슈에서 가져온 것으로 매력처럼 작용합니다.단점은 구성 요소를 사전에 알아야 하지만, 사용 중인 방법을 알아야 합니다.

import { Host, Self, Optional } from '@angular/core';
  
export class ExampleDirective {  
  constructor(
    @Host() @Self() @Optional() public hostCheckboxComponent : MdlCheckboxComponent,
    @Host() @Self() @Optional() public hostSliderComponent   : MdlSliderComponent
  ) {
    if(this.hostCheckboxComponent) {
      console.log("host is a checkbox");
    } else if(this.hostSliderComponent) {
      console.log("host is a slider");
    }
}

크레딧: https://github.com/angular/angular/issues/8277#issuecomment-323678013

지시 제네릭을 만드는 방법 중 하나는 구성 요소의 템플릿 참조를 지시에 @Input으로 전달하는 것입니다.이것은 html을 조금 더 추가하지만 제가 시도한 다른 많은 해킹들보다 더 잘 작동했습니다.

@Directive({selector: '[myDirective]'})
export class MyDirective implements OnInit {
  @Input() componentRef: any;
  @Input() propName: string;

  ngOnInit(){
    if (this.componentRef != null) {
      // Access component properties
      this.componentRef[this.propName];
    }
 }
}

보기의 사용량:

<!-- Pass component ref and the property name from component as inputs -->
<app-component #appComponentRef myDirective [componentRef]="appComponentRef" [propName]="'somePropInComponent'" .... >

또한 다른 지시사항들과 함께 작동합니다.exportAs를 의 속성으로 사용@Directive지시 인스턴스에 대한 참조를 가져오는 데코레이터.

<form #myForm="ngForm" myDirective [componentRef]="myForm" [propName]="'ngSubmit'" ....>

ViewContainerRef를 사용하여 호스트 구성 요소에 액세스할 수 있습니다.

constructor(private el: ViewContainerRef) {}

ngOnInit() {
    const _component = this.el && this.el.injector && this.el.injector.get(MyComponent);
}

참조 : https://angular.io/api/core/ViewContainerRef

Angular 12의 경우, 이 코멘트는 더러운 해결책에 대한 올바른 방향을 나에게 가리켰습니다.이것이 원칙적으로 이 문제를 해결하는 좋은 방법이 아니라는 것은 잘 알고 있지만, 여러 모듈에 걸쳐 문제가 분리되어 있기 때문에 제 사용 사례에서는 쓰기 시간에 무엇인지 알지 못한 채 구성 요소 인스턴스에 액세스할 수 있어야 했습니다.

TL;DR:

class MyDirective {
  constructor(private vcRef: ViewContainerRef) {}

  private getHostComponent(): any {
    return this.vcRef._lContainer[0][8];
  }
}

에 할 수 .ViewContainerRef_lContainer컨테이너와 연관된 상태를 나타내는 property.이것.LContainer인덱스 0(내부 상수)의 엔트리를 갖습니다.LView 컨테이너가 구성 요소 노드에 있는 경우.

LView, 다시, 는 위치 8(내부 상수)에 항목이 있습니다. 이 항목은 연결된 구성 요소가 비루트 구성 요소(예: non-root component element)인 경우 구성 요소 인스턴스에 대한 참조입니다.<app-* 컨텍스트 구성 수 있습니다.lContainer[HOST][CONTEXT].

설명과 함께 복사 붙여넣기에 대한 긴 답변:

class MyDirective {
  constructor(private vcRef: ViewContainerRef) {}

  private getHostElementFromViewContainerRef(): unknown | null {
    // TL;DR of the below method:
    // return this.vcRef._lContainer[0][8];
    // Inspired by https://stackoverflow.com/questions/46014761/how-to-access-host-component-from-directive#comment119646192_48563965

    const vcRef = this.vcRef as any; // We're accessing private properties so we cast to any to avoid awkward TS validation issues

    // We fetch the component associated with the element this directive is attached to by navigating via the ViewContainerRef.
    // The VCRef contains a reference to the LContainer, which represents the state associated with the container:
    // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/container.ts#L65
    const lContainer = vcRef._lContainer;

    if (!lContainer) {
      return null;
    }

    // LView has all its elements defined as array elements, with keys hardcoded to numeric constants:
    // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/view.ts#L26-L57
    // We care about two of them:
    const HOST = 0; // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/view.ts#L29
    const CONTEXT = 8; // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/view.ts#L37

    // LContainer is an array, with the element at the HOST position being an LView if the container is on a Component Node.
    // This means that this may not work if this directive is declared on a native HTML element.
    // Note that LContainer uses the same indexes as LView, so it's the same HOST constant as declared in the LView interfaces file.
    // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/container.ts#L66-L72

    const lView = lContainer[HOST];
    if (!lView) {
      return null;
    }

    // For a non-root component, the context is the component instance.
    // So if this directive is correctly attached to an Angular Component (e.g. `<app-*`),
    // this array entry will contain the instance of that component.
    // https://github.com/angular/angular/blob/12.2.x/packages/core/src/render3/interfaces/view.ts#L173-L180
    const contextElement = lView[CONTEXT];

    return contextElement || null;
  }
}

저는 이게 좋아요, Angular 9 & 11에서 작동해요.

element는 element .DOM, 현재 객체인재 Component의다를 동적으로 할 수 .

  1. 지정 에 입니다.__componentnativeElement드,

  2. nativeElement.__component인에Directve;

    내보내기 클래스FromItemComponentBase{contractor(개인 호스트Element:ElementRef){hostElement.nativeElement.__component=this; }


@Component({
  selector: 'input-error',
  templateUrl: 'component.html'
})
export class FromItemErrorComponent extends FromItemComponentBase {
  constructor(private hostElement: ElementRef) {
    super(hostElement);
  }
}

@Component({
  selector: 'input-password',
  templateUrl: 'component.html'
})
export class FromItemPasswordComponent extends FromItemComponentBase {
  constructor(private hostElement: ElementRef) {
    super(hostElement);
  }
}

@Directive({selector: 'input-error,input-password,input-text'})
export class FormInputDirective {
  component:FromItemComponentBase;

  constructor(private hostElement: ElementRef) {
    this.component=hostElement.nativeElement.__component;
  }
}
constructor(private vcRef: ViewContainerRef){
        let parentComponent=(<any>this.vcRef)._view.context;
}

viewContainerRef: ViewContainerRef in Constructor 및 다음 코드(this.viewContainerRef as any)가 있습니다._hostLview[8]이(가) 완벽하게 작동합니다.그러나 아래에 설명된 대로 구성 요소 참조를 Input 매개 변수로 지시에 전달하는 것이 좋습니다.

<my-comp myDirective [hostComponent]="hostComponent"></my-comp>

내 CompComponent.ts에서

hostComponent = this;

내 지시사항.ts에서,

@Input() hostComponent: Component;

참고: 이것은 해킹성이 있으며 Angular의 향후 버전에서는 작동하지 않을 가능성이 높습니다.Angular 10에서는 다음과 같이 호스트 컴포넌트에 접근할 수 있었습니다.

하게 @Sunil Garg 의ViewContainerRef지침에 따라 다음과 같이 합니다.

constructor(_viewContainerRef: ViewContainerRef)

다음과 같이 호스트 구성 요소를 가져옵니다.

let hostComponent = (<any>_viewContainerRef)._lContainer[0][8];

여기서 두 가지 해결책을 시도했습니다.
Michiel Windey )abstract class될 구성 에 대한 및로서 )및 Anthony것용)@Host() @Self() @Optional()).
둘 다 Angular 11과 함께 일합니다.
두 모두 이 없으며, 되지 않은 입니다를 사용하는 에 대비할 수

두 번째는 기존 구성요소를 변경하지 않고 접근할 수 있는 장점이 있습니다.하지만 실제로 투입되는 부품에 따라 처리해야 할 검사와 특수 케이스가 많이 필요할 것 같습니다.

첫 번째는 구성요소를 수정해야 하는 불편함이 있지만 인터페이스(추상 클래스)에서 지시어에 사용할 모든 필드와 메서드를 정의할 수 있으므로 어떤 구성요소가 주입되는지 확인할 필요 없이 하나의 매개변수로 액세스할 수 있습니다.

따라서 사용 사례에 따라 선택이 달라집니다.

언급URL : https://stackoverflow.com/questions/46014761/how-to-access-host-component-from-directive

반응형