Skip to main content

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: &#x27;<h2>{{ user.name }}</h2><p>{{ user.email }}</p>&#x27;
})
export class UserCardComponent {
  @Input() user: { name: string; email: string };
}

Example: Input with Alias

@Component({
  selector: 'app-product',
  template: &#x27;<h3>{{ title }}</h3><p>Price: ${{ price }}</p>&#x27;
})
export class ProductComponent {
  @Input(&#x27;productTitle&#x27;) title: string;
  @Input(&#x27;productPrice&#x27;) 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);
}

Example: Overriding Component Styles

my-custom-component {
  --background: #f0f0f0;
  --color: #333;
}
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.

Was this article helpful?