Angular 2에서 <ng-content>가 비어 있는지 확인하는 방법은 무엇입니까?


92

구성 요소가 있다고 가정합니다.

@Component({
    selector: 'MyContainer',
    template: `
    <div class="container">
        <!-- some html skipped -->
        <ng-content></ng-content>
        <span *ngIf="????">Display this if ng-content is empty!</span>
        <!-- some html skipped -->
    </div>`
})
export class MyContainer {
}

이제이 <ng-content>구성 요소가 비어있는 경우 일부 기본 콘텐츠를 표시하고 싶습니다 . DOM에 직접 액세스하지 않고 쉽게 할 수있는 방법이 있습니까?



참고로, 수락 된 답변이 작동한다는 것을 알고 있지만 "useDefault"유형 입력 매개 변수를 구성 요소에 전달하는 것이 더 나은 스타일이라고 생각합니다. 기본값은 false입니다.
bryan60

답변:


97

ng-contenta와 같은 HTML 요소를 감싸서 div로컬 참조를 가져온 다음 ngIf표현식을 다음과 같이 바인딩합니다 ref.children.length == 0.

template: `<div #ref><ng-content></ng-content></div> 
           <span *ngIf="ref.nativeElement.childNodes.length == 0">
              Display this if ng-content is empty!
           </span>`

23
이것에 대한 대안이 없습니까? 이 추한이기 때문에, 아우렐 리아 대체 슬롯에 비해
우주 비행사

18
를 사용하는 것이 더 안전합니다 ref.children.length. text공백이나 새 줄로 html을 형식화하면 childNodes에 노드 가 포함 되지만 자식은 여전히 ​​비어 있습니다.
의회

5
Angular 이슈 트래커에 더 나은 방법에 대한 기능 요청 이 있습니다 : github.com/angular/angular/issues/12530 (+1을 추가 할 가치가있을 수 있습니다).
eppsilon

5
ref.childNodes.length == 0대신에 예제를 사용한다는 것을 깨달을 때까지 이것이 작동하지 않는 것 같았다고 게시하려고했습니다 ref.children.length == 0. 대답을 일관성있게 편집 할 수 있다면 많은 도움이 될 것입니다. 당신을 때리는 것이 아니라 쉬운 실수. :)
Dustin Cleveland

3
이 예제를 따랐고 잘 작동합니다. 하지만 사용하는 !ref.hasChildNodes()대신에ref.nativeElement.childNodes.length == 0
세르게이 Dzyuba

34

@pixelbits 답변에 누락 된 부분이 있습니다. children부모 템플릿의 줄 바꿈이나 공백으로 인해 children빈 텍스트 \ 줄 바꿈이있는 요소 가 발생하기 때문에 속성 뿐만 아니라 확인해야합니다 . 더 나은이 확인 .innerHTML하고 .trim()그것.

작업 예 :

<span #ref><ng-content></ng-content></span>
<span *ngIf="!ref.innerHTML.trim()">
    Content if empty
</span>

1
:) 나에게 가장 좋은 대답
카밀 Kiełczewski

이 메서드를 사용하여 자식 요소가 비어 있는지 확인하고 있다면 부모를 숨길 수 있습니까? 나는 운이 시도하지
Kavinda Jayakody에게

@KavindaJayakody 예, 이것은 자식 요소가 비어 있는지 확인합니다. 코드를 보여주세요.
lfoma

* ngIf = "! ref.innerHTML.trim ()"이 부분은 성능 문제를 유발합니다. 트림은 O (n)입니다.
RandomCode

1
@RandomCode가 맞으면 함수가 여러 번 호출됩니다. 더 나은 방법은 요청하려는 항목에 따라 element.children.length 또는 을 확인 element.childnodes.length하는 것입니다.
Stefan Rein

31

17.03.2020 수정

순수 CSS (2 가지 솔루션)

ng-content에 아무것도 투영되지 않는 경우 기본 콘텐츠를 제공합니다.

가능한 선택자 :

  1. :only-child선택자. 여기에서이 게시물을 참조하십시오 : : only-child Selector

    이것은 더 적은 코드 / 마크 업을 필요로합니다. IE 9 이후 지원 : Can I Use : only-child

  2. :empty선택자. 더 읽으십시오.

    IE 9 및 부분적으로 IE 7/8 이후 지원 : https://caniuse.com/#feat=css-sel3

HTML

<div class="wrapper">
    <ng-content select="my-component"></ng-content>
</div>
<div class="default">
    This shows something default.
</div>

CSS

.wrapper:not(:empty) + .default {
    display: none;
}

작동하지 않는 경우

공백이 하나 이상 있으면 비어 있지 않은 것으로 간주됩니다. Angular는 공백을 제거하지만 그렇지 않은 경우를 대비합니다.

