Home > Blockchain >  How to remove parent element if ng-content is empty
How to remove parent element if ng-content is empty

Time:02-02

I want to hide remove div.parent if ng-content is empty from bellow code

<div >
 <ng-content></ng-content>
</div>

I tried using reference like bellow, but seems to be not working

<div  *ngIf="!child.children.length">
  <div > 
    <ng-content></ng-content>
  </div>
</div>

Is there a better approach to remove parent element if ng-content is empty?

CodePudding user response:

Your name for parent is confusing considering the ng-content holder is normally the child of another component which calls it.

<div >
 <ng-content></ng-content>
</div>

The calling HTML would look something like this: (where your ng-content is in app-footer)

<app-footer>
   <p>MY CONTENT TO THE ng-content THINGY</p>
</app-footer>

You'll want to upgrade your app-footer (ng-content component) to have an input field

import { Component, Input } from '@angular/core'; 

//.......
export class FooterComponent {
  @Input() displayParentDiv = true; 

And in you template

<div  *ngIf="displayParentDiv">
 <ng-content></ng-content>
</div>

And then in your footer (ng-content holder) file:

<p>Pass the input false if you don't want it displayed:</p>
<app-footer [displayParentDiv]="false">
   <p>MY CONTENT TO THE ng-content THINGY</p>
</app-footer>

<p> To display it, don't give it any value considering default is true</p>
<app-footer>
   <p>MY CONTENT TO THE ng-content THINGY</p>
</app-footer>

Example code of a object which I reuse in many of my projects is the use of a material icon navigation item for a toolbar is the code below. Notice that I defaulted all the values, making them not obligated fields, I could for instance only pass an material icon reference and no hint or routerlink

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'ps-nav-item',
  template: `
    <a mat-list-item [routerLink]="routerLink" (click)="navigate.emit()">
      <mat-icon mat-list-icon>{{ icon }}</mat-icon>
      <span mat-line><ng-content></ng-content></span>
      <span mat-line >{{ hint }}</span>
    </a>
  `,
  styles: [
    `
      .secondary {
        color: rgba(0, 0, 0, 0.54);
      }
    `,
  ],
})
export class NavItemComponent {
  @Input() icon = '';
  @Input() hint = '';
  @Input() routerLink: string | any[] = '/';
  @Output() navigate = new EventEmitter();
}

The parents HTML then calls this multiple times:

        <ps-nav-item (navigate)="closeSidenav()" *ngIf="loggedIn$ | async" routerLink="/" icon="book" hint="View your report collection">
          My Collection
        </ps-nav-item>
        <ps-nav-item (navigate)="closeSidenav()" *ngIf="loggedIn$ | async" routerLink="/reports/find" icon="search" hint="Search for a report!">
          Browse Reports
        </ps-nav-item>
        <ps-nav-item (navigate)="closeSidenav()" *ngIf="!(loggedIn$ | async)">
          Sign In
        </ps-nav-item>
        <ps-nav-item (navigate)="logout()" *ngIf="loggedIn$ | async">
          Sign Out
        </ps-nav-item>

ref angular docs

CodePudding user response:

If you have no access to the ng-content nor to the possible input from the people using your components, then you have to validate it with javascript or CSS

CSS method:

.displayNoneWhenEmpty:empty{
  display:none;
}
<div >Not Empty</div>

<div ></div>

reference

Javascript method:

Add an id to the parent div and a boolean check:

<div  id="parentDivId" *ngIf="hideParent">
 <ng-content></ng-content>
</div>

Then in your component add a boolean variable, which you set in the constructor

hideParent: boolean = false;
constructor(){
    hideParent = document.getElementById("parentDivId")?.childElementCount > 0;
}

CodePudding user response:

You can not reference a class like that. I think you want to reference an element, for that you would use # on the element. But that won't help you either. Parent is rendered before child is and if parent is removed from DOM because of child changes you would stumble on an error Expression Changed After It Has Been Checked. For that I have implemented to viewChild of parentRef in cycle ngAfterContentChecked and from that we count if child has any children (if ng-container has any elements). If it does have children we remove parent.

If for some reason you want to remove parent a later time you can call removeParent() to do so.

Edit children = [1,2,3] to observe effects of having children and not having them.

<p>Remove parent if child is empty (look console)</p>
<br>
<div  *ngIf="!hideParent">
  I am parent
  <div  #childRef> 
    <ng-container *ngFor="let child of children">
      <div>I am child nr: # {{child}}<br></div>
    </ng-container>
  </div>
</div>
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterContentChecked {
  name = 'Angular';
  @ViewChild('childRef') child: ElementRef;
  hideParent: boolean = false;
  children = [1, 2, 3];
  parentElem: any;

  ngAfterContentChecked() {
    this.removeParent();
  }

  removeParent() {
    if (this.child) {
      let childrenCount = this.child.nativeElement.childElementCount;
      console.log('child: ', this.child);
      console.log('child elements in child: ', childrenCount);
      if (childrenCount == 0) {
        console.log('hiding');
        this.hideParent = true;
      }
    }
  }
}

Here is a working example: https://stackblitz.com/edit/angular-4krctv?file=src/app/app.component.html

  •  Tags:  
  • Related