각도 투영/주입(옵션)
25763 단어 angular
각도 투영/주입(옵션)
최근에 사용자 정의 내용으로 구성 요소의 일부분을 교체해야 하는 요구가 있습니다. (사용자 정의 표의 제목)어떤 내용도 제공하지 않으면 '기본' 내용을 보여야 합니다.주입/투영의 내용은 간단한 텍스트에서 슬라이더/전환의 모든 내용일 수 있기 때문에 간단한 속성으로는 부족합니다..
이러한 요구 사항은 다음과 같이 요약할 수 있습니다.
ng-content
이나 ng-template
을 사용하고 있다.현명한 결정을 내리기 위해, 나는 이 두 가지 옵션을 실현하기 위해 POC를 만들었는데, 그 중 하나가 다른 것보다 우수한지 보았다.필요에 반하여 만들어진 POC은 여러 개의 내용(예를 들어 눈썹과 꼬리)을 교체하여 해결 방안이 앞으로 확장될 수 있는지 검증할 수 있습니다(필요할 경우).다음 몇 절에서는 내가 생각해 낼 수 있는 대체 방안을 소개할 것이다.내용
이것은 통상적으로 첫 번째 옵션이다. 왜냐하면 그것은 실현하기 쉽고 사용하기 쉽기 때문이다.사용자 정의 컨텐트는
ng-content
을 하위 레벨로 사용합니다.select
속성을 사용하여 여러 컨텐트를 투영할 수도 있습니다.<ng-content select="[slot='header']"></ng-content>
<ng-content select="[slot='footer']"></ng-content>
이것은 첫 번째 요구를 포함한다.두 번째 문제는 ng-content
을 단독으로 사용하면 더욱 어렵다.사용자 정의 내용을 보여줄지 기본 내용을 보여줄지 확인하려면 어떤 내용이 ng-content
으로 전달되었는지 확인하는 방법이 필요합니다.구성 요소나 템플릿에서 정보를 조회하거나 얻을 수 있는 내장 기능을 찾을 수 없습니다. 사용자 정의 해결 방안이 필요합니다.다음 예제에서는 투영할 컨텐트에 배치하는 명령을 작성합니다.
<app-render-slot>
<div appSlot slot="header">Custom Header</div>
<div appSlot slot="footer">Custom Footer</div>
</app-render-slot>
구성 요소는 appSlot
조회를 사용하여 명령을 검색할 수 있습니다.자리 표시자 컨텐트가 발견되면 사용자 정의 컨텐트를 사용하고 그렇지 않으면 기본 컨텐트가 반환됩니다.@Component({
selector: 'app-render-slot',
templateUrl: './component.html',
styleUrls: ['./component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RenderSlotComponent {
@ContentChildren(SlotDirective, { read: ElementRef }) set slotDirectives(
value: QueryList<ElementRef>
) {
this.nativeSlots.next(Array.from(value));
}
private nativeSlots: BehaviorSubject<Array<ElementRef>>;
readonly slotNames$: Observable<SlotNames>;
constructor() {
this.nativeSlots = new BehaviorSubject<Array<ElementRef>>([]);
this.slotNames$ = this.setSlotsByName(this.nativeSlots.asObservable());
}
isSlotSet(slotName: SlotName): Observable<boolean> {
return this.slotNames$.pipe(
map((slotNames) => slotNames.includes(slotName))
);
}
private setSlotsByName(
slots$: Observable<Array<ElementRef>>
): Observable<SlotNames> {
return slots$.pipe(
map((slots) =>
slots.map((slot) => slot.nativeElement.getAttribute('slot'))
)
);
}
}
예를 들어, 슬롯의 이름(머리글 또는 바닥글)은 투영 컨텐트에 대한 사용자 정의 슬롯 속성에 설정된 컨텐트를 기준으로 추출됩니다.찾으려는 @ContentChildren
은 ElementRef
에 표시되어 있고 SlotDirective
에 표시되어 있습니다.구현의 또 다른 부분은 @ContentChildren
의 목록을 슬롯 이름에 비추는 것입니다.ElementRef
메서드의 도움말에서 템플릿은 사용자 정의 컨텐트(슬롯이 있는 경우)를 표시하거나 기본 컨텐트로 되돌릴 수 있습니다.예를 들면, 구성 요소의 템플릿은
isSlotSet
자리 표시자만 포함하여 간단합니다.<ng-content
select="[slot='header']"
*ngIf="isSlotSet('header') | async; else defaultHeader"
></ng-content>
<ng-content
select="[slot='footer']"
*ngIf="isSlotSet('footer') | async; else defaultFooter"
></ng-content>
<ng-template #defaultHeader> Default Header </ng-template>
<ng-template #defaultFooter> Default Footer </ng-template>
여기 설명된 대안은 예시 저장소의 ng-content
폴더에서 찾을 수 있습니다.ng-content/render-slot
의 div
템플릿에서 "사용자 머리글"또는 "사용자 머리글"AppComponent
을 삭제하면 기본 예비(fallback)이 표시됩니다.더 느린 사람
알림: 이 해결 방안은 효과가 없습니다. 관심이 없으면 넘어가십시오.
상술한 방법의 단점은 선택할 수 있는 내용 투영을 가진 모든 구성 요소는 반드시 렌더링 내용을 찾거나 확정하는 메커니즘을 실현해야 한다는 것이다.
app-render-slot
이라는 Assistant 구성 요소를 생성하여 솔루션을 개선하는 것이 제 생각입니다. 이 구성 요소는 구성 요소를 사용하여 전달하는 내용을 보여줍니다.<app-slot-renderer [defaultSlotContent]="defaultHeader"
><ng-content select="[slot='header']"></ng-content
></app-slot-renderer>
<app-slot-renderer [defaultSlotContent]="defaultFooter"
><ng-content select="[slot='footer']"></ng-content
></app-slot-renderer>
<ng-template #defaultHeader> <div>Default Header</div> </ng-template>
<ng-template #defaultFooter> <div>Default Footer</div> </ng-template>
사용자 정의 컨텐트는 SlotRendererComponent
과 ng-content
속성을 사용하여 제공됩니다(select
항목이 하나일 경우 후자를 생략할 수 있습니다).기본 컨텐트는 ng-content
속성을 TemplateRef
전송으로 사용합니다.Input
은 또한 SlotRendererComponent
을 사용하여 어셈블리에서 투영된 컨텐트를 렌더링합니다.<ng-content *ngIf="isSlotSet$ | async; else defaultSlotContent"></ng-content>
따라서 처음에 전달된 사용자 정의 컨텐트는 두 번 투영됩니다.ng-content
)RenderSlotSlotRendererComponent
초<!-- From SlotRendererComponent -->
<ng-content *ngIf="isSlotSet$ | async; else defaultSlotContent">
<!-- From RenderSlotSlotRendererComponent -->
<ng-content select="[slot='header']">
<!-- Projected custom content -->
<div appSlot slot="header">Custom Header</div>
</ng-content>
</ng-content>
<!-- Same for the footer -->
첫 번째 방법과 같은 메커니즘을 통해 사용자 정의 또는 기본 내용은 SlotRendererComponent
에 나타난다.이 해결 방안이 작동하지 않는 이유는
SlotRendererComponent
에서 끼워 넣은 @ContentChildren
s를 조회할 수 없기 때문이다. ng-content
을 설정하는 것도 나에게 작용하지 않는다.나는 issue이 { descendants: true }
저장소의 문제를 묘사했기 때문에 그것이 관련되었을 수도 있다. (또는 내가 여기서 무엇을 잘못했는지.)템플릿
템플릿 속성 사용
AngularDart
을 기반으로 한 해결 방안의 하나는 속성 중의 사용자 정의 내용을 ng-template
s로 직접 전달하는 것이다.<app-template-render-props
[templates]="{ 'header': header, 'footer': footer }"
></app-template-render-props>
<ng-template #header><div>Custom Header</div></ng-template>
<ng-template #footer><div>Custom Footer</div></ng-template>
슬롯당 TemplateRef
은 TemplateRef
을 사용하여 렌더링됩니다.*ngTemplateOutlet
방법과 마찬가지로, 정의된 내용이 없으면 구성 요소는 기본 내용을 되돌려줍니다. (이 예에서 ng-content
조수가 완성했습니다.)<app-render-template
[template]="{ customTemplate: templates.header, defaultTemplate: defaultHeader }"
></app-render-template>
<app-render-template
[template]="{ customTemplate: templates.footer, defaultTemplate: defaultHeader }"
></app-render-template>
<ng-template #defaultHeader> <div>Default Header</div> </ng-template>
<ng-template #defaultFooter> <div>Default Footer</div> </ng-template>
지령을 달다
모든 사용자 정의 내용에 전용
RenderTemplateComponent
패키지를 정의해야 합니다. 이것은 구성 요소의 템플릿을 사용하는 데 불편을 주고 혼란을 초래할 수 있습니다.스토리지 ng-template
과 슬롯 이름에 대한 Fabric 명령을 사용하여 이러한 상황을 방지할 수 있습니다.@Directive({
selector: '[appTemplateSlot]'
})
export class TemplateSlotDirective {
@Input() appTemplateSlot: SlotName | null = null;
constructor(public template: TemplateRef<unknown>) {}
}
이 명령은 슬롯 이름(예제의 머리글 또는 바닥글)을 입력 속성으로 하고 연관된 TemplateRef
을 공통 TemplateRef
속성에 저장합니다(template
의 unknown
유형은 알고 있거나 사용 가능한 경우 연관된 컨텍스트로 대체할 수 있음).렌더링 구성 요소는 현재
TemplateRef
을 사용하여 TemplateSlotDirective
을 쿼리하고 스토리지의 @ContentChildren
을 관련 슬롯에 렌더링할 수 있습니다.@Component({
selector: 'app-render-props-directive',
templateUrl: './component.html',
styleUrls: ['./component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RenderPropsDirectiveComponent {
@ContentChildren(TemplateSlotDirective) set templateSlots(
templateSlots: QueryList<TemplateSlotDirective>
) {
this.templateDirectives.next(
templateSlots.length > 0 ? Array.from(templateSlots) : []
);
}
private templateDirectives: ReplaySubject<Array<TemplateSlotDirective>>;
templates$: Observable<Partial<Templates>>;
constructor() {
this.templateDirectives = new ReplaySubject(1);
this.templates$ = this.setupTemplates(
this.templateDirectives.asObservable()
);
}
private setupTemplates(
templateDirectives$: Observable<Array<TemplateSlotDirective>>
): Observable<Partial<Templates>> {
return templateDirectives$.pipe(
map((templateDirectives) =>
templateDirectives.reduce(
(partialTemplateDirectives, templateDirective) =>
templateDirective.appTemplateSlot
? {
...partialTemplateDirectives,
[templateDirective.appTemplateSlot]:
templateDirective.template
}
: partialTemplateDirectives,
{}
)
),
shareReplay({ bufferSize: 1, refCount: true })
);
}
}
일반적으로 렌더링 구성 요소는 각 슬롯에 대해 사용자 지정 또는 예비(fallback) 컨텐트를 렌더링합니다.<app-render-template
[template]="{ customTemplate: (templates$ | async)?.header, defaultTemplate: defaultHeader }"
></app-render-template>
<app-render-template
[template]="{ customTemplate: (templates$ | async)?.footer, defaultTemplate: defaultHeader }"
></app-render-template>
<ng-template #defaultHeader> <div>Default Header</div> </ng-template>
<ng-template #defaultFooter> <div>Default Footer</div> </ng-template>
다음 그림과 같이 template
패키지를 사용자 정의 콘텐츠에 ng-template
선택기로 교체합니다.<app-render-props-directive>
<div *appTemplateSlot="'header'">Custom Header</div>
<div *appTemplateSlot="'footer'">Custom Footer</div>
</app-render-props-directive>
결론
TemplateSlotDirective
과 ng-content
을 사용하면 사용자 정의 내용을 표시하거나 기본 내용을 표시하는 요구를 충족시킬 수 있습니다.ng-template
기반 솔루션을 선호합니다.ng-template
과 같은 용이성을 제공한다.ng-content
쿼리 중첩 ng-content
의 문제로 인해 ng-content
의 해결 방안을 바탕으로 이 점을 실현할 수 없다.Reference
이 문제에 관하여(각도 투영/주입(옵션)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/remshams/optional-content-projection-injection-in-angular-2jnn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)