<div class="wrapper"><!--
    --><ng-content select="my-component"></ng-content><!--
--></div>

또는

<div class="wrapper"><ng-content select="my-component"></ng-content</div>

1
지금까지 이것이 제 사용 사례에 가장 적합한 솔루션이었습니다. 나는 우리가 한 기억되지 않은 emptyCSS를 pseudoclass
julianobrasil

@Stefan 기본 콘텐츠는 어쨌든 렌더링됩니다 ... 숨기기 만 할 것입니다. 따라서 복잡한 기본 콘텐츠의 경우 이것이 최상의 솔루션이 아닙니다. 이게 옳은 거니?
BossOz

1
@BossOz 당신이 맞아요. 렌더링됩니다 (각 구성 요소가있는 경우 생성자 등이 호출 됨). 이 솔루션은 멍청한보기에만 효과적입니다. 로딩이나 그와 같은 것들을 포함하는 복잡한 로직이 있다면, 가장 좋은 방법은 템플릿 / 클래스를 얻을 수있는 구조적 지시문을 작성하는 것입니다. 원하는 구성 요소 또는 기본 구성 요소입니다. 댓글 섹션은 예를 들어 너무 작습니다. 우리 앱 중 하나에있는 또 다른 예에 대해 다시 언급합니다.
Stefan Rein

한 가지 방법은 Directive를 통해 ContentChildren을 선택하는 구성 요소를 갖는 것입니다 (DirectiveClass를 사용하여 쿼리하지만 구조 지시문으로 사용하므로 마크 업을 갖는 것과 관련된 초기 부트 스트랩이 없음). <loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>로딩 구성 요소에서인지 된 성능 로딩을 표시 할 수 있습니다. 데이터가로드 중입니다. 다양한 방법으로 작성 / 해결할 수 있습니다. 이것은 당신이 그것을 어떻게 해결할 수 있는지 하나의 시연입니다.
Stefan Rein

21

콘텐츠를 삽입 할 때 참조 변수를 추가합니다.

<div #content>Some Content</div>

구성 요소 클래스에서 @ContentChild ()를 사용하여 삽입 된 콘텐츠에 대한 참조를 가져옵니다.

@ContentChild('content') content: ElementRef;

구성 요소 템플릿에서 콘텐츠 변수에 값이 있는지 확인할 수 있습니다.

<div>
  <ng-content></ng-content>
  <span *ngIf="!content">
    Display this if ng-content is empty!
  </span>    
</div> 

이것은 가장 깨끗하고 훨씬 더 나은 대답입니다. 그것은 즉시 ContentChild API를 지원합니다. 상위 답변이 왜 그렇게 많은 표를 얻었는지 이해할 수 없습니다.
CarbonDry

10
OP는 ngContent가 비어 있는지 여부를 묻습니다 <MyContainer></MyContainer>. 이것은 의미 합니다. 솔루션은 사용자가 MyContainer : 아래에 하위 요소를 생성 할 것으로 예상합니다 <MyContainer><div #content></div></MyContainer>. 이것이 가능성이기는하지만 이것이 우월하다고 말하고 싶지는 않습니다.
pixelbits 2010 년

8

주사 elementRef: ElementRef하고 elementRef.nativeElement아이가 있는지 확인하십시오 . 이것은 encapsulation: ViewEncapsulation.Native.

<ng-content>태그를 감싸고 자녀가 있는지 확인하십시오. 이것은 encapsulation: ViewEncapsulation.Native.

<div #contentWrapper>
  <ng-content></ng-content>
</div>

자녀가 있는지 확인하십시오.

@ViewChild('contentWrapper') contentWrapper;

ngAfterViewInit() {
  contentWrapper.nativeElement.childNodes...
}

(검증되지 않은)


3
나는 @ViewChild. "Legal"인 동안 ViewChild는 템플릿 내의 자식 노드에 액세스하는 데 사용해서는 안됩니다. 부모가 할 수있는 동안이, 안티 패턴 때문입니다 알고 아이들에 대해, 그들이해야되지 커플 에 그것은 일반적으로 리드로 그들에게 병적 커플 링 ; 보다 적절한 형태의 데이터 전송 또는 요청 처리는 이벤트 기반 방법론 을 사용하는 것 입니다.
Cody

5
@Cody는 귀하의 반대표에 댓글을 게시 해 주셔서 감사합니다. 사실 나는 당신의 주장을 따르지 않습니다. 템플릿과 구성 요소 클래스는 단일 단위 (구성 요소)입니다. 코드에서 템플릿에 액세스하는 것이 반 패턴이어야하는 이유를 모르겠습니다. Angular (2/4)는 웹 프레임 워크와 이름을 제외하고는 AngularJS와 거의 공통점이 없습니다.
Günter Zöchbauer

