Prerequisites
- Basic understanding of JavaScript ๐
- TypeScript installation โก
- VS Code or preferred IDE ๐ป
- Angular framework basics ๐ ฐ๏ธ
What you'll learn
- Understand Angular pipes fundamentals ๐ฏ
- Apply pipes in real Angular projects ๐๏ธ
- Debug common pipe issues ๐
- Write type-safe custom pipes โจ
๐ฏ Introduction
Welcome to the exciting world of Angular Pipes! ๐ In this guide, weโll explore how to transform data elegantly in your Angular applications using TypeScript.
Youโll discover how Angular pipes can transform your development experience. Whether youโre building web applications ๐, dashboards ๐, or user interfaces ๐จ, understanding pipes is essential for clean, maintainable data presentation.
By the end of this tutorial, youโll feel confident creating and using pipes in your own Angular projects! Letโs dive in! ๐โโ๏ธ
๐ Understanding Angular Pipes
๐ค What are Angular Pipes?
Angular pipes are like magical data transformers ๐จ! Think of them as filters in a coffee machine โ - they take raw data (coffee beans) and transform it into something beautiful and usable (perfect coffee).
In TypeScript terms, pipes are functions that take input data and transform it for display ๐. This means you can:
- โจ Format dates and currencies without cluttering components
- ๐ Transform text case and structure instantly
- ๐ก๏ธ Keep your templates clean and readable
๐ก Why Use Angular Pipes?
Hereโs why developers love Angular pipes:
- Separation of Concerns ๐: Keep data transformation separate from business logic
- Reusability ๐ป: Use the same pipe across multiple components
- Performance ๐: Built-in memoization for efficiency
- Clean Templates ๐ง: Readable and maintainable template code
Real-world example: Imagine building a shopping cart ๐. With pipes, you can format prices, dates, and product names consistently across your entire application without repeating code!
๐ง Basic Syntax and Usage
๐ Simple Pipe Example
Letโs start with Angularโs built-in pipes:
// ๐ Component with pipe examples
import { Component } from '@angular/core';
@Component({
selector: 'app-product-display',
template: `
<div class="product-card">
<!-- ๐ฐ Currency formatting -->
<p>Price: {{ price | currency:'USD':'symbol':'1.2-2' }}</p>
<!-- ๐
Date formatting -->
<p>Added: {{ dateAdded | date:'short' }}</p>
<!-- ๐ค Text transformation -->
<h3>{{ productName | titlecase }}</h3>
<!-- ๐ข Number formatting -->
<p>Rating: {{ rating | number:'1.1-1' }}/5.0</p>
</div>
`
})
export class ProductDisplayComponent {
price: number = 29.99; // ๐ต Raw price
dateAdded: Date = new Date(); // ๐ Current date
productName: string = "AWESOME WIDGET"; // ๐ Product name
rating: number = 4.567; // โญ Rating value
}
๐ก Explanation: Notice how pipes use the |
symbol! Each pipe transforms the data before displaying it, making your templates super clean.
๐ฏ Common Built-in Pipes
Here are the pipes youโll use most often:
// ๐๏ธ Template with multiple pipe examples
@Component({
template: `
<!-- ๐ฐ Currency pipes -->
<p>USD: {{ 1234.56 | currency:'USD' }}</p>
<p>EUR: {{ 1234.56 | currency:'EUR':'symbol':'1.0-0' }}</p>
<!-- ๐
Date pipes -->
<p>Short: {{ today | date:'short' }}</p>
<p>Custom: {{ today | date:'EEEE, MMMM d, y' }}</p>
<!-- ๐ค Text pipes -->
<p>Upper: {{ 'hello world' | uppercase }}</p>
<p>Lower: {{ 'HELLO WORLD' | lowercase }}</p>
<p>Title: {{ 'hello world' | titlecase }}</p>
<!-- ๐ข Number pipes -->
<p>Decimal: {{ 1234.5678 | number:'1.2-3' }}</p>
<p>Percent: {{ 0.1234 | percent:'1.2-2' }}</p>
<!-- ๐ Array pipes -->
<p>JSON: {{ user | json }}</p>
<p>Slice: {{ fruits | slice:1:3 | json }}</p>
`
})
export class PipeExamplesComponent {
today: Date = new Date();
user = { name: 'Alice ๐ฉโ๐ป', role: 'Developer' };
fruits = ['๐', '๐', '๐', '๐', '๐ฅ'];
}
๐ก Practical Examples
๐ Example 1: E-commerce Product Display
Letโs build a real product listing with type-safe pipes:
// ๐๏ธ Product interface
interface Product {
id: string;
name: string;
price: number;
rating: number;
dateAdded: Date;
category: string;
description: string;
emoji: string;
}
// ๐ช Product component
@Component({
selector: 'app-product-card',
template: `
<div class="product-card">
<!-- ๐จ Product header -->
<h3>{{ product.emoji }} {{ product.name | titlecase }}</h3>
<!-- ๐ฐ Price display -->
<div class="price-section">
<span class="price">{{ product.price | currency:'USD':'symbol':'1.2-2' }}</span>
<span class="category">({{ product.category | uppercase }})</span>
</div>
<!-- โญ Rating display -->
<div class="rating">
<span>{{ product.rating | number:'1.1-1' }}</span>
<span class="stars">{{ getStars(product.rating) }}</span>
</div>
<!-- ๐ Description -->
<p class="description">
{{ product.description | slice:0:100 }}{{ product.description.length > 100 ? '...' : '' }}
</p>
<!-- ๐
Date added -->
<small class="date-added">
Added {{ product.dateAdded | date:'MMM d, y' }}
</small>
</div>
`,
styles: [`
.product-card {
border: 2px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
margin: 8px;
transition: transform 0.2s;
}
.product-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.price {
font-size: 1.2em;
font-weight: bold;
color: #2e7d32;
}
.category {
font-size: 0.8em;
color: #666;
}
.rating {
margin: 8px 0;
}
.stars {
margin-left: 8px;
}
`]
})
export class ProductCardComponent {
@Input() product!: Product;
// โญ Generate star display
getStars(rating: number): string {
const fullStars = Math.floor(rating);
const halfStar = rating % 1 >= 0.5 ? 1 : 0;
const emptyStars = 5 - fullStars - halfStar;
return 'โญ'.repeat(fullStars) +
'๐'.repeat(halfStar) +
'โ'.repeat(emptyStars);
}
}
// ๐ฎ Usage example
@Component({
template: `
<div class="product-grid">
<app-product-card
*ngFor="let product of products"
[product]="product">
</app-product-card>
</div>
`
})
export class ProductListComponent {
products: Product[] = [
{
id: '1',
name: 'TypeScript Course',
price: 99.99,
rating: 4.8,
dateAdded: new Date('2024-01-15'),
category: 'education',
description: 'Learn TypeScript from beginner to expert with hands-on projects and real-world examples.',
emoji: '๐'
},
{
id: '2',
name: 'Wireless Headphones',
price: 199.99,
rating: 4.2,
dateAdded: new Date('2024-02-10'),
category: 'electronics',
description: 'Premium wireless headphones with noise cancellation and 30-hour battery life.',
emoji: '๐ง'
}
];
}
๐ฏ Try it yourself: Add a discount pipe that shows original and sale prices!
๐ฎ Example 2: User Dashboard with Pipes
Letโs create a dashboard with multiple data transformations:
// ๐ค User data interface
interface UserStats {
username: string;
email: string;
joinDate: Date;
lastLogin: Date;
totalPosts: number;
reputation: number;
badges: string[];
profileCompletion: number;
}
// ๐ Dashboard component
@Component({
selector: 'app-user-dashboard',
template: `
<div class="dashboard">
<!-- ๐ Welcome section -->
<div class="welcome-section">
<h2>Welcome back, {{ userStats.username | titlecase }}! ๐</h2>
<p>{{ userStats.email | lowercase }}</p>
</div>
<!-- ๐ Stats grid -->
<div class="stats-grid">
<div class="stat-card">
<h3>๐
Member Since</h3>
<p>{{ userStats.joinDate | date:'MMMM yyyy' }}</p>
<small>{{ getDaysSince(userStats.joinDate) }} days ago</small>
</div>
<div class="stat-card">
<h3>๐ Last Active</h3>
<p>{{ userStats.lastLogin | date:'short' }}</p>
<small>{{ getTimeAgo(userStats.lastLogin) }}</small>
</div>
<div class="stat-card">
<h3>๐ Total Posts</h3>
<p>{{ userStats.totalPosts | number:'1.0-0' }}</p>
<small>Awesome contribution! ๐</small>
</div>
<div class="stat-card">
<h3>โญ Reputation</h3>
<p>{{ userStats.reputation | number:'1.0-0' }}</p>
<small>{{ getReputationLevel(userStats.reputation) }}</small>
</div>
</div>
<!-- ๐ Badges section -->
<div class="badges-section">
<h3>๐ Your Badges</h3>
<div class="badges">
<span
*ngFor="let badge of userStats.badges | slice:0:5"
class="badge">
{{ badge }}
</span>
<span
*ngIf="userStats.badges.length > 5"
class="badge-more">
+{{ userStats.badges.length - 5 }} more
</span>
</div>
</div>
<!-- ๐ Profile completion -->
<div class="progress-section">
<h3>๐ Profile Completion</h3>
<div class="progress-bar">
<div
class="progress-fill"
[style.width.%]="userStats.profileCompletion * 100">
</div>
</div>
<p>{{ userStats.profileCompletion | percent:'1.0-0' }} complete</p>
</div>
</div>
`,
styles: [`
.dashboard {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin: 20px 0;
}
.stat-card {
background: #f5f5f5;
padding: 16px;
border-radius: 8px;
text-align: center;
}
.badges {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.badge {
background: #e3f2fd;
padding: 4px 8px;
border-radius: 16px;
font-size: 0.9em;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4caf50, #8bc34a);
transition: width 0.3s ease;
}
`]
})
export class UserDashboardComponent {
userStats: UserStats = {
username: 'ALEX_DEVELOPER',
email: '[email protected]',
joinDate: new Date('2023-03-15'),
lastLogin: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2 hours ago
totalPosts: 1247,
reputation: 8934,
badges: ['๐ Expert', '๐ Fast Learner', '๐ Premium', '๐ฏ Accurate', '๐ Champion', 'โก Quick', '๐ฅ Hot Contributor'],
profileCompletion: 0.85
};
// ๐
Calculate days since joining
getDaysSince(date: Date): number {
const diffTime = Date.now() - date.getTime();
return Math.floor(diffTime / (1000 * 60 * 60 * 24));
}
// ๐ Get human-readable time ago
getTimeAgo(date: Date): string {
const diffHours = Math.floor((Date.now() - date.getTime()) / (1000 * 60 * 60));
if (diffHours < 1) return 'Just now';
if (diffHours === 1) return '1 hour ago';
if (diffHours < 24) return `${diffHours} hours ago`;
return `${Math.floor(diffHours / 24)} days ago`;
}
// ๐ Get reputation level
getReputationLevel(rep: number): string {
if (rep >= 10000) return '๐ Expert Level';
if (rep >= 5000) return '๐ Advanced';
if (rep >= 1000) return '๐ช Intermediate';
return '๐ฑ Beginner';
}
}
๐ Advanced Concepts
๐งโโ๏ธ Creating Custom Pipes
When built-in pipes arenโt enough, create your own magical transformers:
// ๐ฏ Custom pipe for file size formatting
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize',
pure: true // ๐ Performance optimization
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number, decimals: number = 2): string {
if (bytes === 0) return '0 Bytes ๐';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes ๐', 'KB ๐', 'MB ๐พ', 'GB ๐ฟ', 'TB ๐๏ธ'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
// ๐จ Custom pipe for text highlighting
@Pipe({
name: 'highlight',
pure: false // ๐ Allows dynamic updates
})
export class HighlightPipe implements PipeTransform {
transform(text: string, searchTerm: string): string {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm})`, 'gi');
return text.replace(regex, '<mark class="highlight">$1</mark>');
}
}
// ๐ Advanced pipe with multiple parameters
@Pipe({
name: 'truncateWords',
pure: true
})
export class TruncateWordsPipe implements PipeTransform {
transform(
text: string,
wordLimit: number = 10,
suffix: string = '...'
): string {
if (!text) return '';
const words = text.split(' ');
if (words.length <= wordLimit) return text;
return words.slice(0, wordLimit).join(' ') + suffix;
}
}
// ๐ Using custom pipes in a component
@Component({
selector: 'app-file-manager',
template: `
<div class="file-list">
<h2>๐ File Manager</h2>
<!-- ๐ Search input -->
<input
[(ngModel)]="searchTerm"
placeholder="Search files... ๐"
class="search-input">
<div class="files">
<div
*ngFor="let file of files"
class="file-item">
<!-- ๐ File name with highlighting -->
<h3 [innerHTML]="file.name | highlight:searchTerm"></h3>
<!-- ๐พ File size -->
<p>Size: {{ file.size | fileSize:1 }}</p>
<!-- ๐ Description with word limit -->
<p>{{ file.description | truncateWords:15:'... ๐' }}</p>
<!-- ๐
Last modified -->
<small>Modified: {{ file.lastModified | date:'medium' }}</small>
</div>
</div>
</div>
`,
styles: [`
.file-list {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.search-input {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
}
.file-item {
background: #f9f9f9;
padding: 16px;
margin: 8px 0;
border-radius: 8px;
border-left: 4px solid #2196f3;
}
::ng-deep .highlight {
background-color: yellow;
font-weight: bold;
}
`]
})
export class FileManagerComponent {
searchTerm: string = '';
files = [
{
name: 'TypeScript Advanced Guide.pdf',
size: 2048576, // 2MB
description: 'Comprehensive guide to advanced TypeScript features including generics, decorators, and advanced type manipulation techniques.',
lastModified: new Date('2024-01-20')
},
{
name: 'Angular Project Setup.md',
size: 8192, // 8KB
description: 'Step-by-step instructions for setting up a new Angular project with TypeScript configuration.',
lastModified: new Date('2024-02-15')
}
];
}
๐๏ธ Async Pipes and Observables
For the reactive programming enthusiasts:
// ๐ Working with async data
import { Observable, interval, map, take } from 'rxjs';
@Component({
selector: 'app-live-data',
template: `
<div class="live-dashboard">
<h2>๐ Live Data Dashboard</h2>
<!-- โฐ Live timer -->
<div class="timer-section">
<h3>๐ Current Time</h3>
<p>{{ currentTime$ | async | date:'medium' }}</p>
</div>
<!-- ๐ Live counter -->
<div class="counter-section">
<h3>๐ข Live Counter</h3>
<p>{{ counter$ | async | number:'1.0-0' }}</p>
</div>
<!-- ๐ก๏ธ Temperature simulation -->
<div class="temp-section">
<h3>๐ก๏ธ Temperature</h3>
<p>{{ temperature$ | async | number:'1.1-1' }}ยฐC</p>
</div>
<!-- ๐ฐ Stock price simulation -->
<div class="stock-section">
<h3>๐ Stock Price</h3>
<p>{{ stockPrice$ | async | currency:'USD':'symbol':'1.2-2' }}</p>
</div>
</div>
`,
styles: [`
.live-dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
padding: 20px;
}
.timer-section, .counter-section, .temp-section, .stock-section {
background: #f0f8ff;
padding: 16px;
border-radius: 8px;
text-align: center;
border: 2px solid #e3f2fd;
}
h3 {
margin: 0 0 10px 0;
color: #1976d2;
}
p {
font-size: 1.2em;
font-weight: bold;
margin: 0;
}
`]
})
export class LiveDataComponent {
// โฐ Current time observable
currentTime$: Observable<Date> = interval(1000).pipe(
map(() => new Date())
);
// ๐ข Counter observable
counter$: Observable<number> = interval(500).pipe(
map(count => count + 1)
);
// ๐ก๏ธ Temperature simulation
temperature$: Observable<number> = interval(2000).pipe(
map(() => 18 + Math.random() * 15) // 18-33ยฐC range
);
// ๐ Stock price simulation
stockPrice$: Observable<number> = interval(1000).pipe(
map(() => 100 + (Math.random() - 0.5) * 20) // $90-$110 range
);
}
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Performance Issues with Impure Pipes
// โ Wrong way - impure pipe called on every change detection!
@Pipe({
name: 'expensiveFilter',
pure: false // ๐ฅ This will hurt performance!
})
export class ExpensiveFilterPipe implements PipeTransform {
transform(items: any[], filterValue: string): any[] {
console.log('๐ Pipe called again!'); // This logs constantly!
return items.filter(item =>
item.name.toLowerCase().includes(filterValue.toLowerCase())
);
}
}
// โ
Correct way - keep pipes pure when possible!
@Pipe({
name: 'efficientFilter',
pure: true // ๐ Only runs when inputs change
})
export class EfficientFilterPipe implements PipeTransform {
transform(items: any[], filterValue: string): any[] {
if (!filterValue) return items;
return items.filter(item =>
item.name.toLowerCase().includes(filterValue.toLowerCase())
);
}
}
// ๐ฏ Or better yet - handle filtering in component
@Component({
template: `
<input [(ngModel)]="filterText" placeholder="Filter...">
<div *ngFor="let item of filteredItems">
{{ item.name }}
</div>
`
})
export class SmartFilterComponent {
items = [{ name: 'Apple ๐' }, { name: 'Banana ๐' }];
filterText = '';
get filteredItems() {
if (!this.filterText) return this.items;
return this.items.filter(item =>
item.name.toLowerCase().includes(this.filterText.toLowerCase())
);
}
}
๐คฏ Pitfall 2: Incorrect Pipe Parameters
// โ Dangerous - wrong parameter types!
@Component({
template: `
<!-- ๐ฅ This will cause errors! -->
<p>{{ "not-a-number" | currency }}</p>
<p>{{ "invalid-date" | date }}</p>
<p>{{ null | slice:0:5 }}</p>
`
})
export class BadPipeUsageComponent {}
// โ
Safe - validate data first!
@Component({
template: `
<!-- ๐ก๏ธ Safe with validation -->
<p>{{ isValidPrice(price) ? (price | currency:'USD') : 'Invalid price' }}</p>
<p>{{ isValidDate(date) ? (date | date:'short') : 'Invalid date' }}</p>
<p>{{ items?.length ? (items | slice:0:5) : 'No items' }}</p>
`
})
export class SafePipeUsageComponent {
price: any = "not-a-number";
date: any = "invalid-date";
items: string[] | null = null;
isValidPrice(value: any): boolean {
return typeof value === 'number' && !isNaN(value);
}
isValidDate(value: any): boolean {
return value instanceof Date && !isNaN(value.getTime());
}
}
๐ ๏ธ Best Practices
- ๐ฏ Keep Pipes Pure: Use
pure: true
whenever possible for better performance - ๐ Type Your Pipes: Always define proper TypeScript types for inputs and outputs
- ๐ก๏ธ Validate Inputs: Check for null/undefined values before transformation
- ๐จ Name Clearly: Use descriptive pipe names that explain their purpose
- โจ Single Responsibility: Each pipe should do one thing well
// ๐ Example of a well-designed pipe
@Pipe({
name: 'safeUrl',
pure: true
})
export class SafeUrlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(url: string | null | undefined): SafeUrl | string {
if (!url || typeof url !== 'string') {
return 'javascript:void(0)'; // ๐ก๏ธ Safe fallback
}
try {
return this.sanitizer.bypassSecurityTrustUrl(url);
} catch (error) {
console.warn('โ ๏ธ Invalid URL provided to SafeUrlPipe:', url);
return 'javascript:void(0)';
}
}
}
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Social Media Post Formatter
Create a pipe system for formatting social media posts:
๐ Requirements:
- โ Format mentions (@username) with highlighting
- ๐ท๏ธ Format hashtags (#hashtag) with different styling
- ๐ Format timestamps (relative time like โ2 hours agoโ)
- ๐ Format URLs as clickable links
- โ๏ธ Truncate long posts with โread moreโ functionality
- ๐ Format engagement numbers (likes, shares, comments)
๐ Bonus Points:
- Add emoji support
- Implement post filtering by hashtags
- Create a search highlight pipe
- Add accessibility features
๐ก Solution
๐ Click to see solution
// ๐ฏ Social media post pipes!
// ๐ Mention highlighting pipe
@Pipe({ name: 'mentions', pure: true })
export class MentionsPipe implements PipeTransform {
transform(text: string): string {
return text.replace(
/@(\w+)/g,
'<span class="mention">@$1</span>'
);
}
}
// ๐ท๏ธ Hashtag formatting pipe
@Pipe({ name: 'hashtags', pure: true })
export class HashtagsPipe implements PipeTransform {
transform(text: string): string {
return text.replace(
/#(\w+)/g,
'<span class="hashtag">#$1</span>'
);
}
}
// ๐ Relative time pipe
@Pipe({ name: 'timeAgo', pure: true })
export class TimeAgoPipe implements PipeTransform {
transform(date: Date): string {
const now = new Date().getTime();
const postTime = date.getTime();
const diffMs = now - postTime;
const diffMins = Math.floor(diffMs / (1000 * 60));
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffMins < 1) return 'just now โก';
if (diffMins < 60) return `${diffMins}m ago ๐`;
if (diffHours < 24) return `${diffHours}h ago ๐`;
return `${diffDays}d ago ๐
`;
}
}
// ๐ Engagement numbers pipe
@Pipe({ name: 'engagementCount', pure: true })
export class EngagementCountPipe implements PipeTransform {
transform(count: number): string {
if (count < 1000) return count.toString();
if (count < 1000000) return (count / 1000).toFixed(1) + 'K';
return (count / 1000000).toFixed(1) + 'M';
}
}
// ๐ฑ Social media post component
interface SocialPost {
id: string;
author: string;
content: string;
timestamp: Date;
likes: number;
shares: number;
comments: number;
emoji: string;
}
@Component({
selector: 'app-social-post',
template: `
<div class="post-card">
<!-- ๐ค Author info -->
<div class="author-info">
<span class="author">{{ post.emoji }} {{ post.author }}</span>
<span class="timestamp">{{ post.timestamp | timeAgo }}</span>
</div>
<!-- ๐ Post content with formatting -->
<div
class="post-content"
[innerHTML]="post.content | mentions | hashtags">
</div>
<!-- ๐ Engagement stats -->
<div class="engagement">
<button class="engage-btn">
โค๏ธ {{ post.likes | engagementCount }}
</button>
<button class="engage-btn">
๐ {{ post.shares | engagementCount }}
</button>
<button class="engage-btn">
๐ฌ {{ post.comments | engagementCount }}
</button>
</div>
</div>
`,
styles: [`
.post-card {
border: 1px solid #e1e8ed;
border-radius: 12px;
padding: 16px;
margin: 8px 0;
background: white;
}
.author-info {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
}
.author {
font-weight: bold;
color: #1da1f2;
}
.timestamp {
color: #657786;
font-size: 0.9em;
}
.post-content {
margin: 12px 0;
line-height: 1.4;
}
.engagement {
display: flex;
gap: 16px;
margin-top: 12px;
}
.engage-btn {
background: none;
border: none;
color: #657786;
cursor: pointer;
padding: 4px 8px;
border-radius: 16px;
transition: background-color 0.2s;
}
.engage-btn:hover {
background-color: #f7f9fa;
}
::ng-deep .mention {
color: #1da1f2;
font-weight: bold;
}
::ng-deep .hashtag {
color: #1da1f2;
font-weight: bold;
}
`]
})
export class SocialPostComponent {
@Input() post!: SocialPost;
}
// ๐ฎ Demo component
@Component({
template: `
<div class="social-feed">
<h2>๐ฑ Social Feed Demo</h2>
<app-social-post
*ngFor="let post of posts"
[post]="post">
</app-social-post>
</div>
`
})
export class SocialFeedComponent {
posts: SocialPost[] = [
{
id: '1',
author: 'TechGuru_Sarah',
content: 'Just finished an amazing #TypeScript tutorial! ๐ Big thanks to @angular team for the great framework. Learning pipes has never been this fun! #webdev #coding',
timestamp: new Date(Date.now() - 30 * 60 * 1000), // 30 mins ago
likes: 1247,
shares: 89,
comments: 156,
emoji: '๐ฉโ๐ป'
},
{
id: '2',
author: 'DevLife_Alex',
content: 'Hot take: Angular pipes are underrated! ๐ฅ They make data transformation so elegant. Shoutout to @everyone who helped me understand async pipes! #angular #javascript',
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2 hours ago
likes: 892,
shares: 234,
comments: 67,
emoji: '๐งโ๐ป'
}
];
}
๐ Key Takeaways
Youโve learned so much! Hereโs what you can now do:
- โ Use built-in Angular pipes with confidence ๐ช
- โ Create custom pipes for specific needs ๐ก๏ธ
- โ Apply performance best practices in real projects ๐ฏ
- โ Debug pipe issues like a pro ๐
- โ Build type-safe data transformations with TypeScript! ๐
Remember: Angular pipes are your friends for clean, readable templates! They help separate concerns and make your code more maintainable. ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve mastered Angular Pipes and data transformation!
Hereโs what to do next:
- ๐ป Practice with the social media exercise above
- ๐๏ธ Build a project using custom pipes for data formatting
- ๐ Move on to our next tutorial: Angular Directives - Custom Directives
- ๐ Share your pipe creations with the community!
Remember: Every Angular expert was once a beginner. Keep coding, keep learning, and most importantly, have fun transforming data! ๐
Happy coding! ๐๐โจ