1
votes

Whenever I use tag mat-expansion-panel-header in html I get error in console.

Using https://material.angular.io/components/expansion/overview basic expansion panel example.

ERROR TypeError: Cannot read property 'pipe' of undefined
at new MatExpansionPanelHeader (expansion.es5.js:326)
at createClass (core.js:9103)
at createDirectiveInstance (core.js:8978)
at createViewNodes (core.js:10198)
at callViewAction (core.js:10514)
at execComponentViewsAction (core.js:10433)
at createViewNodes (core.js:10226)
at createRootView (core.js:10112)
at callWithDebugContext (core.js:11143)
at Object.debugCreateRootView [as createRootView] (core.js:10630)
View_TrainingOptionsComponent_0 @ TrainingOptionsComponent.html:25

I have MatExpansionModule in module.ts.

Versions in package.json:

  • "@angular/material": "^6.3.3",
  • "@angular/material-moment-adapter": "^6.3.3",
  • "typescript": "^2.7.2"

<div fxLayoutAlign="space-between stretch" class="options-list-container">
          <div class="options-list">
            <mat-divider class="mat-divider"></mat-divider>
            <mat-accordion *ngFor="let option of filteredHeaders" class="option mat-accordion" data-id="{{option.id}}">
              <mat-expansion-panel #optionPanel>
                <mat-expansion-panel-header class="mat-expansion-panel-header">
                  <mat-panel-title fxFlex.lg="70" fxFlex.xs="70" fxFlex.md="70">
                    {{option.title}}
                  </mat-panel-title>
                  <mat-panel-title fxFlex.lg="15" fxFlex.xs="70" fxFlex.md="70">
                    {{option.dateFrom | date:'dd-MM-yyyy'}}
                  </mat-panel-title>
                  <mat-panel-title class="status" fxFlex.lg="15" fxFlex.xs="15">
                    {{option.price}} + ~{{option.additionalPrice}} EUR
                  </mat-panel-title>
                </mat-expansion-panel-header>
                <app-option-details *ngIf="optionPanel._getExpandedState() === 'expanded'"
                                    [id]="option.id"
                                    (dateEmitter)="getDates($event)">

                </app-option-details>
                <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
                  <div fxFlex="50" fxLayout.lt-lg="100" fxLayoutAlign="start center">
                    <button mat-raised-button color="primary" (click)="openEditOption(option.id); editMode = true">EDIT OPTION</button>
                  </div>
                  <div fxFlex="100" fxLayout.lt-lg="100" fxLayoutAlign="end center">
                    <button mat-raised-button color="primary" (click)="openDialog(option);">APPLY</button>
                  </div>
                </div>
              </mat-expansion-panel>
            </mat-accordion>
          </div>
        </div>

component.ts

