Angular’s flexibility with templates makes it possible to pass HTML and other content into components as inputs, making your components more reusable and adaptable. Using Angular templates as component inputs can improve your app’s structure by allowing you to define customizable, dynamic content. This article will guide you through the process of using templates as inputs, covering the advantages, syntax, and best practices for creating flexible Angular components.
What Does it Mean to Use Templates as Component Inputs?
In Angular, component inputs are typically data or configurations passed into a component through properties marked with the @Input()
decorator. However, Angular allows us to take it a step further by passing entire templates as inputs, which the component can then render internally. This technique is especially useful for creating customizable components like lists, cards, or dialogs where the inner content varies based on the context.
Why Use Templates as Inputs?
- Customizable Content: Allows users of the component to provide custom templates for specific sections of a component, improving flexibility and reuse.
- Code Reusability: Templates as inputs prevent the need to create multiple component variations for different content structures.
- Maintainability: Template inputs keep content organized, as the template logic is defined in the component’s structure, reducing the need for complex conditional rendering.
Setting Up Template Inputs in Angular Components
To pass a template as an input, you’ll use Angular’s TemplateRef
and ngTemplateOutlet
. Here’s how:
- Define an input property in your component that expects a
TemplateRef
type. - Use
ngTemplateOutlet
to render the template wherever required within the component.
Example: Creating a Customizable List Component
Let’s start by building a customizable list component that accepts a template for rendering each item.
Step 1: Define the List Component
typescriptCopy codeimport { Component, Input, TemplateRef } from '@angular/core';
@Component({
selector: 'app-custom-list',
template: `
<ul>
<ng-container *ngFor="let item of items">
<ng-container
*ngTemplateOutlet="itemTemplate; context: { $implicit: item }">
</ng-container>
</ng-container>
</ul>
`,
})
export class CustomListComponent {
@Input() items: any[] = [];
@Input() itemTemplate!: TemplateRef<any>;
}
In this example:
items
is an array of data passed into the component.itemTemplate
is an input property that receives a template, which is then rendered usingngTemplateOutlet
.- The context object
{ $implicit: item }
makes eachitem
accessible to the template.
Step 2: Using the Customizable Component in Another Component
Now, let’s see how to use the CustomListComponent
by providing a specific template for rendering each item.
htmlCopy code<app-custom-list [items]="productList" [itemTemplate]="productTemplate"></app-custom-list>
<ng-template #productTemplate let-product>
<li>
<h3>{{ product.name }}</h3>
<p>Price: {{ product.price | currency }}</p>
</li>
</ng-template>
Here:
productList
is an array defined in the parent component with product data.productTemplate
is a template defined withng-template
that theCustomListComponent
will use to render each item.
Step 3: Setting Up the Parent Component’s TypeScript Code
In the parent component, define the data structure and initialize productList
:
typescriptCopy codeimport { Component } from '@angular/core';
@Component({
selector: 'app-product-display',
templateUrl: './product-display.component.html'
})
export class ProductDisplayComponent {
productList = [
{ name: 'Laptop', price: 1200 },
{ name: 'Smartphone', price: 800 },
{ name: 'Tablet', price: 400 }
];
}
Advanced Use Cases for Templates as Component Inputs
Once you understand the basics, you can apply this technique to more complex scenarios, such as rendering slots, creating dialog boxes with custom content, or conditionally rendering templates based on user actions.
Example 2: Customizable Dialog Component
Let’s create a dialog component where the title and body content can be customized by passing in templates.
Step 1: Define the Dialog Component
typescriptCopy codeimport { Component, Input, TemplateRef } from '@angular/core';
@Component({
selector: 'app-dialog',
template: `
<div class="dialog-backdrop">
<div class="dialog-box">
<div class="dialog-header">
<ng-container *ngTemplateOutlet="titleTemplate"></ng-container>
</div>
<div class="dialog-body">
<ng-container *ngTemplateOutlet="bodyTemplate"></ng-container>
</div>
</div>
</div>
`,
styleUrls: ['./dialog.component.css']
})
export class DialogComponent {
@Input() titleTemplate!: TemplateRef<any>;
@Input() bodyTemplate!: TemplateRef<any>;
}
In this component:
titleTemplate
andbodyTemplate
are both inputs that accept templates.- These templates are rendered using
ngTemplateOutlet
, allowing the parent component to provide custom title and body content.
Step 2: Using the Dialog Component with Custom Templates
In the parent component, you can now provide specific templates for the dialog’s title and body.
htmlCopy code<app-dialog [titleTemplate]="dialogTitle" [bodyTemplate]="dialogBody"></app-dialog>
<ng-template #dialogTitle>
<h2>Custom Dialog Title</h2>
</ng-template>
<ng-template #dialogBody>
<p>This is the body content of the dialog, which can be customized as needed.</p>
</ng-template>
This approach allows you to have a single dialog component with customizable content, rather than creating separate dialog components for each variation.
Example 3: Optional Template Inputs
Sometimes you might want to make a template input optional. For example, if the user does not provide a template for a specific section, the component should render a default template instead.
Step 1: Define the Component with a Default Template Fallback
In this example, the dialogComponent
will render a default template if no custom body template is provided.
typescriptCopy codeimport { Component, Input, TemplateRef, ContentChild } from '@angular/core';
@Component({
selector: 'app-optional-dialog',
template: `
<div class="dialog">
<div class="header">
<ng-container *ngTemplateOutlet="headerTemplate || defaultHeader"></ng-container>
</div>
<div class="body">
<ng-container *ngTemplateOutlet="bodyTemplate || defaultBody"></ng-container>
</div>
</div>
<!-- Default Templates -->
<ng-template #defaultHeader><h2>Default Header</h2></ng-template>
<ng-template #defaultBody><p>This is default body content.</p></ng-template>
`,
})
export class OptionalDialogComponent {
@Input() headerTemplate?: TemplateRef<any>;
@Input() bodyTemplate?: TemplateRef<any>;
}
- Here, if
headerTemplate
orbodyTemplate
is not provided, the component uses the fallback templatesdefaultHeader
anddefaultBody
.
Best Practices for Using Templates as Component Inputs
- Use Context Object for Dynamic Data: When passing data to templates, leverage the context object to keep the template adaptable to various data inputs.
- Keep Logic in the Component: Avoid adding complex logic directly in templates. Instead, keep the logic in the component and pass only the necessary data to the template.
- Default Templates for Flexibility: Consider providing default templates as fallbacks for optional inputs, ensuring your component is always functional.
- Minimize Nested Templates: Overusing nested templates can make your code hard to read. Use them only when needed to avoid cluttering your HTML structure.
- Comment and Document: For maintainability, comment on how template inputs are expected to be used, especially if the component is part of a shared library.
Conclusion
Using Angular templates as component inputs is a powerful technique that enables you to create highly customizable, reusable components. With TemplateRef
and ngTemplateOutlet
, you can build flexible components that render custom templates for different scenarios, streamlining your code and enhancing reusability. Whether you’re creating a dynamic list, a customizable dialog, or a reusable form, template inputs in Angular are a valuable tool in your development arsenal. By following best practices and experimenting with different configurations, you can create a more adaptable and maintainable Angular application.