我正在尝试为企业网站开发一些基本的框架控件,开发人员可以在其中指定某些具有最小努力的原始类型,但仍然有能力完全控制布局和内容。每个控件都有责任定义样式并保持一些可访问性要求。一个类比将在Windows桌面应用程序中。我可以将组框或面板拖到表单上并从表单中设置属性,但是我可以在面板中添加控件和自定义内容,而无需面板需要了解所放置的内容。
因此,以示例为例,我有一个仪表板组件,我想在屏幕顶部放置一些瓷砖,然后再放置几个面板。一个面板将显示一个表格,另一个面板将有一个文本段落,然后是图表,另一个面板将显示一些瓷砖垂直堆叠。我需要指定框架控件以及将其放置的位置,但我不确定如何传递富含的内容或其他配置的组件要在我的面板组件的panel-body
Div中渲染。
仪表板模板这定义了一个瓷砖,然后是一些接受属性的面板,这些属性将很容易绑定,因为这些字符串值已知:
<h1 class="page-header">{{title}}</h1>
<div class="row">
<div class="col-md-3 col-sm-6">
<app-infra-tile headerText="Total Visitors" [mainText]="'0' | number" footerText="Some Text" icon="fa-users" colour="bg-blue"></app-infra-tile>
</div>
</div>
<div class="row">
<div class="col-md-3 col-sm-6">
<app-infra-panel heading="Table Panel"></app-infra-panel>
</div>
<div class="col-md-3 col-sm-6">
<app-infra-panel heading="Graph Panel"></app-infra-panel>
</div>
<div class="col-md-3 col-sm-6">
<app-infra-panel heading="Some Panel"></app-infra-panel>
</div>
</div>
瓷砖组件和模板
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-infra-tile',
templateUrl: './tile.component.html',
styleUrls: ['./tile.component.css']
})
export class TileComponent implements OnInit {
@Input() icon: string;
@Input() headerText: string;
@Input() mainText: string;
@Input() footerText: string;
@Input() colour = 'bg-blue';
constructor() { }
ngOnInit() {
}
}
<div class="widget widget-stats {{colour}}">
<div *ngIf="icon" class="stats-icon"><i class="fa {{icon}}"></i></div>
<div class="stats-info">
<div class="stats-title | uppercase">{{headerText}}</div>
<div class="stats-number">{{mainText}}</div>
<div class="stats-desc">{{footerText}}</div>
</div>
</div>
面板组件和模板
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-infra-panel',
templateUrl: './panel.component.html',
styleUrls: ['./panel.component.css']
})
export class PanelComponent implements OnInit {
@Input() htmlId: string;
@Input() heading: string;
constructor() { }
ngOnInit() {
}
}
<div class="panel panel-inverse" id="{{htmlId}}">
<div class="panel-heading">
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-default" data-click="panel-expand"><i class="fa fa-expand"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-success" data-click="panel-reload"><i class="fa fa-repeat"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-warning" data-click="panel-collapse"><i class="fa fa-minus"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-danger" data-click="panel-remove"><i class="fa fa-times"></i></a>
</div>
<h4 class="panel-title">{{heading}}</h4>
</div>
<div class="panel-body">
----- The internal content managed by the dashboard should be rendered here ----
</div>
</div>
我已经考虑过传递包含工厂方法的服务和面板的oninit,将any
类型的局部变量设置为结果,但是仪表板应仅实现该方法来输出RAW HTML,否则我可以通过另一个方法通过... ??的组件实例
任何建议都非常感谢。我可能缺少一些非常简单的东西。
几乎没有在一周内观看,但这是同事提出的答案。该方法是使用指令。因此,鉴于我有一个仪表板组件,我要放置几个面板,而无需重复编写面板html,我会在我的dashboardcomponent的模板中这样做:
<div class="row">
<div class="col-md-4 col-sm-6">
<app-infra-panel heading="Some Panel" [componentName]="somePanelComponent"></app-infra-panel>
</div>
<div class="col-md-8 col-sm-6">
<app-infra-panel heading="Other Panel" [componentName]="otherPanelComponent"></app-infra-panel>
</div>
</div>
heading
是面板的属性,以及 somePanelComponent
和 otherPanelComponent
。对于我打算用一些Sub Component
填充的每个面板,我为组件名称创建一个相应的字符串。
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class ProviderDashboardComponent implements OnInit {
title = 'The Dashboard';
// Panel Content Component Names
somePanelComponent = 'SomePanelComponent';
otherPanelComponent = 'OtherPanelComponent';
constructor() { }
ngOnInit() {
}
}
放置的小组组件:
<div class="panel panel-inverse">
<div class="panel-heading">
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-default" data-click="panel-expand"><i class="fa fa-expand"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-success" data-click="panel-reload"><i class="fa fa-repeat"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-warning" data-click="panel-collapse"><i class="fa fa-minus"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-circle btn-danger" data-click="panel-remove"><i class="fa fa-times"></i></a>
</div>
<h4 class="panel-title">{{heading}}</h4>
</div>
<div class="panel-body">
<div control-factory component="{{componentName}}"></div>
</div>
</div>
import { Component, Input, ComponentFactoryResolver, ViewChild } from '@angular/core';
@Component({
selector: 'app-infra-panel',
templateUrl: './panel.component.html',
styleUrls: ['./panel.component.css']
})
export class PanelComponent {
@Input() heading: string;
@Input() componentName: string;
constructor() { }
}
control-factory
指令:
import { Directive,
Component,
ComponentFactory,
OnChanges,
Input,
ViewContainerRef,
Compiler,
ComponentFactoryResolver,
OnDestroy
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from 'app/app.component';
@Directive({
selector: '[control-factory]'
})
export class ControlFactoryDirective implements OnChanges, OnDestroy {
@Input() component: string;
componentRef: any;
init = false;
factoryClass: any;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { }
ngOnChanges(): void {
if (!this.component || this.init) return;
var factories = Array.from(this.resolver['_factories'].keys());
console.log(this.component);
this.factoryClass = factories.find((x: any) => x.name === this.component);
const factory = this.resolver.resolveComponentFactory<any>(this.factoryClass);
const compRef = this.vcRef.createComponent(factory);
if (this.componentRef) {
this.componentRef.destroy();
}
this.componentRef = compRef;
this.init = true;
}
public ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
}
对于要在面板中放置的每个组件,需要创建一个组件,在我的情况下,SomePanelComponent
和OtherPanelComponent
。这些都需要添加到App.Module中的declarations
和entryComponents
数组。我试图弄清楚如何将它们放在项目中的更深模块中,而不必将所有内容放在顶层。到目前为止,无论我把它们放在哪里,我都会得到错误,如果不在App.Module中,则未在入口附件中指定它们。
无论如何,我希望这对任何人都有价值。
我认为 transclusion (ng2 中的内容投影)是