Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template as <script type="text/ng-template"> equivalent with angular 2

Tags:

angular

Angularjs (e.g. angular 1) had this convenient behaviour of searching for a <script type="text/ng-template"> element which had the id of the given template url before requesting it to the server.

Ex: The code below is not triggering any additional http request

<script type="text/ng-template" id="mytemplate.html">
  This is a body for my template
</script>
<script>
    //...
    app.directive('myComponent', function() {
        return {
            templateUrl: 'mytemplate.html'  // does NOT trigger a http get
        };
    });
</script>

This does not seems to work using Angular 2.

@View({
   templateUrl: 'mytemplate.html',  // will be fetched from server !
})
class MyComponent{}

Is there another way of achieving it ? Am I missing something ?

ps: i do not wish to embbed all my html in my ts files...

like image 698
Olivier Avatar asked Nov 12 '15 13:11

Olivier


People also ask

What is a template in Angular 2?

In Angular, a template is a chunk of HTML. Use special syntax within a template to build on many of Angular's features.

What is ng-template in Angular?

What is ng-template in Angular? ng-template is an Angular element that is used for rendering HTML in a template. However, it is not rendered directly on DOM. If you include an ng-template tag to a template, the tag and the content inside it will be replaced by comment upon render.

What is the job of ng-template ></ ng-template >?

One of the main uses for <ng-template> is to hold template content that will be used by Structural directives. Those directives can add and remove copies of the template content based on their own logic.

What is let row in ng-template?

The let keyword in Angular declares a template input variable that is referenced within the template. In Angular, the micro syntax is used to configure a directive in a compact and friendly string.


2 Answers

If anyone is interested, i found a simple workaround (a cleaner solution would be better, though)

function template(url, viewdef) {
    var elt = document.getElementById(url);
    if (elt && elt.getAttribute('type') == 'text/ng-template') {
        viewdef.template = elt.innerHTML;
    } else
        viewdef.templateUrl = url;
    return viewdef;
}

@View(template('mytemplate.html', {
    directives: [NgIf /*required directives*/]
}))
class MyComponent{}

But it assumes that the <script> is already present when this script is loaded.

[EDIT] Better workaround

I just came up with the simple idea to just override the @View decorator factory.

1) Create a viewoverride.ts file

import * as ng from 'angular2/core'
let oldNgView = ng.View;
function ViewOverride(viewDef) {
    if (typeof viewDef.templateUrl == "string") {
        let elt = document.getElementById(viewDef.templateUrl);
        if (elt && elt.getAttribute('type') == 'text/ng-template') {
            viewDef.template = elt.innerHTML;
            delete viewDef.templateUrl;
        }
    }
    return oldNgView(viewDef);
}
ng.View = <ng.ViewFactory>ViewOverride;

nb: It's very important to put it in a separate and independant file, to force it to be executed before other imports

2) And put this as the first line of your bootstrap file:

import './viewoverride'

3) That's it. The @View notation is now overriden

@View({templateUrl:'mytemplate.template'}) class MyClass{} 

will now seek for a script element which id is mytemplate.template

like image 76
Olivier Avatar answered Sep 21 '22 10:09

Olivier


I think a cleaner way for this would be if you provide your custom ViewResolver, inspired by the angular beta 17's source code, something in the lines of:

import { Type, ViewMetadata, Reflector, Injectable, ComponentMetadata } from 'angular2/core';
import { ViewResolver } from 'angular2/compiler';

const SCRIPT_TYPE_NAME = 'text/ng2-template';

@Injectable()
export class CustomViewResolver extends ViewResolver 
{
  constructor(private _r: Reflector){ super() }
  resolve(component: Type): ViewMetadata
  {
    let annotations = this._r.annotations(component);
    annotations.forEach( cm => 
    {
      if(cm instanceof ComponentMetadata && cm.templateUrl && typeof cm.templateUrl == 'string' )
      {
        let elemTpl = (<any>document).getElementById(cm.templateUrl);
        if( elemTpl && elemTpl.getAttribute('type') == SCRIPT_TYPE_NAME )
        {
          cm.template = elemTpl.innerHTML;
          elemTpl.remove();
          cm.templateUrl = undefined
        }
        else
          throw new Error(`template "${cm.templateUrl}" not found among html scripts`)
      }
    })
    return super.resolve(component)
  }
}

Plunker Link

like image 28
Billy Avatar answered Sep 23 '22 10:09

Billy