代码之家  ›  专栏  ›  技术社区  ›  masterfloda

angular 4可重用组件和模板

  •  6
  • masterfloda  · 技术社区  · 7 年前

    我一直致力于在Angular 4中创建一个可重用的组件。我有一堆报告,都由一个搜索表单(每个报告的字段不同)和一个材料表结果列表(每个报告的字段列表不同)组成。当我为每个报表复制整个组件时,它会按预期工作,但我希望将其重构为可重用的组件/模板和扩展它的子组件。但是示波器都错了,我无法理解它是如何工作的。

    汇报组成部分输电系统 (可重用组件)

    import {Component, ViewChild} from '@angular/core';
    import {MatPaginator} from '@angular/material';
    
    import 'rxjs/add/operator/map';
    
    import {ReportsDataSource} from '../services/reports-datasource.service';
    
    @Component({
        selector: 'app-report',
        templateUrl: './report.component.html',
    })
    export class ReportComponent {
        @ViewChild(MatPaginator) paginator: MatPaginator;
    
        /** result table columns */
        columns = [];
    
        /** Column definitions in order */
        displayedColumns = this.columns.map(x => x.columnDef);
    
        /** empty search parameters object, used for form field binding */
    
        /** datasource service */
        dataSource: ReportsDataSource;
    
        /** submit the form */
        getData() {
            this.dataSource.getData();
        }
    }
    

    汇报组成部分html (可重用模板)

    <form (ngSubmit)="getData()" #ReportSearchForm="ngForm">
        <ng-content select=".container-fluid"></ng-content>
        <button type="submit" mat-button class="mat-primary" [disabled]="!ReportSearchForm.form.valid">Search</button>
    </form>
    <mat-table #table [dataSource]="dataSource">
        <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
            <mat-header-cell *matHeaderCellDef>{{ column.header }}</mat-header-cell>
            <mat-cell *matCellDef="let row">{{ column.cell(row) }}</mat-cell>
        </ng-container>
        <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
        <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
    </mat-table>
    <mat-paginator #paginator
                   [length]="dataSource ? dataSource.meta.total_results : 0"
                   [pageSize]="dataSource ? dataSource.meta.per_page : 25"
                   [pageSizeOptions]="[10, 25, 50, 100]"
    >
    </mat-paginator>
    

    儿童报告。组成部分输电系统 (具体报告)

    import {Component, OnInit} from '@angular/core';
    
    import {ReportComponent} from '../report.component';
    import {ChildreportService} from './childreport.service';
    import {ReportsDataSource} from '../../services/reports-datasource.service';
    
    @Component({
        selector: 'app-report-child',
        templateUrl: './childreport.component.html',
        providers: [ChildreportService, ReportsDataSource]
    })
    export class ChildreportComponent extends ReportComponent implements OnInit {
        constructor(private childreportService: ChildreportService) {
            super();
        }
    
        /** result table columns */
        columns = [
            {columnDef: 'column1', header: 'Label 1', cell: (row) => `${row.column1}`},
            {columnDef: 'column2', header: 'Label 2', cell: (row) => `${row.column2}`}
        ];
    
        ngOnInit() {
            this.dataSource = new ReportsDataSource(this.ChildreportService, this.paginator);
        }
    }
    

    儿童报告。组成部分html (此报告的搜索表单,嵌入在父模板中)

    <app-report>
        <div class="container-fluid">
            <mat-form-field>
                <input matInput placeholder="some field" name="fieldx">
            </mat-form-field>
        </div>
    </app-report>
    

    什么不起作用:表单和表绑定到 ReportComponent 而不是 ChildreportComponent . 我有点理解为什么会发生这种情况(因为这个模板的作用域就是那个组件),但我不知道如何“继承”模板并处于 儿童报告组件 . 我错过了什么?

    2 回复  |  直到 7 年前
        1
  •  2
  •   masterfloda    7 年前

    我自己想出来的。事实上,解决方案相当简单。我的错误是在报告中同时尝试了两件事。组件,提供模板和逻辑。我最终得到的是一个抽象组件,它包含逻辑,并由每个报告扩展,以及每个报告中类似部分的几个较小组件(shell、结果列表等)。我还从模板表单切换到反应表单。

    报告库。组成部分输电系统

    import {OnInit} from '@angular/core';
    import {FormBuilder, FormGroup} from '@angular/forms';
    import {MatPaginator, MatSidenav} from '@angular/material';
    
    import 'rxjs/add/operator/map';
    
    import {ReportsDataSource} from '../common/services/reports-datasource.service';
    import {ReportsService} from '../common/services/reports.service';
    import {ReportsResultlistService} from '../common/services/reports-resultlist.service';
    
    export abstract class ReportBaseComponent implements OnInit {
        constructor(
            protected _formBuilder: FormBuilder, protected _reportService: ReportsService, protected _resultlistService: ReportsResultlistService) {
        }
    
    
        /**
         * For toggling the search form and resultlist action buttons
         * @type {boolean}
         */
        protected hasResults = false;
    
        /** Default data source for the table */
        protected dataSource: ReportsDataSource;
    
        /** search form controls */
        protected searchForm: FormGroup;
    
        /** result table columns */
        protected columns = [];
    
        ngOnInit() {
            this.createForm();
            this.dataSource = new ReportsDataSource(this._reportService, this._resultlistService);
        }
    
        /**
         * Builds the searchForm Group
         */
        protected createForm() {
            // create an empty form
            this.searchForm = this._formBuilder.group({});
        }
    
        /**
         * Submits the form/loads data (f.ex. pagination)
         */
        protected getData() {
            this.hasResults = true;
            this.dataSource.search = this.searchForm.value;
            this.dataSource.getData();
        }
    }
    

    报告外壳。组成部分输电系统 是一个子组件(我的逻辑错误之一),它为组件提供外壳:

    import {Component, Input} from '@angular/core';
    import {ActivatedRoute} from '@angular/router';
    
    @Component({
        selector: 'app-report-shell',
        templateUrl: './report-shell.component.html',
    })
    export class ReportShellComponent {
        constructor(private route: ActivatedRoute) {
            this.title = route.routeConfig.data['caption'];
        }
    
        @Input() hasResults = false;
        title: string;
    }
    

    提供搜索表单和结果列表周围的HTML

    <mat-expansion-panel [expanded]="!hasResults">
        <mat-expansion-panel-header>
            Search
        </mat-expansion-panel-header>
        <ng-content select="form"></ng-content>
    </mat-expansion-panel>
    <div class="result-list">
        <mat-toolbar class="result-header"><span>{{ title }}</span>
            <span class="fill-remaining-space"></span>
            <button class="fa fa-file-excel-o" (click)="exportExcel()"></button>
        </mat-toolbar>
        <ng-content select=".result-table"></ng-content>
    </div>
    

    因此,我的报告扩展了报告库,只需在子级使用shell:

    是仅实现此报告特定内容的特定报告

    import {Component, OnInit} from '@angular/core';
    import {FormBuilder, Validators} from '@angular/forms';
    
    import {ReportChildreportService} from './childreport.service';
    import {ReportsDataSource} from '../../common/services/reports-datasource.service';
    import {ReportsResultlistService} from '../../common/services/reports-resultlist.service';
    
    import {ReportBaseComponent} from '../report-base.component';
    
    
    @Component({
        selector: 'app-report-dispatches',
        templateUrl: './dispatches.component.html',
        providers: [ReportChildreportService, ReportsResultlistService, ReportsDataSource]
    })
    export class ReportDispatchesComponent extends ReportBaseComponent implements OnInit {
        constructor(protected _reportService: ReportChildreportService, protected _formBuilder: FormBuilder, protected _resultlistService: ReportsResultlistService) {
            super(_formBuilder, _reportService, _resultlistService);
        }
    
        /** result table columns */
        columns = [
            {columnDef: 'name', header: 'Name', cell: (row) => `${row.name}`}
        ];
    
        createForm() {
            this.searchForm = this._formBuilder.group({
                name: ''
            });
        }
    }
    

    儿童报告。组成部分html

    <app-report-shell [hasResults]="hasResults">
        <form (ngSubmit)="getData()" [formGroup]="searchForm" novalidate>
                        <mat-form-field>
                            <input matInput placeholder="search for a name" name="name" formControlName="name">
                            <mat-error>Invalid name</mat-error>
                        </mat-form-field>
                    </div>
            </div>
            <app-form-buttons [status]="searchForm.status"></app-form-buttons>
        </form>
            <app-report-result-list
                    [(dataSource)]="dataSource"
                    [columns]="columns"
                    [displayedColumns]="displayedColumns"
                    class="result-table"
            ></app-report-result-list>    
    </app-report-shell>
    

    我不会深入讨论表单和结果列表组件的细节,这个答案已经足够长了:-)

    所以我设法减少了很多代码重复,尽管仍然有一些重复(除了表单之外,childreport.component.html中也有重复)。

        2
  •  0
  •   Halil İbrahim Karaalp    7 年前

    我建议你去看看 this article . @WjComponent decorator可能会为您的方法提供线索。 我从这篇文章中了解到,您需要一个新的组件装饰器来在基类和子类之间共享属性。

    引用文章:

    @Component({  selector: 'inherited-grid'
    })
    export class InheritedGrid extends wjGrid.WjFlexGrid {
    ...
    }
    

    现在我们有了组件的新元素名称!但是我们错过了在基本WjFlexGrid类的decorator中定义的所有其他必要设置。例如,WjFlexGrids decorator为输入decorator属性分配一个网格属性数组,该数组可用于标记中的绑定。我们在新组件中丢失了它,如果您现在尝试绑定到它们,您会发现绑定不起作用。

    答案是:Wijmo为Angular 2模块提供的@WjComponent装饰器。它的使用方式与标准@Component decorator相同,并接受所有@Component decorators属性(加上一些特定于Wijmo的属性),但其主要优点是它将其属性值与基类decorator提供的属性合并。因此,我们组件定义的最后一步是用@WjComponent替换@component:

    @WjComponent({
      selector: 'inherited-grid'
    })
    export class InheritedGrid extends wjGrid.WjFlexGrid {
    ...
    }
    


    另一种方法可能是将ReportComponent定义为指令,并通过 @Host decorator .

    您还可以查看的源代码 ngx-datatable . 它们的示例和组件的源代码信息丰富,可以为您提供在组件和重写模板之间共享数据的想法。