title: angular 4 reusable component and template
body: <p>I'm stuck at creating a reusable component in Angular 4. I have a bunch of reports that all consist of a search form (fields are different for each report) and a material table result list (field list differs for each report). It works as expected when I duplicate the whole component for each report, but I want to refactor it into a reusable component/template and child components extending it. But the scopes are all wrong and I can't get my head around how this works.</p>
  
  <p><strong>report.component.ts</strong> (reusable component)</p>
  
  <pre><code>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 =&gt; x.columnDef);
  
      /** empty search parameters object, used for form field binding */
  
      /** datasource service */
      dataSource: ReportsDataSource;
  
      /** submit the form */
      getData() {
          this.dataSource.getData();
      }
  }
  </code></pre>
  
  <p><strong>report.component.html</strong> (reusable template)</p>
  
  <pre><code>&lt;form (ngSubmit)="getData()" #ReportSearchForm="ngForm"&gt;
      &lt;ng-content select=".container-fluid"&gt;&lt;/ng-content&gt;
      &lt;button type="submit" mat-button class="mat-primary" [disabled]="!ReportSearchForm.form.valid"&gt;Search&lt;/button&gt;
  &lt;/form&gt;
  &lt;mat-table #table [dataSource]="dataSource"&gt;
      &lt;ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef"&gt;
          &lt;mat-header-cell *matHeaderCellDef&gt;{{ column.header }}&lt;/mat-header-cell&gt;
          &lt;mat-cell *matCellDef="let row"&gt;{{ column.cell(row) }}&lt;/mat-cell&gt;
      &lt;/ng-container&gt;
      &lt;mat-header-row *matHeaderRowDef="displayedColumns"&gt;&lt;/mat-header-row&gt;
      &lt;mat-row *matRowDef="let row; columns: displayedColumns;"&gt;&lt;/mat-row&gt;
  &lt;/mat-table&gt;
  &lt;mat-paginator #paginator
                 [length]="dataSource ? dataSource.meta.total_results : 0"
                 [pageSize]="dataSource ? dataSource.meta.per_page : 25"
                 [pageSizeOptions]="[10, 25, 50, 100]"
  &gt;
  &lt;/mat-paginator&gt;
  </code></pre>
  
  <p><strong>childreport.component.ts</strong> (a specific report)</p>
  
  <pre><code>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) =&gt; `${row.column1}`},
          {columnDef: 'column2', header: 'Label 2', cell: (row) =&gt; `${row.column2}`}
      ];
  
      ngOnInit() {
          this.dataSource = new ReportsDataSource(this.ChildreportService, this.paginator);
      }
  }
  </code></pre>
  
  <p><strong>childreport.component.html</strong> (the search form for this report, embedded in the parent template)</p>
  
  <pre><code>&lt;app-report&gt;
      &lt;div class="container-fluid"&gt;
          &lt;mat-form-field&gt;
              &lt;input matInput placeholder="some field" name="fieldx"&gt;
          &lt;/mat-form-field&gt;
      &lt;/div&gt;
  &lt;/app-report&gt;
  </code></pre>
  
  <p>What works: I get the form embedded in the main template and no errors.</p>
  
  <p>What doesn't work: The form and table are bound to <code>ReportComponent</code> instead of <code>ChildreportComponent</code>. I kinda understand why this happens (because the scope of this template is that component) but I have no idea how I could "inherit" the template and be in the scope of the <code>ChildreportComponent</code>. What am I missing?</p>