Шаблон как <script type = "text/ng-template" > эквивалент с angular 2

Angularjs (например, angular 1) имел это удобное поведение для поиска элемента < script type = "text/ng-template" > , который имел идентификатор данного шаблон URL, прежде чем запрашивать его на сервере.

Пример: приведенный ниже код не запускает дополнительный HTTP-запрос

<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>

Это не работает, используя angular 2.

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

Есть ли другой способ его достижения? Я что-то пропустил?

ps: я не хочу вставлять все мои html в мои ts файлы...

Ответ 1

Если кому-то интересно, я нашел простой способ обхода (более чистое решение было бы лучше)

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{}

Но он предполагает, что <script> уже присутствует, когда загружается этот script.

[EDIT] Лучшее обходное решение

Я просто придумал простую идею, чтобы просто переопределить декоратор @View factory.

1) Создайте файл viewoverride.ts

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: Очень важно разместить его в отдельном и независимом файле, чтобы заставить его выполнить перед другими импортными

2) И поставьте это как строку first вашего файла начальной загрузки:

import './viewoverride'

3) Это. Обозначение @View теперь переопределено

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

теперь будет искать элемент script, который id mytemplate.template

Ответ 2

Я думаю, что более чистым способом для этого было бы, если бы вы предоставили свой собственный ViewResolver, вдохновленный исходным кодом angular beta 17, что-то в строках:

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)
  }
}

Ссылка на плейер