Creating Custom Components
1. Creating Component Class
| Property | Description | Example |
|---|---|---|
| selector | Defines the HTML tag used to instantiate the component | my-component |
| templateUrl | Path to the component's HTML template file | './my-component.html' |
| styleUrls | Array of paths to component's CSS files | ['./my-component.css'] |
| providers | Services to be injected into the component | [MyService] |
Example: Component Class Definition
import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-component',\n templateUrl: './my-component.html',\n styleUrls: ['./my-component.css']\n})\nclass MyComponent {\n title = 'My Custom Component';\n}\n
2. Using Component Inputs
| Property | Type | Description | Default |
|---|---|---|---|
| @Input() | any |
Marks a class property as an input binding | undefined |
| @Input('alias') | any |
Allows custom alias for input property | undefined |
Example: Basic Input Usage
@Component({
selector: 'app-user-card',
template: '<h2>{{ user.name }}</h2><p>{{ user.email }}</p>'
})
export class UserCardComponent {
@Input() user: { name: string; email: string };
}
Example: Input with Alias
@Component({
selector: 'app-product',
template: '<h3>{{ title }}</h3><p>Price: ${{ price }}</p>'
})
export class ProductComponent {
@Input('productTitle') title: string;
@Input('productPrice') price: number;
}
Note: Inputs are one-way data bindings from parent to child component.
3. Emitting Component Events
| Event | Description | Usage |
|---|---|---|
| @Output() | Decorator to emit custom events from a component | @Output() customEvent = new EventEmitter<any>(); |
| EventEmitter | Class to emit events with optional data | new EventEmitter<string>() |
Example: Emitting an event with data
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-button',
template: '<button (click)="onButtonClick()">Click me</button>'
})
export class ButtonComponent {
@Output() buttonClicked = new EventEmitter<string>();
onButtonClick() {
this.buttonClicked.emit('Button was clicked!');
}
}
Example: Emitting an event without data
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-toggle',
template: '<ion-toggle (ionChange)="onToggleChange()"></ion-toggle>'
})
export class ToggleComponent {
@Output() toggleChanged = new EventEmitter<void>();
onToggleChange() {
this.toggleChanged.emit();
}
}
4. Styling Custom Components
| Property | Values | Description |
|---|---|---|
| --custom-property | value |
Custom CSS variable for component styling |
| color | var(--ion-color-primary) |
Applies Ionic color palette |
| background-color | #ffffff |
Background color of the component |
Example: Using CSS Variables in Custom Components
.my-custom-component {
--custom-color: var(--ion-color-primary);
color: var(--custom-color);
}
Note: When using
ion-content, make sure to apply styles inside the .ion-content class to ensure proper rendering.
Warning: Avoid using
!important in component styles as it can interfere with Ionic's built-in styling.
5. Using ViewChild Reference
| Property | Description | Usage |
|---|---|---|
| ViewChild | Decorator to get a reference to a child component or directive | @ViewChild('myTemplateRef') myTemplateRef: TemplateRef<any>; |
| ViewChildren | Decorator to get a list of references to child components or directives | @ViewChildren('myDirective') myDirectives: QueryList<MyDirective>; |
Example: ViewChild in Ionic Component
import { Component, ViewChild, TemplateRef } from '@angular/core';
@Component({
selector: 'app-example',
template: `
<ng-template #myTemplate>
<p>This is a template reference</p>
</ng-template>
<button (click)="useTemplate()">Use Template</button>
`
})
export class ExampleComponent {
@ViewChild('myTemplate') myTemplate: TemplateRef<any>;
useTemplate() {
console.log('Template reference accessed:', this.myTemplate);
}
}
Note: ViewChild is used to access child components or DOM elements directly in Angular. It's particularly useful when you need to interact with a child component's properties or methods.
6. Implementing Content Projection
Content projection in Ionic allows you to pass content from a parent component into a child component, enabling flexible and reusable UI elements.
| Property | Description | Usage |
|---|---|---|
| ng-content | Used to project content into a component's template | <ng-content></ng-content> |
Example: Basic Content Projection
<ion-card>
<ion-card-header>
<ion-card-title>Card Title</ion-card-title>
</ion-card-header>
<ion-card-content>
<ng-content></ng-content>
</ion-card-content>
</ion-card>
Example: Named Content Projection
<ion-item>
<ion-icon slot="start" name="star"></ion-icon>
<ion-label>
<ng-content select="[slot=header]"></ng-content>
</ion-label>
<ion-note slot="end">
<ng-content select="[slot=note]"></ng-content>
</ion-note>
</ion-item>
Note: Content projection is handled by Angular's
ng-content directive and works similarly to standard Angular content projection. Ensure that projected content is valid HTML or Ionic components.
7. Creating Standalone Components
Standalone components in Ionic are self-contained units that can be used without being part of a module. They simplify component creation and usage by removing the need for module declarations.
| Feature | Description | Usage |
|---|---|---|
| standalone | Boolean flag indicating if the component is standalone | standalone: true |
| imports | Array of components, directives, or pipes to be imported | imports: [IonButton] |
| providers | Array of providers for dependency injection | providers: [MyService] |
Example: Creating a Standalone Component
import { Component, Input } from '@angular/core';
import { IonButton } from '@ionic/angular';
@Component({
selector: 'app-custom-button',
standalone: true,
imports: [IonButton],
template: '<ion-button (click)="onClick()">{{ label }}</ion-button>'
})
export class CustomButtonComponent {
@Input() label = 'Click Me';
onClick() {
console.log('Button clicked!');
}
}
Note: Standalone components must be imported in the component that uses them, or in a route configuration if used in routing.
8. Using Component Inheritance
In Ionic, component inheritance allows you to create a base component with shared functionality that can be extended by other components. This pattern is useful for reducing code duplication and promoting reusability.
| Feature | Description | Usage |
|---|---|---|
| Base Component | Defines common properties, methods, and lifecycle hooks | Extend using extends in component class declaration |
| Inheritance Chain | Supports multiple levels of inheritance (e.g., A extends B, B extends C) | Each level can override or extend parent behavior |
| Method Overriding | Child components can override parent methods | Use super.methodName() to call parent implementation |
Example: Base Component
import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-base-component',\n template: '<div>{{ message }}</div>'\n})\nclass BaseComponent {\n message = 'Hello from base component';\n\n logMessage() {\n console.log(this.message);\n }\n}
Example: Child Component Extending Base
import { Component } from '@angular/core';\nimport { BaseComponent } from './base.component';\n\n@Component({\n selector: 'app-child-component',\n template: '<div>{{ childMessage }}</div>'\n})\nclass ChildComponent extends BaseComponent {\n childMessage = 'Hello from child component';\n\n constructor() {\n super();\n this.message = 'Overridden message';\n }\n\n logChildMessage() {\n console.log(this.childMessage);\n }\n}
Note: When using inheritance in Angular/Ionic, ensure that the base component does not rely on specific DOM structure or styles that may conflict with child components.
Warning: Deep inheritance chains can make code harder to maintain and debug. Prefer composition over deep inheritance where possible.
9. Implementing Component Templates
Example: Basic Component Template
// src/components/my-button/my-button.tsx
import { Component, Prop } from '@stencil/core';
@Component({
tag: 'my-button',
styleUrl: 'my-button.css'
})
export class MyButton {
@Prop() text: string = 'Click me';
@Prop() disabled: boolean = false;
render() {
return (
<button disabled={this.disabled}>
{this.text}
</button>
);
}
}
| Property | Type | Description |
|---|---|---|
| tag | string |
HTML tag name for the component |
| styleUrl | string |
CSS file path for component styles |
| shadow | boolean |
Enables Shadow DOM (default: false) |
Note: The
render() method must return a single JSX element or null.
Example: Template with Conditional Rendering
// src/components/status-indicator/status-indicator.tsx
import { Component, Prop } from '@stencil/core';
@Component({
tag: 'status-indicator',
styleUrl: 'status-indicator.css'
})
export class StatusIndicator {
@Prop() status: 'online' | 'offline' | 'loading' = 'loading';
render() {
let icon;
switch (this.status) {
case 'online':
icon = '✓';
break;
case 'offline':
icon = '✗';
break;
case 'loading':
icon = '⏳';
break;
}
return (
<span class={`status ${this.status}`}>
{icon}
</span>
);
}
}
10. Registering Components
| Component | Type | Registration Method | Notes |
|---|---|---|---|
| MyCustomComponent | Angular |
@Component({ selector: 'app-my-custom' }) |
Used in templates with <app-my-custom> |
| MyWebComponent | Web Components |
customElements.define('my-custom', MyWebComponent) |
Registered with browser's Custom Elements API |
| IonicComponent | Ionic |
IonicModule.forRoot() |
Registered via module imports |
Example: Registering an Angular Component
import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<h2>Hello from My Component</h2>'\n})\nexport class MyComponent { }\n
Example: Registering a Web Component
class MyWebComponent extends HTMLElement {\n constructor() {\n super();\n this.innerHTML = '<h2>Hello from Web Component</h2>';\n }\n}\ncustomElements.define('my-web-component', MyWebComponent);\n
Note: When registering Ionic components, ensure they are imported in the correct module (e.g.,
IonicModule) to make them available in templates.