如何在Angular+PrismJS组件中清理代码片段的ng-content内容
Got it, let's tackle how to clean up the ng-content code in your Angular Prism component. The main pain points here are usually unwanted whitespace (like extra indentation from your template), stray line breaks, and HTML special characters that might get parsed instead of showing up as code. Here's a step-by-step solution tailored to your component:
1. First, Understand the Current Flow
Your component currently captures ng-content in a hidden rawContent element, then binds its content to innerHtml. The problem is that ng-content preserves all the whitespace and formatting from where you use the component in your template—so if you indent the code inside <prism>, that extra indent will show up in the rendered code block.
2. Add Content Cleaning Logic
We'll add a few helper functions to clean the raw content before passing it to Prism. Here's how to modify your component:
Step 1: Add Helper Functions
Add these private methods to handle whitespace normalization and HTML escaping (critical if your code includes <, >, or &):
private escapeHtml(unsafe: string): string { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } private normalizeWhitespace(rawText: string): string { // Trim leading/trailing whitespace const trimmed = rawText.trim(); // Split into lines const lines = trimmed.split('\n'); // Find the minimum indentation across all non-empty lines const minIndent = lines .filter(line => line.trim() !== '') .reduce((min, line) => { const indent = line.match(/^\s*/)?.[0].length || 0; return Math.min(min, indent); }, Infinity); // Remove the minimum indent from each line return lines.map(line => line.slice(minIndent)).join('\n'); }
Step 2: Update the AfterViewInit Logic
Modify your ngAfterViewInit method to use these helpers to clean the content before highlighting:
ngAfterViewInit(): void { // Get the raw text content from the hidden element const rawText = this.rawContent.nativeElement.textContent; // Clean the content: normalize whitespace + escape HTML const cleanedText = this.normalizeWhitespace(rawText); const escapedText = this.escapeHtml(cleanedText); // Use Prism to highlight the cleaned text this.content = Prism.highlight(escapedText, Prism.languages[this.language], this.language); // Trigger change detection since we're updating the content property this.cdRef.detectChanges(); }
Step 3: Full Modified Component Code
Here's the complete component with all cleanup logic included:
import { Component, AfterViewInit, Input, ElementRef, ViewChild, ChangeDetectorRef } from '@angular/core'; declare var Prism: any; @Component({ selector: 'prism', template: ` <div hidden="true" #rawContent> <ng-content></ng-content> </div> <section class="code-container"> <pre><code [innerHtml]="content" class="block language-{{language}}"></code></pre> </section> ` }) export class PrismComponent implements AfterViewInit { @Input() language: string = 'javascript'; content: string = ''; @ViewChild('rawContent') rawContent!: ElementRef; constructor(private cdRef: ChangeDetectorRef) {} ngAfterViewInit(): void { const rawText = this.rawContent.nativeElement.textContent; const cleanedText = this.normalizeWhitespace(rawText); const escapedText = this.escapeHtml(cleanedText); this.content = Prism.highlight(escapedText, Prism.languages[this.language], this.language); this.cdRef.detectChanges(); } private escapeHtml(unsafe: string): string { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } private normalizeWhitespace(rawText: string): string { const trimmed = rawText.trim(); const lines = trimmed.split('\n'); const minIndent = lines .filter(line => line.trim() !== '') .reduce((min, line) => { const indent = line.match(/^\s*/)?.[0].length || 0; return Math.min(min, indent); }, Infinity); return lines.map(line => line.slice(minIndent)).join('\n'); } }
3. Key Benefits of This Approach
- Whitespace Normalization: Removes extra indentation that comes from nesting the code inside your Angular template, so your code block looks clean.
- HTML Escaping: Ensures characters like
<,>, and&are rendered as code instead of being parsed as HTML, which prevents broken syntax highlighting and unexpected rendering. - Preserves Code Structure: Keeps the original line breaks and intentional indentation, so your code remains readable.
4. Optional: Handle Edge Cases
If you need to handle things like trailing spaces or empty lines, you can tweak the normalizeWhitespace function. For example, to remove empty lines entirely:
// Inside normalizeWhitespace, after splitting into lines const filteredLines = lines.filter(line => line.trim() !== ''); // Then use filteredLines instead of lines for minIndent and joining
内容的提问来源于stack exchange,提问作者Jaime Still