2
건터, 당신은 정말 좋은 점 두 개를 만들었습니다. Angular는 AngularJS의 방식에서 낙인으로 반드시 부화해서는 안됩니다. 그러나 나는 첫 번째 요점 (그리고 위에서 언급 한 것)이 공학의 철학적 거터에 속한다는 것을 표시하고 싶습니다. 즉, 저는 소프트웨어 커플 링의 전문가가되었고 @ViewChild. 의견에 균형을 추가해 주셔서 감사합니다. 두 의견 모두 다른 의견만큼 고려할 가치가 있습니다.
Cody

2
@Cody에 대한 귀하의 강한 의견은 @ViewChild()이름에서 비롯된 것 같습니다 . "자식"은 반드시 자식 일 필요는 없으며 구성 요소의 선언적 부분 일뿐입니다 (내가 본 것처럼). 이 마크 업에 대해 생성 된 구성 요소 인스턴스가 액세스 인 경우, 이는 물론 다른 이야기라면 적어도 공개 인터페이스에 대한 지식이 필요하고 실제로 긴밀한 결합을 생성하기 때문입니다. 이것은 매우 흥미로운 토론입니다. 그런 프레임 워크를 사용하면 어떤 방법을 찾기 위해 시간이 너무 많이 소모되기 때문에 그런 문제에 대해 생각할 시간이 부족한 것이 걱정입니다.
Günter Zöchbauer

3
API에서이 노출되지는 진짜 안티 패턴 :-(입니다
Simon_Weaver

4

기본 콘텐츠를 표시하려면 CSS의 'only-child'선택기를 사용하지 마십시오.

https://www.w3schools.com/cssref/sel_only-child.asp

예 : HTML

<div>
  <ng-content></ng-content>
  <div class="default-content">I am deafult</div>
</div>

CSS

.default-content:not(:only-child) {
   display: none;
}

ViewChild 및 Component의 항목을 처리하고 싶지 않은 경우 오히려 스마트 솔루션입니다. 나는 그것을 좋아한다!
M'sieur Toph


1

제 경우에는 빈 ng-content의 부모를 숨겨야합니다.

<span class="ml-1 wrapper">
  <ng-content>
  </ng-content>
</span>

간단한 CSS 작동 :

.wrapper {
  display: inline-block;

  &:empty {
    display: none;
  }
}

1

@ContentChildren 을 사용하여 솔루션을 구현했습니다. 데코레이터 의 답변과 유사합니다.

문서 에 따르면 이 데코레이터는 다음과 같습니다.

콘텐츠 DOM에서 요소 또는 지시문의 QueryList를 가져옵니다. 자식 요소가 추가, 제거 또는 이동할 때마다 쿼리 목록이 업데이트되고 쿼리 목록에서 관찰 가능한 변경 사항이 새 값을 생성합니다.

따라서 상위 구성 요소에 필요한 코드는 다음과 같습니다.

<app-my-component>
  <div #myComponentContent>
    This is my component content
  </div>
</app-my-component>

구성 요소 클래스에서 :

@ContentChildren('myComponentContent') content: QueryList<ElementRef>;

그런 다음 구성 요소 템플릿에서 :

<div class="container">
  <ng-content></ng-content>
  <span *ngIf="*ngIf="!content.length""><em>Display this if ng-content is empty!</em></span>
</div>

전체 예 : https://stackblitz.com/edit/angular-jjjdqb

이 솔루션 matSuffix양식 필드 에서 각도 구성 요소에 구현 된 것을 발견했습니다. 구성 요소 .

나중에 컴포넌트의 콘텐츠가 주입되는 상황 에서 앱이 초기화 된 다음 changes이벤트를 구독하여 리 액티브 구현을 사용할 수도 있습니다 QueryList.

export class MyComponentComponent implements AfterContentInit, OnDestroy {
  private _subscription: Subscription;
  public hasContent: boolean;

  @ContentChildren('myComponentContent') content: QueryList<ElementRef>;

  constructor() {}

  ngAfterContentInit(): void {
    this.hasContent = (this.content.length > 0);
    this._subscription = this.content.changes.subscribe(() => {
      // do something when content updates
      //
      this.hasContent = (this.content.length > 0);
    });
  }

  ngOnDestroy() {
    this._subscription.unsubscribe();
  }

}

전체 예 : https://stackblitz.com/edit/angular-essvnq


1

Angular 10에서는 약간 변경되었습니다. 다음을 사용합니다.

<div #ref><ng-content></ng-content></div> 
<span *ngIf="ref.children.length == 0">
  Display this if ng-content is empty!
</span>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.