1

I'm quite new to Angular and I struggle with following problem.

I'd like to render the content of my component templates within the DOM. The goal is to produce a styleguide where I want to show code-snippets of the templates. Therefor I need the escaped template-content.

I've tried several things and came up with a possible solution to create a custom renderer which escapes the template output.

I thought it could be as easy as described here: Rendering in Angular But... it wasn't.

I created as described here Stack Overflow - custom-renderer-for-angular2 a custom renderer, which should first of all function like the default DebugDomRenderer. But I do not know how to use this renderer.

I tried things like:

import {Component} from '@angular/core';
import {EscapeRootRenderer} from "../../renderer/escapeRootRenderer";
import {DebugDomRootRenderer} from "@angular/core/src/debug/debug_renderer";

@Component({
  selector:'escape',
  template:'<pre><code><ng-content></ng-content></code></pre>',
  providers:[
    {provide: DebugDomRootRenderer, useClass:EscapeRootRenderer}
  ]
})

export class EscapeComponent{

}

This part is not ready yet. My plan is to take the ChildViews and render them escaped...

My EscapeRootRender does exactly the same thing as the DebugDomRootRenderer except that I removed the debug stuff and added some console.logs()

import {Injectable, RenderComponentType, Renderer, RootRenderer} from '@angular/core';

import {AnimationStyles} from "@angular/core/esm/src/animation/animation_styles";
import {AnimationKeyframe} from "@angular/core/esm/src/animation/animation_keyframe";


@Injectable()
export class EscapeRootRenderer implements RootRenderer {

  constructor(private _delegate:RootRenderer) {
    console.log('EscapeRootRenderer constructed')
    console.log(_delegate)
  }


  renderComponent(componentProto: RenderComponentType): EscapeRenderer {
    console.log('EscapeRootRenderer - renderComponent');
    return new EscapeRenderer(this._delegate.renderComponent(componentProto));
  }

}

export class EscapeRenderer implements Renderer{

  constructor(  private _delegate: Renderer){
    console.log('EscapeRenderer constructed');
  }

  animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string): any {
    console.log('EscapeRenderer animate');
    return this._delegate.animate(element, startingStyles, keyframes, duration, delay, easing);
  }

  selectRootElement(selector: string): any {
    console.log('EscapeRenderer selectRootElement');
    var nativeEl = this._delegate.selectRootElement(selector);
    return nativeEl;
  }

  createElement(parentElement: any, name: string): any {
    console.log('EscapeRenderer createElement');
    var nativeEl = this._delegate.createElement(parentElement, name);
    return nativeEl;
  }

  createViewRoot(hostElement: any): any {
    console.log('EscapeRenderer createViewRoot');
    return this._delegate.createViewRoot(hostElement); }

  createTemplateAnchor(parentElement: any): any {
    console.log('EscapeRenderer createTemplateAnchor');
    var comment = this._delegate.createTemplateAnchor(parentElement);
    return comment;
  }

  createText(parentElement: any, value: string): any {
    console.log('EscapeRenderer createText');
    var text = this._delegate.createText(parentElement, value);
    return text;
  }

  projectNodes(parentElement: any, nodes: any[]) {
    console.log('EscapeRenderer projectNodes');
    return this._delegate.projectNodes(parentElement, nodes);
  }

  attachViewAfter(node: any, viewRootNodes: any[]) {
    console.log('EscapeRenderer attachViewAfter');
    return this._delegate.attachViewAfter(node, viewRootNodes);
  }

  detachView(viewRootNodes: any[]) {
    console.log('EscapeRenderer detachView');
    return this._delegate.detachView(viewRootNodes);
  }

  destroyView(hostElement: any, viewAllNodes: any[]) {
    console.log('EscapeRenderer destroyView');
    return this._delegate.destroyView(hostElement, viewAllNodes);
  }

  listen(renderElement: any, name: string, callback: Function) {
    console.log('EscapeRenderer listen');
    return this._delegate.listen(renderElement, name, callback);
  }

  listenGlobal(target: string, name: string, callback: Function): Function {
    console.log('EscapeRenderer listenGlobal');
    return this._delegate.listenGlobal(target, name, callback);
  }

  setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
    console.log('EscapeRenderer setElementProperty');
    return this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
  }

  setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
    console.log('EscapeRenderer setElementAttribute');
    return this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
  }

  /**
   * Used only in debug mode to serialize property changes to comment nodes,
   * such as <template> placeholders.
   */
  setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
    console.log('EscapeRenderer setBindingDebugInfo');
    return this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
  }


  setElementClass(renderElement: any, className: string, isAdd: boolean) {
    console.log('EscapeRenderer setElementClass');
    return this._delegate.setElementClass(renderElement, className, isAdd);
  }

  setElementStyle(renderElement: any, styleName: string, styleValue: string) {
    console.log('EscapeRenderer setElementStyle');
    return this._delegate.setElementStyle(renderElement, styleName, styleValue);
  }

  invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
    console.log('EscapeRenderer invokeElementMethod');
    return this._delegate.invokeElementMethod(renderElement, methodName, args);
  }

  setText(renderNode: any, text: string) {
    console.log('EscapeRenderer setText');
    return this._delegate.setText(renderNode, text); }

}

Now to the question(s):

  1. Is that (customRenderer) a way to handle the problem of escaping template-content, or am I totally wrong?
  2. How can I tell a single component to use a different Renderer?

Thanks for some help,

best Jan

5
  • I think a renderer needs to be provided globally not per component (not tried myself yet). Commented Sep 16, 2016 at 11:33
  • i also tried: providers:[ {provide:DebugDomRootRenderer, useClass:EscapeRootRenderer} ] Commented Sep 16, 2016 at 13:33
  • Do you actually want to render the view source of a component or do you want to render HTML that contains Angular2 bindings but prevent Angular2 from processing the bindings? If you use AoT, the components won't have the original view code included. Bindings are then replaced by JS code. Commented Sep 16, 2016 at 13:37
  • I want to render the escaped Template-File --> <div class="component"><div class="component__element"></div></div>to see the real HTML-Code on the website. The Code-Snippets for developer to copy without opening the Dev-Tools... And I already made it, but in a really simple way, see answer Commented Sep 16, 2016 at 18:16
  • Somebody here on stackblitz went as far as implementing a complete renderer (traversing nodes, adding classes by commands,...) Commented Feb 14, 2020 at 16:31

2 Answers 2

2

I solved my problem without any Custom-Renderer:

import {
  Component, ViewChild, OnInit, AfterViewInit, OnInit
} from '@angular/core';


@Component({
  selector:'escape',
  template:'<content #kiddo><ng-content></ng-content></content><pre><code #codeContent>{{componentInnerHTML}}</code></pre>'
})

export class EscapeComponent implements OnInit, AfterViewInit{
  @ViewChild('kiddo') viewChild;
  @ViewChild('codeContent') codeContent;
  componentInnerHTML:string;

  ngAfterViewInit(){
    this.componentInnerHTML = this.viewChild.nativeElement.innerHTML;
  }

  ngOnInit(){
    var that = this;
    setTimeout(()=>hljs.highlightBlock(that.codeContent.nativeElement), 1)
  }
}

The Result looks now like this. Developers can now simply copy code directly from the website...

Screenshot output EscapeComponent

But, I'm still interested on how I can use a Custom-Renderer.

Sign up to request clarification or add additional context in comments.

Comments

0

I still don't fully understand the question but perhaps this does what you want

<pre ngNonBindable>
    template content here
</pre>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.