web-dev-qa-db-ja.com

別のファイルのangular2 ng-template

angular2別のファイルからng-templateを使用する方法? ng-templateを使用するのと同じHTML内に配置すると機能しますが、ng-templateを別のファイルに移動すると機能しません。 ng-templateを独自のファイルに移動して別のhtmlファイルで使用する方法はありますか?

info-message.html

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

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

ng-templateと使用法が同じファイルにあるため、上記は正常に動作しています

message-template.html

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

info-message.html

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

これは機能していません。別のhtml内の別のファイルにある"messageTemplate"を使用する方法はありますか(例:info-message.html)

前もって感謝します。

12
Pratap A.K

これ見たことある? https://github.com/angular/angular/issues/275 dawidgarusが提供する例があります

テンプレートを別のファイルで再利用する場合は、テンプレートの内容を別のコンポーネントに変換する必要があります。そうすれば、そのコンポーネントをどこでも再利用できます。

2
KlavierCat

別のファイルをロードする場合は、(<ng-template>の代わりに)別のファイルでコンポーネントを定義できます。次に、<ng-container>を使用して、コンポーネント全体を*ngComponentOutletに挿入します。

あなたはここで例を使って完全なsulotionを見つけることができます: https://stackoverflow.com/a/59180628/265868

0
Gil Epshtain

説明と移植性の理由から@ 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ポータルパッケージ に関してホイールの再発明について言及しています。ただし、私は彼/この実装が、アプリケーションフローでの使用方法と、コンポーネントからポータルアウトレットを含む別のコンポーネントへのテンプレートの移植が容易である(コンポーネントからコンポーネント->ポータルテンプレートへのアクセスを許可する)ことに、より意味があると思います。たとえば、 Angular Material MatBottomSheet (idcBottomSheet))を実装するコンポーネントテンプレート内。

「input」要素:

<!--
/*
  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>

'output'要素(MatBottomSheetコンポーネントテンプレート内):

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

次のようなものを使用できます(テンプレートは別のコンポーネントから使用されます)。

@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;
    }
}
0
bluray

この動作は「ポータル」を介して実現できます。これは、Angularアプリケーションで便利でかなり一般的なパターンです。たとえば、トップレベルのアプリの近くにグローバルサイドバーアウトレットがあり、子コンポーネントがローカル<ng-template/>を指定する場合があります。テンプレート全体の一部として、この場所にレンダリングされます。

<ng-template/>は、目的のアウトレットが定義されているファイルの外部で定義される場合がありますが、<ng-template/>someコンポーネント。これは、<ng-template/>のラップのみを担当する最小限のコンポーネントにすることができますが、<ng-template/>が重要な役割を果たすだけの複雑なコンポーネントになることもあります。

このコードは、ポータルの1つの可能な基本的な実装を示しています。

@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;
  }
}

3つの部分を使用して動作します。

  • 「ポータル」ディレクティブ。これは目的の<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このような実装 を提供します。実際には、これを使用するのではなく、それを使用することをお勧めします。

0
Peter554