3

Is it possible to render html which is inside app-nav tag already, rather then providing it in templateUrl?

@Component({
  selector: 'app-nav',
  // commented out - templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.scss']
})
export class NavComponent {
  title: string = 'This is my title';
}

Html that is already on the html page.

<app-nav>
  nav works! {{ title }}
</app-nav>

If I uncomment the templateUrl then app-nav will be replaced by nav-component.html page, but I dont want that. I have dynamic html and I want to render that.

Basit
  • 16,316
  • 31
  • 93
  • 154

2 Answers2

2

You can use embedded view with ngTemplateOutlet projection. Wrap your content within <app-nav> tags in <template>. Than in your NavComponent find this TemplateRef with ContentChild and insert this templateRef into component's template passing context that contains your title variable to it. Something like this:

@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.scss']
})
export class NavComponent {
  title: string = 'This is my title';

  @ContentChild('defaultTemplate') defaultTemplate = null // get templateRef
}

In nav.component.html create template outlet with relative template context

<template [ngOutletContext]="{ title: title }" [ngTemplateOutlet]="defaultTemplate"></template>

....other component content....

And then in place of component use:

<app-nav>
  <template #defaultTemplate let-title="title">
    nav works! {{ title }}
  </template>
</app-nav>

UPD:

Here is a plunk with example

in app/some.component.ts there is a ngTemplateOutlet projection from app/app.component.ts template

UPD:

Ok, there is a way to get initial content from index.html into the component. You can use APP_INITIALIZER function that will be executed when an application is initialized.

Here is the plunk

  1. See app/root-template.initializer.ts
  2. In app/app.component.ts I just replace relevant property with initial content. This is a bit hacky way and should be done by replacing template in ComponentMetadata which is obtained with Reflect.getMetadata:

    const annotations = Reflect.getMetadata('annotations', NavComponent)
    const meta = annotations.find(annotation => annotation instanceof ComponentMetadata)
    
    meta.template = meta.template.replace(
      '{{ someVarInTemplate }}',
      'initialContentInIndex'
    )
    

This way the component template will have initial index content and it will be parsed by angular. More about Reflect here

Community
  • 1
  • 1
Yaroslav Grishajev
  • 2,127
  • 17
  • 22
  • didn't work, it was replaced by `nav.component.html` content – Basit Oct 29 '16 at 23:15
  • Well, it should work. Perhaps there is a mistake somewhere. I've updated my answer with a plunk demoing how that should be implemented – Yaroslav Grishajev Oct 29 '16 at 23:48
  • Actually I wanted to put that `nav works {{ title }}` inside `Loading...` my-app tag.. basically that `app-nav` is root component and not sub component. Is it still possible? – Basit Oct 29 '16 at 23:58
  • 1
    I don't think so. You can find explanation here http://stackoverflow.com/questions/32568808/angular2-root-component-with-ng-content#answer-32574733 – Yaroslav Grishajev Oct 30 '16 at 00:02
  • I came up with a solution. It's not complete but it works. To make it fully right I just need some more investigation but perhaps it's enough for you:) – Yaroslav Grishajev Oct 30 '16 at 09:49
  • first of all, thank you for looking into this. Have you worked more on this? I really would like the template to render as it renders in angualr2, rendering like following, will just not work well for little big components. `"this.initialStuff = AppComponent.rootTemplate.replace('{{ title }}', this.title) // this is a hacky way..."` really prefer to have this automated (as angular2 does it) – Basit Nov 04 '16 at 18:15
0

@Yaroslav, expanding on your solution:

  1. Looks like a transclusion (in Angular 1 terms), so in Angular 2 you can use ng-content on the inner component.

    <div class="my-component">
      <ng-content></ng-content>
    </div>
    
  2. To get interpolation working on outer transcluded markup, give the element an id and prefix the interpolated content with it.

    <my-component #comp>
      This is my transcluded content! ++{{comp.title}}++
    </my-component>
    
  3. Don't try to transclude from index.html, it's not an angular component, so it doesn't seem to work as the outer component. If you use app.component as the outer and another my.component as inner, it works.

Here's a fork of your plunkr with the changes. plnkr

For reference, I used Todd Motto's excellent article on angular 2 transclusion: ref here.
The angular guide only vaguely refers to ng-content (with a link that 404's), so I wonder if it's disappearing. May be superseded by ngComponentOutlet ref here

Richard Matsen
  • 20,671
  • 3
  • 43
  • 77