import { Component, OnInit } from '@angular/core';
import { trigger, style, animate, transition } from '@angular/animations'
import { HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { MatDialog } from '@angular/material';
import { ITrainingOption } from '../shared/trainingOption.model';
import { ISelector } from '../shared/selector.model';
import { IOptionHeader } from '../shared/optionHeader.model';
import { ApiService } from '../shared/api.service';
import { FormGroup, FormControl } from '@angular/forms';
import { ApplicationsComponent } from './applications/applications.component';
import { MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';

export const dateFormat = {
  parse: {
    dateInput: 'l',
  },
  display: {
    dateInput: 'l',
    monthYearLabel: 'MMM YYYY',
    dateA11yLabel: 'l',
    monthYearA11yLabel: 'MMMM YYYY',
  },
};

export interface IIsInternalSelect {
  value: boolean;
  viewValue: string;
}

@Component({
  selector: 'app-options',
  templateUrl: './training-options.component.html',
  styleUrls: ['./training-options.component.scss'],
  providers: [
    {
      provide: DateAdapter,
      useClass: MomentDateAdapter,
      deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS, MAT_DATE_FORMATS]
    },
    {
      provide: MAT_DATE_FORMATS,
      useValue: dateFormat
    },
    { provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
    { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
  ],
  animations: [
    trigger(
      'enterAnimation', [
        transition(':enter',
          [
            style({ transform: 'translateX(100%)' }),
            animate('400ms', style({ transform: 'translateX(0)' }))
          ]),
        transition(':leave', [
          style({ transform: 'translateX(0)' }),
          animate('200ms', style({ transform: 'translateX(100%)' }))
        ])
      ]
    )
  ],
})

export class TrainingOptionsComponent implements OnInit {
  constructor(private apiService: ApiService, public dialog: MatDialog) { }

  ngOnInit(): void {
    this.getCategories();
    this.getEventTypes();
    this.getHeaders();
  }

  form = new FormGroup({
    id: new FormControl(),
    title: new FormControl(),
    link: new FormControl(),
    description: new FormControl(),
    categoryId: new FormControl(),
    periodFrom: new FormControl(),
    periodTo: new FormControl(),
    price: new FormControl(),
    additionalPrice: new FormControl(),
    eventTypeId: new FormControl(),
    isInternal: new FormControl(),
    isFullDay: new FormControl(),
    createdById: new FormControl()
  });

  isAddOptionShown: boolean;
  minDateFrom: Date = new Date();
  minDate: Date;
  maxDate: Date;

  categories: ISelector[] = [];
  eventTypes: ISelector[] = [];
  headers: IOptionHeader[] = [];
  filteredHeaders: IOptionHeader[] = [];
  optionDates: Date[] = [];

  organizerTypes: IIsInternalSelect[] = [
    { value: true, viewValue: 'Internal' },
    { value: false, viewValue: 'External' }
  ];

  openDialog(option: IOptionHeader): void {
    this.dialog.open(ApplicationsComponent, {
      data: {
        opt: option,
        dates: this.optionDates
      }
    });
  }

  set searchString(value: string) {
    this.filteredHeaders = value ? this.performFilter(value) : this.headers;
  }

  performFilter(filterBy: string): IOptionHeader[] {
    filterBy = filterBy.toLocaleLowerCase();
    return this.headers.filter((header: IOptionHeader) =>
      header.title.toLocaleLowerCase().indexOf(filterBy) !== -1);
  }

  submitOption(option: ITrainingOption) {
    this.apiService.addOrUpdateOption(option).subscribe(() => {
      this.isAddOptionShown = false;
      this.getHeaders();
    });
  }

  getHeaders() {
    this.apiService.getTrainingOptionHeaders().subscribe(
      x => {
        this.headers = x;
        this.filteredHeaders = x;
      }
    );
  }

  getCategories() {
    this.apiService.getCategories().subscribe(
      categories => {
        this.categories = categories;
      });
  }

  getEventTypes() {
    this.apiService.getEventTypes().subscribe(
      eventTypes => {
        this.eventTypes = eventTypes;
      });
  }

  openEditOption(editOptionId: number) {
    this.apiService.getTrainingOption(editOptionId).subscribe(x => {
      this.form.setValue({
        'id': x.id,
        'title': x.title,
        'description': x.description,
        'link': x.link,
        'categoryId': x.categoryId,
        'eventTypeId': x.eventTypeId,
        'isInternal': x.isInternal,
        'price': x.price,
        'additionalPrice': x.additionalPrice,
        'periodFrom': x.periodFrom,
        'periodTo': x.periodTo,
        'isFullDay': x.isFullDay,
        'createdById': x.createdById
      });
      this.isAddOptionShown = true;
      this.minDateFrom = x.periodFrom;
      this.minDate = x.periodFrom;
      this.maxDate = x.periodTo;
    });
  }

  getDates(dates: Date[]) {
    this.optionDates = dates;
  }

  resetForm() {
    this.form.setValue({
      'id': 0,
      'title': '',
      'description': '',
      'link': '',
      'categoryId': '',
      'eventTypeId': '',
      'isInternal': false,
      'price': 0,
      'additionalPrice': 0,
      'periodFrom': null,
      'periodTo': null,
      'isFullDay': false,
      'createdById': '.'
    });
    this.minDateFrom = new Date();
    this.minDate = this.minDateFrom;
    this.maxDate = null;
  }
}

module.ts

import {
  MatExpansionModule,
  MatButtonModule,
  MatButtonToggleModule,
  MatInputModule,
  MatToolbarModule,
  MatIconModule,
  MatListModule,
  MatSelectModule,
  MatDatepickerModule,
  MatNativeDateModule,
} from '@angular/material';
import { MatChipsModule } from '@angular/material/chips';
import { CoreModule } from '../core/core.module';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TrainingOptionsComponent } from './training-options.component';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatCardModule } from '@angular/material/card';
import { CommonModule } from '@angular/common';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ApplicationsComponent } from './applications/applications.component';
import { TrainingRequestsModule } from '../training-requests/training-requests.module';

export const appRoutes: Routes = [
  { path: '', component: TrainingOptionsComponent },
  { path: 'application', component: ApplicationsComponent }
];

@NgModule({
  imports: [
    MatCardModule,
    FlexLayoutModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatInputModule,
    MatToolbarModule,
    MatIconModule,
    MatCheckboxModule,
    MatListModule,
    MatChipsModule,
    MatSelectModule,
    MatDatepickerModule,
    MatNativeDateModule,
    MatExpansionModule,
    CoreModule,
    CommonModule,
    RouterModule.forChild(appRoutes),
    FormsModule,
    ReactiveFormsModule,
    TrainingRequestsModule
  ],
  declarations: [TrainingOptionsComponent, ApplicationsComponent],
  exports: [],
})
export class TrainingOptionsModule { }

EDIT: Added code. For a record, doesn't work even if I change html to basic expansion panel example's html. Link is in second line.

2
Can you post your fragment code?Allan Braga

2 Answers

1
votes

The probable cause is that you have a mismatch in @angular/material and @angular/cdk versions.

They need to be exactly the same version.

Source: Github issue comment

0
votes

It might be helpful to share your HTML structure and you TypeScript code as well for that component.

In the most basic scenario you just would have to import and export the MatExpansionModule on you module.ts, after that you just need to write the correct HTML tag inside an <mat-expansion-panel> tag.

It seems you are trying to use a Pipe on an piece of information that's not properly defined. So as I said, it would help a lot if you share what your HTML looks like.