【问题标题】:Angular 2+ Developing Framework componentsAngular 2+ 开发框架组件
【发布时间】:2017-12-08 05:51:07
【问题描述】:

我正在尝试为企业网站开发一些基本的框架控件,开发人员可以在其中指定某些原语类型,这些原语类型可以以最小的努力实现一致性,但仍具有完全控制布局和内容的能力。每个控件都有责任定义样式和维护一些可访问性要求。一个类比是在 Windows 桌面应用程序中。我可以将 GroupBox 或 Panel 拖到表单上并从表单设置属性,然后我可以将控件和自定义内容添加到该面板,而无需面板知道所放置的内容。

所以,作为一个例子,到目前为止,我有一个仪表板组件,我想在屏幕顶部放置一些图块,然后是几个面板。一个面板将显示一个表格,另一个面板将有一段文本,后跟一个图表,另一个面板将显示一些垂直堆叠的瓷砖。我需要指定框架控件及其放置位置,但我不确定如何传递丰富的内容或另一个配置组件以在我的面板组件的panel-body div 中呈现。

仪表板模板 这定义了一个 tile,然后是一些接受属性的面板,这些属性很容易绑定,因为这些是已知的字符串值:

<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 类型的局部变量,但是仪表板应该只实现该方法以输出原始 html 还是我可以通过...传递另一个组件实例??

非常感谢任何建议。 我可能错过了一些非常简单的东西。

【问题讨论】:

    标签: angular single-page-application


    【解决方案1】:

    一周内几乎看不到,但这是colleague 想出的答案。 该方法是使用指令。因此,鉴于我有一个 Dashboard 组件,我将放置几个面板,而不必重复编写 Panel 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 是 PanelComponent 以及 somePanelComponentotherPanelComponent 的属性。对于我打算用一些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() {    
    
      }
    
    }
    

    正在放置的 PanelComponent:

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

    对于放置在面板中的每个组件,都需要创建一个组件,在我的例子中是 SomePanelComponentOtherPanelComponent。这些也需要添加到 App.module 中的 declarationsentryComponents 数组中。我正在努力研究如何将这些放在我项目中更深层次的模块中,而不是把所有东西都放在顶层。到目前为止,无论我把它们放在哪里,如果它们不在 App.module 中,我都会得到错误,即它们没有在 entryComponents 数组中指定。

    无论如何,我希望这对任何人都有价值。

    【讨论】:

      【解决方案2】:

      我认为Transclusion(NG2+ 中的内容投影)是要走的路

      【讨论】:

        猜你喜欢
        • 2012-10-11
        • 2018-06-18
        • 1970-01-01
        • 1970-01-01
        • 2017-09-09
        • 2011-06-22
        • 2015-01-03
        • 2018-06-01
        • 1970-01-01
        相关资源
        最近更新 更多