angular2 ng-template在一个单独的文件中

时间:2018-03-21 11:13:10

标签: angular typescript ng-template

angular2如何使用来自不同文件的ng-template?当我将ng-template放在我使用它的同一个HTML中时,但是当我将ng-template移动到一个单独的文件中时,它就无法工作。有没有办法将ng-template移动到自己的文件中并在不同的html文件中使用?

信息-message.html

<ng-template #messageTemplate>
    Hi
</ng-template>

<ng-container *ngTemplateOutlet="messageTemplate;"></ng-container>

以上工作正常,因为ng-template和用法在同一个文件中

消息template.html

<ng-template #messageTemplate>
    Hi
</ng-template>

信息-message.html

<ng-container *ngTemplateOutlet="messageTemplate;"></ng-container>

这不起作用。有没有办法使用&#34; messageTemplate&#34; ,它位于另一个html中的单独文件中(例如:info-message.html)

提前致谢。

5 个答案:

答案 0 :(得分:1)

您看到了吗? https://github.com/angular/angular/issues/27503 dawidgarus提供了一个示例

建议是,如果要在其他文件中重复使用模板,则应将模板中的内容转换为单独的组件,然后可以在任何位置重复使用该组件。

答案 1 :(得分:0)

你可以使用这样的东西(模板用于另一个组件):

@Component(
    template: '<ng-container *ngTemplateOutlet="infoMessage.template;"></ng-container>'
)
export class MessageTemplate {
    infoMessage: InfoMessage;    
}

@Component(
    ....
)
export class InfoMessage{    
    @ContentChild('columnTemplate') template: TemplateRef<any>;

    constructor(private messageTemplate: MessageTemplate) {
        messageTemplate.infoMessage = this;
    }
}

答案 2 :(得分:0)

此行为可以通过“门户”来实现。这是Angular应用程序中有用且相当普遍的模式。例如,您可能有一个位于顶层应用程序附近的全局边栏出口,然后子组件可以在其位置指定本地<ng-template/>作为其整体模板的一部分。

请注意,尽管<ng-template/>可以在定义了所需出口的文件之外定义,但仍需要将<ng-template/>放在 some 的模板内零件。这可以是仅用于包装<ng-template/>的极简主义组件,但也可以是其中所关注的<ng-template/>仅占较小部分的复杂组件。

此代码说明了门户网站的一种可能的基本实现。

@Directive({
  selector: '[appPortal]'
})
export class PortalDirective implements AfterViewInit {
  @Input() outlet: string;

  constructor(private portalService: PortalService, private templateRef: TemplateRef<any>) {}

  ngAfterViewInit(): void {
    const outlet: PortalOutletDirective = this.portalService.outlets[this.outlet];
    outlet.viewContainerRef.clear();
    outlet.viewContainerRef.createEmbeddedView(this.templateRef);
  }
}

@Directive({
  selector: '[appPortalOutlet]'
})
export class PortalOutletDirective implements OnInit {
  @Input() appPortalOutlet: string;

  constructor(private portalService: PortalService, public viewContainerRef: ViewContainerRef) {}

  ngOnInit(): void {
    this.portalService.registerOutlet(this);
  }
}

@Injectable({
  providedIn: 'root'
})
export class PortalService {
  outlets = new Map<string, PortalOutletDirective>();

  registerOutlet(outlet: PortalOutletDirective) {
    this.outlets[outlet.appPortalOutlet] = outlet;
  }
}

它分为三个部分工作:

  • “门户”指令。这位于所需的<ng-template/>上,并以应呈现内容的出口名称作为输入。
  • “门户出口”指令。这生活在一个出口,例如<ng-container/>,并定义出口。
  • “门户”服务。这是在根级别提供的,并存储对门户出口的引用,以便门户可以访问它们。

对于一些非常简单的事情来说,这似乎是很多工作,但是一旦安装好管道,就很容易(重复)使用。

<div class="container">
  <div class="row">
    <div class="col-6">
      <app-foo></app-foo>
    </div>
    <div class="col-6">
      <ng-container [appPortalOutlet]="'RightPanel'"></ng-container>
    </div>
  </div>
</div>

// foo.component.html
<h1>Foo</h1>
<ng-template appPortal [outlet]="'RightPanel'">
 <h1>RIGHT</h1>
</ng-template>

总的来说,重塑轮子不是一个好主意,尽管已经有经过测试,证明和稳定的实现方式。 Angular CDK提供了such an implementation,我建议您在实践中使用它而不是您自己的。

