如何使用动态模板创建组件? (使用内联模板嵌入组件)
How to create a Component with a dynamic template? (Component transclude with inline template)
我正在尝试创建一个内部具有动态模板字符串的组件,该字符串可以访问模板上的局部变量。我尝试过的每一种方法都以 "dynamic template string" 没有被 $compile
结束(angular 1 术语,请原谅)。
下面是组件的代码。在你看到评论的地方我想插入一个模板字符串,它可以在 ngFor
.
中引用 item
@Component({
selector: 'ion-alpha-scroll',
template: `
<ion-scroll [ngStyle]="calculateScrollHeight()" scrollX="false" scrollY="true">
<ion-list class="ion-alpha-list-outer">
<div *ngFor="let items of sortedItems | mapToIterable;">
<ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
<ion-item *ngFor="let item of items.value">
<!-- how can I pass a dynamic template here that can reference item ? -->
</ion-item>
</div>
</ion-list>
</ion-scroll>
<ul class="ion-alpha-sidebar" [ngStyle]="calculateDimensionsForSidebar()">
<li (click)="alphaScrollGoToList(letter)" *ngFor="let letter of alphabet">
<div class="letter">{{letter}}</div>
</li>
</ul>
`,
pipes: [MapToIterable]
})
export class IonAlphaScroll {
@Input() listData: any;
@Input() key: string;
@Input() template: string;
....
}
理想情况下,我希望 ion-alpha-scroll
的嵌入内容引用 ngFor
中的 item
。我尝试在组件的必要 ngFor
中使用 ng-content
但没有成功 -
<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t">
{{item.$t}}
</ion-alpha-scroll>
我试过的一件事是这样的 -
<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t" [template]="alphaScrollTemplate">
</ion-alpha-scroll>
alphaScrollTemplate
只是一个包含{{item.$t}}
的字符串。然后我尝试在评论提出问题的组件中引用它,但它不起作用 -
...
<ion-item *ngFor="let item of items.value">
{{template}}
<!-- this just outputs {{item.$t}} literally -->
</ion-item>
...
我真的很好奇 angular 2 是否有可能做到这一点。我刚找到 。任何帮助或建议将不胜感激,谢谢。
这是我用于 angular 2.0.0-rc.3
的解决方案
此解决方案创建一个动态组件并使用 ViewContainerRef
和 ComponentFactory
加载它。 Here is the ionic 2 component on GitHub.
export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
const cmpClass = class DynamicComponent {};
const decoratedCmp = Component(metadata)(cmpClass);
return resolver.resolveComponent(decoratedCmp);
}
@Directive({
selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
@Input() src: string;
@Input() ionAlphaScrollRef: any;
@Input() currentPageClass: any;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
}
ngOnChanges() {
if (!this.src) return;
const metadata = new ComponentMetadata({
selector: 'dynamic-html',
template: this.src,
pipes: [MapToIterable]
});
createComponentFactory(this.resolver, metadata).then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
let component = this.vcRef.createComponent(factory, 0, injector, []);
component.instance.ionAlphaScrollRef = this.ionAlphaScrollRef;
component.instance.currentPageClass = this.currentPageClass;
});
}
}
@Component({
selector: 'ion-alpha-scroll',
template: `
<dynamic-html-outlet
[src]="alphaScrollTemplate"
[ionAlphaScrollRef]="ionAlphaScrollRef"
[currentPageClass]="currentPageClass">
</dynamic-html-outlet>
`,
pipes: [MapToIterable],
directives: [DynamicHTMLOutlet]
})
export class IonAlphaScroll {
@Input() listData: any;
@Input() key: string;
@Input() itemTemplate: string;
@Input() currentPageClass: any;
@Input() triggerChange: any;
private _scrollEle: HTMLElement;
sortedItems: any = {};
alphabet: any = [];
alphaScrollTemplate: string;
ionAlphaScrollRef = this;
constructor(
@Host() private _content: Content,
private _elementRef: ElementRef,
private vcRef: ViewContainerRef,
private resolver: ComponentResolver
) {
}
ngOnInit() {
this.alphaScrollTemplate = `
<style>
.ion-alpha-sidebar {
position: fixed;
right: 0;
display: flex;
flex-flow: column;
z-index: 50000;
}
.ion-alpha-sidebar li {
flex: 1 1 auto;
list-style: none;
width: 15px;
text-align: center;
}
</style>
<ion-scroll class="ion-alpha-scroll" [ngStyle]="ionAlphaScrollRef.calculateScrollDimensions()" scrollX="false" scrollY="true">
<ion-list class="ion-alpha-list-outer">
<div *ngFor="let items of ionAlphaScrollRef.sortedItems | mapToIterable; trackBy:ionAlphaScrollRef.trackBySortedItems">
<ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
<ion-item *ngFor="let item of items.value">
${this.itemTemplate}
</ion-item>
</div>
</ion-list>
</ion-scroll>
<ul class="ion-alpha-sidebar" [ngStyle]="ionAlphaScrollRef.calculateDimensionsForSidebar()">
<li *ngFor="let letter of ionAlphaScrollRef.alphabet" tappable (click)="ionAlphaScrollRef.alphaScrollGoToList(letter)">
<a>{{letter}}</a>
</li>
</ul>
`;
setTimeout(() => {
this._scrollEle = this._elementRef.nativeElement.querySelector('scroll-content');
this.setupHammerHandlers();
});
}
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
let tmp = {};
for (let i = 0; i < this.listData.length; i++) {
let letter = this.listData[i][this.key].toUpperCase().charAt(0);
if (typeof tmp[letter] === 'undefined') {
tmp[letter] = [];
}
tmp[letter].push(this.listData[i]);
}
this.alphabet = this.iterateAlphabet(tmp);
this.sortedItems = tmp;
}
// ....
}
我正在尝试创建一个内部具有动态模板字符串的组件,该字符串可以访问模板上的局部变量。我尝试过的每一种方法都以 "dynamic template string" 没有被 $compile
结束(angular 1 术语,请原谅)。
下面是组件的代码。在你看到评论的地方我想插入一个模板字符串,它可以在 ngFor
.
item
@Component({
selector: 'ion-alpha-scroll',
template: `
<ion-scroll [ngStyle]="calculateScrollHeight()" scrollX="false" scrollY="true">
<ion-list class="ion-alpha-list-outer">
<div *ngFor="let items of sortedItems | mapToIterable;">
<ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
<ion-item *ngFor="let item of items.value">
<!-- how can I pass a dynamic template here that can reference item ? -->
</ion-item>
</div>
</ion-list>
</ion-scroll>
<ul class="ion-alpha-sidebar" [ngStyle]="calculateDimensionsForSidebar()">
<li (click)="alphaScrollGoToList(letter)" *ngFor="let letter of alphabet">
<div class="letter">{{letter}}</div>
</li>
</ul>
`,
pipes: [MapToIterable]
})
export class IonAlphaScroll {
@Input() listData: any;
@Input() key: string;
@Input() template: string;
....
}
理想情况下,我希望 ion-alpha-scroll
的嵌入内容引用 ngFor
中的 item
。我尝试在组件的必要 ngFor
中使用 ng-content
但没有成功 -
<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t">
{{item.$t}}
</ion-alpha-scroll>
我试过的一件事是这样的 -
<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t" [template]="alphaScrollTemplate">
</ion-alpha-scroll>
alphaScrollTemplate
只是一个包含{{item.$t}}
的字符串。然后我尝试在评论提出问题的组件中引用它,但它不起作用 -
...
<ion-item *ngFor="let item of items.value">
{{template}}
<!-- this just outputs {{item.$t}} literally -->
</ion-item>
...
我真的很好奇 angular 2 是否有可能做到这一点。我刚找到
这是我用于 angular 2.0.0-rc.3
的解决方案此解决方案创建一个动态组件并使用 ViewContainerRef
和 ComponentFactory
加载它。 Here is the ionic 2 component on GitHub.
export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
const cmpClass = class DynamicComponent {};
const decoratedCmp = Component(metadata)(cmpClass);
return resolver.resolveComponent(decoratedCmp);
}
@Directive({
selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
@Input() src: string;
@Input() ionAlphaScrollRef: any;
@Input() currentPageClass: any;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
}
ngOnChanges() {
if (!this.src) return;
const metadata = new ComponentMetadata({
selector: 'dynamic-html',
template: this.src,
pipes: [MapToIterable]
});
createComponentFactory(this.resolver, metadata).then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
let component = this.vcRef.createComponent(factory, 0, injector, []);
component.instance.ionAlphaScrollRef = this.ionAlphaScrollRef;
component.instance.currentPageClass = this.currentPageClass;
});
}
}
@Component({
selector: 'ion-alpha-scroll',
template: `
<dynamic-html-outlet
[src]="alphaScrollTemplate"
[ionAlphaScrollRef]="ionAlphaScrollRef"
[currentPageClass]="currentPageClass">
</dynamic-html-outlet>
`,
pipes: [MapToIterable],
directives: [DynamicHTMLOutlet]
})
export class IonAlphaScroll {
@Input() listData: any;
@Input() key: string;
@Input() itemTemplate: string;
@Input() currentPageClass: any;
@Input() triggerChange: any;
private _scrollEle: HTMLElement;
sortedItems: any = {};
alphabet: any = [];
alphaScrollTemplate: string;
ionAlphaScrollRef = this;
constructor(
@Host() private _content: Content,
private _elementRef: ElementRef,
private vcRef: ViewContainerRef,
private resolver: ComponentResolver
) {
}
ngOnInit() {
this.alphaScrollTemplate = `
<style>
.ion-alpha-sidebar {
position: fixed;
right: 0;
display: flex;
flex-flow: column;
z-index: 50000;
}
.ion-alpha-sidebar li {
flex: 1 1 auto;
list-style: none;
width: 15px;
text-align: center;
}
</style>
<ion-scroll class="ion-alpha-scroll" [ngStyle]="ionAlphaScrollRef.calculateScrollDimensions()" scrollX="false" scrollY="true">
<ion-list class="ion-alpha-list-outer">
<div *ngFor="let items of ionAlphaScrollRef.sortedItems | mapToIterable; trackBy:ionAlphaScrollRef.trackBySortedItems">
<ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
<ion-item *ngFor="let item of items.value">
${this.itemTemplate}
</ion-item>
</div>
</ion-list>
</ion-scroll>
<ul class="ion-alpha-sidebar" [ngStyle]="ionAlphaScrollRef.calculateDimensionsForSidebar()">
<li *ngFor="let letter of ionAlphaScrollRef.alphabet" tappable (click)="ionAlphaScrollRef.alphaScrollGoToList(letter)">
<a>{{letter}}</a>
</li>
</ul>
`;
setTimeout(() => {
this._scrollEle = this._elementRef.nativeElement.querySelector('scroll-content');
this.setupHammerHandlers();
});
}
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
let tmp = {};
for (let i = 0; i < this.listData.length; i++) {
let letter = this.listData[i][this.key].toUpperCase().charAt(0);
if (typeof tmp[letter] === 'undefined') {
tmp[letter] = [];
}
tmp[letter].push(this.listData[i]);
}
this.alphabet = this.iterateAlphabet(tmp);
this.sortedItems = tmp;
}
// ....
}