Support us .Net Basics C# SQL ASP.NET ADO.NET MVC Slides C# Programs Subscribe Buy DVD

Angular content projection

Suggested Videos
Part 59 - Edit form in angular | Text | Slides
Part 60 - Angular delete form | Text | Slides
Part 61 - Angular accordion example | Text | Slides

In this video we will discuss content projection in Angular with an example. This is continuation to Part 61. Please watch Part 61 from Angular CRUD tutorial before proceeding.


Content projection helps us create reusable components. In Angular 1, this is called transclusion. Let us understand content projection, with an example. We want to create a reusable accordion type of control. The panel heading must be clickable, and when clicked the panel body and footer must be collapsed. Clicking on the panel heading again, must expand the collapsed panel body and footer. 


Another important requirement is, this accordion panel must be reusable with any other component in our application. The component that uses this accordion panel component, must be able to specify what content it wants in the accordion panel body and footer.

angular 2 ng-content select multiple

For example, if we use this accordion panel, with a ProductComponent that displays a product, then in the accordion panel body, the ProductComponent may want to project and display product image, price, weight etc. In the footer, the ProductComponent may want to project and display buttons to customise the product or buy. 

In our case we want to use this accordion panel, with DisplayEmployeeComponent. So in the panel body we want to project and display, employee photo, gender, date of birth, email etc. In the footer, we want to project and display buttons to View, Edit and Delete employee as shown below.

angular content projection

So the important question that we need to answer is, how will the components that use this accordion component be able to inject variable content into the acoordion panel body and footer. 
By using <ng-content> tag

As you can see in the image below, you can think of this <ng-content> as a place holder for the variable content. In a bit we will understand, how a component that uses this accordion component can project variable content depending on the requirements of your application. 

angular 2 transclusion example

First, let's create our reusable accordion component. This is a reusable component and can be used by another component in our application. So, let's place this component in the "Shared" folder. Use the following Angular CLI command to create the component.
ng g c shared/accordion --flat

accordion.component.ts : Notice, in the component class we have introduced 3 properties. The code is commented and self-explanatory.

export class AccordionComponent implements OnInit {
  // We use this property to set a different CSS class on the employee
  // panel if we have just viewed his details
  @Input() hasJustViewed: boolean;
  // Sets the panel title, in our case the name of the employee
  @Input() title: string;
  // Controls hiding and showing panel body and footer
  @Input() isHidden = false;

  constructor() { }

  ngOnInit() {
  }
}

accordion.component.css : The "pointerCursor" class makes the cursor a pointer when hovered over panel title, so the end user knows, it is clickable.

.pointerCursor {
    cursor: pointer;
}

accordion.component.html : As you can see, we have defined the shell for the accordion panel i.e accordion panel header, body and footer. We also have encapsulated the logic in this component to show and hide the panel body and footer. But the content that goes in the panel body and footer will be decided by the component that consumes this accordion component. The consuming component will also need to bind and pass data for the 3 input properties (title, isHidden and hasJustViewed).

<!-- Add panel-success class only if hasJustViewed property is true -->
<div class="panel panel-primary" [class.panel-success]="hasJustViewed">
    <!-- pointerCursor class changes the cursor style to pointer when hovered
             over the employee panel title. When clicked on the title, isHidden
             boolean property is toggled from true to false & vice-versa. We use
             this property to toggle the visibility of panel body & footer -->
    <div class="panel-heading pointerCursor" (click)="isHidden = !isHidden">
        <h3 class="panel-title">{{title | uppercase}}</h3>
    </div>
    <div class="panel-body" [hidden]="isHidden">
        <!-- ng-content specifies the slot into which the content will be projected
               by the component that consumes this accordion component -->
        <ng-content select=".myPanelBody"></ng-content>
    </div>
    <div class="panel-footer" [hidden]="isHidden">
        <!-- Another slot into which the content can be projected. Since we have more
               than one slot into which the content can be projected, this is called
               multi-slot content projection-->
        <ng-content select=".myPanelFooter"></ng-content>
    </div>
</div>

Changes in display-employee.component.html : The changes are commented and self-explanatory.

<!-- Pass employee name as the value for title input property. Also set
     isHidden input propety to false if you want the panel body and footer
     to be collapsed on the initial page load.-->
<app-accordion [title]="employee.name" [isHidden]="true"
[hasJustViewed]="selectedEmployeeId === employee.id">
<!-- Notice myPanelBody css class is present on this <div>. This CCS class is
used as the selector on the <ng-content> tag in accordion component. So all this
content in this DIV will be projected at the location where we have <ng-content>
tag with css class selector .myPanelBody -->
<div class="col-xs-10 myPanelBody">
  <div class="row vertical-align">
    <div class="col-xs-4">
      <img class="imageClass" [src]="employee.photoPath" />
    </div>
    <div class="col-xs-8">

      <div class="row">
        <div class="col-xs-6">
          Gender
        </div>
        <div class="col-xs-6">
          : {{employee.gender}}
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Date of Birth
        </div>
        <div class="col-xs-6">
          : {{employee.dateOfBirth | date}}
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Contact Preference
        </div>
        <div class="col-xs-6">
          : {{employee.contactPreference}}
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Phone
        </div>
        <div class="col-xs-6">
          : {{employee.phoneNumber}}
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Email
        </div>
        <div class="col-xs-6">
          : {{employee.email}}
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Department
        </div>
        <div class="col-xs-6" [ngSwitch]="employee.department">
          :
          <span *ngSwitchCase="1">Help Desk</span>
          <span *ngSwitchCase="2">HR</span>
          <span *ngSwitchCase="3">IT</span>
          <span *ngSwitchCase="4">Payroll</span>
          <span *ngSwitchDefault>N/A</span>
        </div>
      </div>
      <div class="row">
        <div class="col-xs-6">
          Is Active
        </div>
        <div class="col-xs-6">
          : {{employee.isActive}}
        </div>
      </div>

    </div>
  </div>
</div>
<!-- The content in the following DIV will be projected at the location
where we have <ng-content> tag with css selector .myPanelFooter -->
<div class="myPanelFooter">
  <button class="btn btn-primary" (click)="viewEmployee()">View</button>
  <button class="btn btn-primary" (click)="editEmployee()">Edit</button>
  <span *ngIf="confirmDelete">
    <span>Are you sure you want to delete ?</span>
    <button class="btn btn-danger" (click)="deleteEmployee()">Yes</button>
    <button class="btn btn-primary" (click)="confirmDelete=false">No</button>
  </span>
  <span *ngIf="!confirmDelete">
    <button class="btn btn-danger" (click)="confirmDelete=true">Delete</button>
  </span>
</div>
</app-accordion>

At the moment we are using class selector to match the projection content with the ng-content slot. We can use any of the CSS selectors (class selector, element selector, attribute selector etc)

angular crud tutorial

No comments:

Post a Comment

If you like this website, please share with your friends on facebook and Google+ and recommend us on google using the g+1 button on the top right hand corner.