答案 3 :(得分:0)

如果要加载单独的文件,则可以在单独的文件中定义组件(而不是<ng-template>)。然后使用<ng-container>将整个组件注入*ngComponentOutlet中。

您可以在此处找到带有示例的完整后缀:https://stackoverflow.com/a/59180628/2658683

答案 4 :(得分:0)

出于解释和可移植性的原因,通过@ peter554扩展答案。这将使您可以跨组件使用模板。

要使用:

'app.module.ts'
import {NgModule} from '@angular/core';
import {
    IdcPortalDirective, IdcTemplatePortalDirective,
    PortalService
} from './idc-template-portal/idc-template-portal.component';

@NgModule({
    declarations: [
        IdcPortalDirective,
        IdcTemplatePortalDirective
    ],
    imports: [],
    exports: [],
    providers: [
        PortalService
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}
'./idc-template-portal/idc-template-portal.component.ts'
import {
    AfterViewInit,
    Directive,
    Injectable,
    Input,
    OnInit, Output,
    TemplateRef,
    ViewContainerRef
} from '@angular/core';
/*** Input Template ***/
/*** <ng-template idcPortal [outlet]="'outletname'">Template Contents</ng-template> ***/
@Directive({
    selector: '[idcPortal]'
})
export class IdcPortalDirective implements OnInit {
    @Input() outlet: string;
    @Output() inlet: string = this.outlet;

    constructor(private portalService: PortalService, public templateRef: TemplateRef<any>) {}

    ngOnInit():void {
        this.portalService.registerInlet(this);
    }

}
/*** Output Container ***/
/*** <ng-container [idcPortalOutlet]="'outletname'"></ng-container> ***/
@Directive({
    selector: '[idcPortalOutlet]'
})
export class IdcTemplatePortalDirective implements OnInit, AfterViewInit {
    @Input() appPortalOutlet: string;
    @Output() outlet: string = this.appPortalOutlet;

    constructor(private portalService: PortalService, public viewContainerRef: ViewContainerRef) {}

    ngOnInit():void {
        this.portalService.registerOutlet(this);
    }

    ngAfterViewInit() {
        this.portalService.initializePortal(this.appPortalOutlet);
    }

}
@Injectable({
    providedIn: 'root'
})
export class PortalService {
    outlets = new Map<string, IdcTemplatePortalDirective>();
    inlets = new Map<string, IdcPortalDirective>();

    registerOutlet(outlet: IdcTemplatePortalDirective) {
        this.outlets[outlet.outlet] = outlet;
    }

    registerInlet(inlet: IdcPortalDirective) {
        this.inlets[inlet.inlet] = inlet;
    }

    initializePortal(portal:string) {
        const inlet: IdcPortalDirective = this.inlets[portal];
        const outlet: IdcTemplatePortalDirective = this.outlets[portal];
        outlet.viewContainerRef.clear();
        outlet.viewContainerRef.createEmbeddedView(inlet.templateRef);
    }
}

他(@ peter554)提到了有关Angular CDK portals package的创新。但是,我发现他/这个实现在应用程序流程中的使用方式更有意义,并且可以轻松地将模板从组件移植到另一个包含门户网站出口的组件(允许将组件移植到component-> portal模板)例如在实现Angular Material MatBottomSheet(idcBottomSheet)的组件模板中。

“输入”元素:

<!--
/*
  For example, perhaps you have a mobile view
  where a template is hidden (via css) and ported
  over to a MatBottomSheet component template to be 
  popped up when requested (by button click). 
*/
-->
<button #bottomsheetButton (click)="openBottomSheet(Notes)" mat-button>
    <mat-icon>notes</mat-icon>
</button>
<!--/* hidden in mobile view mode. */-->
<ng-content *ngTemplateOutlet="Notes"></ng-content>
<ng-template #Notes idcPortal [outlet]="'idcBottomSheet'"><!--/* template to port */-->
    <form>
        <mat-form-field class="w-100 h-100">
            <mat-label>A place for your thoughts:</mat-label>
            <textarea matInput
                      cdkTextareaAutosize
                      #autosize="cdkTextareaAutosize"
                      cdkAutosizeMinRows="10"
                      cdkAutosizeMaxRows="10"
                      placeholder="Angular. It makes me feel...">
            </textarea>
        </mat-form-field>
    </form>
</ng-template>

“输出”元素(在MatBottomSheet组件模板内部):

<ng-container [idcPortalOutlet]="'appIdcBottomSheet'"></ng-container>