I am following this project (https://stackblitz.com/edit/angular-material-expandable-table-rows) for my feature development as I am trying to achieve the similar functionality.
I was able get my feature working correctly, however when I run the test, I get the following error -
Template parse errors:
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatHeaderRow ("
</ng-container>
[ERROR ->]<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
"): ng:///DynamicTestModule/MaterialAudioProgramsComponent.html#107:18
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatRow ("t-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
[ERROR ->]<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
I am unable to figure out what I need to do here.
Here's my component.html
<mat-spinner *ngIf="dataLoading" class="loading"></mat-spinner>
<ng-container class="ap-container" *ngIf="materialAudioPrograms$ | async as materialAudioPrograms">
<div class="container" *ngIf="materialAudioPrograms.length > 0; else dataLoading">
<mat-table [dataSource]="dataSource$" class="audio-programs vertical-data">
<ng-container matColumnDef="Start Channel">
<mat-header-cell *matHeaderCellDef>
Start Channel </mat-header-cell>
<mat-cell *matCellDef="let element">
<mat-icon *ngIf="element.show== false" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_right
</mat-icon>
<mat-icon *ngIf="element.show== true" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_down
</mat-icon>
{{element.startChannelNumber}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Layout">
<mat-header-cell *matHeaderCellDef> Layout </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.layout}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Language">
<mat-header-cell *matHeaderCellDef> Language </mat-header-cell>
<mat-cell mat-cell *matCellDef="let element"> {{(element.language != null) ? element.language : 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Active">
<mat-header-cell *matHeaderCellDef> Active </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.active == true)? 'Yes': 'No'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail">
<mat-tab-group>
<!-- Program Tab and Table -->
<mat-tab label="Program">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Censored">
<mat-header-cell *matHeaderCellDef> Censored </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.censored != null) ? (element.censored ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Dubbed">
<mat-header-cell *matHeaderCellDef> Dubbed </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dubbed != null) ? (element.dubbed ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Music">
<mat-header-cell *matHeaderCellDef> Music </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.music != null) ? (element.music ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Effects">
<mat-header-cell *matHeaderCellDef> Effects </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.effects != null) ? (element.effects ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="DVS">
<mat-header-cell *matHeaderCellDef> DVS </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.dvs != null) ? (element.dvs ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="MOS">
<mat-header-cell *matHeaderCellDef> MOS </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.muteOrSilence != null) ? (element.muteOrSilence ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Voiceover">
<mat-header-cell *matHeaderCellDef> Voiceover </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.voiceOver != null) ? (element.voiceOver ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Rescore">
<mat-header-cell *matHeaderCellDef> Rescore </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.rescore != null) ? (element.rescore ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Contains Dialog">
<mat-header-cell *matHeaderCellDef> Contains Dialog </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dialog != null) ? (element.dialog ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="CALM Act">
<mat-header-cell *matHeaderCellDef> CALM Act Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.calmACT != null) ? (element.calmAct ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="EBUR128">
<mat-header-cell *matHeaderCellDef> EBUR128 Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.ebuR128 != null) ? (element.ebuR128 ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
<!--
Attributes Tab and Table -->
<mat-tab label="Attributes">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Loudness Level">
<mat-header-cell *matHeaderCellDef> Loudness Level </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudnessLevel != null) ? (element.loudnessLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Loudness Range">
<mat-header-cell *matHeaderCellDef> Loudness Range </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudness_Range != null) ? (element.loudness_Range ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Level (Max)">
<mat-header-cell *matHeaderCellDef> True Peak Level (Max) </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevel != null) ? (element.truePeakLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-L">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-L </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_L != null) ? (element.truePeakLevelChannel_L ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-R">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-R </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_R != null) ? (element.truePeakLevelChannel_R ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAttributeDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAttributeDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
</mat-tab-group>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAudioPrograms"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAudioPrograms;" class="element-row" matRipple
(click)="toggleRow(row)"></mat-row>
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[#detailExpand]="row.element.show ? 'expanded' : 'collapsed'" style="overflow: hidden"> </mat-row>
</mat-table>
<div class="container" *ngIf="materialAudioPrograms.length === 0 && (dataLoading === false | async)">
<p class="no-data-msg">No Audio Programs Available</p>
</div>
</div>
</ng-container>
Here' the component.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import {
MatTableModule,
MatTabsModule,
MatIconModule,
MatProgressSpinnerModule
} from '#angular/material';
#NgModule({
declarations: [MaterialAudioProgramsComponent],
imports: [CommonModule, MatProgressSpinnerModule, MatTableModule, MatTabsModule, MatIconModule],
entryComponents: [MaterialAudioProgramsComponent]
})
export class MaterialAudioProgramsModule {
// Define entry property to access entry component in lazy-loader service
static entry = MaterialAudioProgramsComponent;
}
And the component.spec.ts
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { MaterialMockModule } from '#content-platform/unit-test-helpers';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import { StoreModule } from '#ngrx/store';
import { RouterTestingModule } from '#angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '#angular/core';
import { MatTableModule } from '#angular/material';
describe('MaterialAudioProgramsComponent', () => {
let component: MaterialAudioProgramsComponent;
let fixture: ComponentFixture<MaterialAudioProgramsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MaterialAudioProgramsComponent],
imports: [MaterialMockModule, StoreModule.forRoot({}), RouterTestingModule, MatTableModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MaterialAudioProgramsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I am not able to figure out a solution for this,any help is greatly appreciated.
Thanks
Try removing MaterialMockModule and leaving MatTableModule. If I were you, I would take advantage of NO_ERRORS_SCHEMA and not import any module but you may need them (the child components) when doing integration tests.
Related
I would like to help me with updating records in a dynamic table worked with MatTable.
In my component get:
getComponentsFactualGeneral(pil: number){
this.diagnosticoService.readPilarParamFormId(pil).pipe().subscribe(pilar=>{
this.facgeneral = [];
(pilar as Pilari[]).forEach( getpilar => {
this.diagnosticoService.readComponentsPilarId(getpilar.idPilar).subscribe( comp=>{
getpilar.components = comp;
});
this.facgeneral.push(getpilar);
});
});
}
This generate the follow:
The html have:
<mat-tab>
<ng-template mat-tab-label>
<span (click)="getComponentsFormaGeneral(2)">Formal general</span>
</ng-template>
<mat-accordion class="example-headers-align" multi>
<mat-expansion-panel *ngFor="let item of fgeneral">
<mat-expansion-panel-header>
<mat-panel-title> {{ item.descripcion }} </mat-panel-title>
</mat-expansion-panel-header>
<div class="table-responsive">
<table mat-table [dataSource]="item.components" class="mat-elevation-z8"> <!-- here generate datasource -->
<ng-container matColumnDef="nombreestandar">
<th mat-header-cell *matHeaderCellDef> Estandar </th>
<td mat-cell *matCellDef="let element"> {{element.nombreestandar}} </td>
</ng-container>
<ng-container matColumnDef="nombrecomponente">
<th mat-header-cell *matHeaderCellDef> Componente </th>
<td mat-cell *matCellDef="let element"> {{element.nombrecomponente}} </td>
</ng-container>
<ng-container matColumnDef="evidencia">
<th mat-header-cell *matHeaderCellDef> Evidencia </th>
<td mat-cell *matCellDef="let element"> {{ element.evidencia }}
</td>
</ng-container>
<ng-container matColumnDef="conclusionCumple">
<th mat-header-cell *matHeaderCellDef> Cumplimiento </th>
<td mat-cell *matCellDef="let element"> {{ element.conclusionCumple }}
<ng-template #elseBlock>
No cumple
</ng-template>
</td>
</ng-container>
<ng-container matColumnDef="comentariosCc">
<th mat-header-cell *matHeaderCellDef> Comentario </th>
<td mat-cell *matCellDef="let element"> {{ element.comentariosCc }}
</td>
</ng-container>
<ng-container matColumnDef="comentariosComite">
<th mat-header-cell *matHeaderCellDef> Comentarios Comité </th>
<td mat-cell *matCellDef="let element"> {{ element.comentariosComite }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Acciones </th>
<td mat-cell *matCellDef="let e; let i=index;">
<button mat-icon-button color="primary" (click)="openDialog(e.idRespuestamacro,e.idEstandar,e.idFormulario,e.idInstitucion,e.idPilar,e.paramComponente)">
<mat-icon aria-label="Edit">edit</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</mat-expansion-panel>
</mat-accordion>
</mat-tab>
The generate datasource from components [dataSource]="item.components"
Now I need to update a row after edit, but how can I do it if datasource is generated from "item.components".
Thank you
For the dynamic table updates, definitely use --> RxJS
Step 1 -- In HTML Use an async pipe
Step 2 -- change fgeneral to fgeneral$ observable list, it will auto update from the observable.
Step 3 -- Make sure you import rxjs libraries
import { Component } from '#angular/core'
import { Observable } from 'rxjs'
Your HTML
<div *ngFor='let item of fgeneral$ | async' ... id="line_{{i}}" class="new-line glow">
// put your row template here...
</div>
In Your Component
// this Function will return an observable array. assign it to what you want..
// this is to give you an idea... a quick edit... will not compile..
public getComponentsFactualGeneral(): Observable<Fac[]> {
return this.http.get(this.baseUrl + '/xyz')
.pipe(
catchError(this.handleError('getFacs', []))
)
.map(fac => {
if(Array.isArray(fac )) {
return fac .map((item) => new Facs(item));
} else {
return [new Facs(fac )];
}
});
}
This ref should help.. and this
I'm new to Angular Material and have been implementing the MatTable by fetching the data from the Firebase (Real-time Database). For every Row of the MatTable, I need a button to delete the particular data of the row by its $key. The MatTable, fetching of data and the button shows up properly. Just when the button is clicked, it doesn't calls the delete function given in the click event.
Angular 8.2
Angular Material 8.2.3
Firebase 7.14.1
Typescript 3.5.3
HTML code for MatTable
<mat-table [dataSource]="listData" matSort>
<ng-container matColumnDef="key">
<mat-header-cell *matHeaderCellDef >key</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.$key}}</mat-cell>
</ng-container>
<ng-container matColumnDef="studentPRN">
<mat-header-cell *matHeaderCellDef >PRN</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveStudentPRN}}</mat-cell>
</ng-container>
<ng-container matColumnDef="studentName" >
<mat-header-cell *matHeaderCellDef >Name</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveStudentName}}</mat-cell>
</ng-container>
<ng-container matColumnDef="studentClass">
<mat-header-cell *matHeaderCellDef>Class</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveStudentClass}}</mat-cell>
</ng-container>
<ng-container matColumnDef="startDate">
<mat-header-cell *matHeaderCellDef mat-sort-header>Start Date</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveStartDate}}</mat-cell>
</ng-container>
<ng-container matColumnDef="endDate">
<mat-header-cell *matHeaderCellDef mat-sort-header>End Date</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveEndDate}}</mat-cell>
</ng-container>
<ng-container matColumnDef="leaveType">
<mat-header-cell *matHeaderCellDef>Type</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveType}}</mat-cell>
</ng-container>
<ng-container matColumnDef="leaveDescription">
<mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveDescription}}</mat-cell>
</ng-container>
<ng-container matColumnDef="leaveTeacher">
<mat-header-cell *matHeaderCellDef>Requested to</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveTeacher}}</mat-cell>
</ng-container>
<ng-container matColumnDef="leaveStatus">
<mat-header-cell *matHeaderCellDef>Status</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.leaveStatus}}</mat-cell>
</ng-container>
<ng-container matColumnDef="action">
<mat-header-cell *matHeaderCellDef>Action</mat-header-cell>
<mat-cell *matCellDef="let row">
<button mat-icon-button color="warn" (click)="withdrawLeave(row.$key)">
<mat-icon>delete_outline</mat-icon></button>
</mat-cell>
</ng-container>
<ng-container matColumnDef="loading">
<mat-footer-cell *matFooterCellDef colspan="6">Fetching data...</mat-footer-cell>
</ng-container>
<ng-container matColumnDef="noData">
<mat-footer-cell *matFooterCellDef colspan="6">No data found.</mat-footer-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayColumns"></mat-header-row>
<mat-row *matRowDef="let rows; columns: displayColumns;"></mat-row>
<mat-footer-row *matFooterRowDef="['noData']" [ngClass]="{'hide':listData!=null}"></mat-footer-row>
<mat-footer-row *matFooterRowDef="['loading']" [ngClass]="{'hide':!(listData!=null && listData.data.length==0)}"></mat-footer-row>
</mat-table>
<mat-paginator [pageSizeOptions]="[5,10,20,30]" [pageSize]="5" showFirstLastButtons></mat-paginator>
Typescript for button click event
withdrawLeave($key){
if(confirm("Do you want to withdraw the leave request?")){
this.auth.withdrawLeave($key);
}
Auth Service
leaveList : AngularFireList<any>;
getLeaves(){
this.leaveList = this.firebase.list('Leaves');
return this.leaveList.snapshotChanges();
}
withdrawLeave($key: string){
this.leaveList.remove($key);
}
Typescript for fetching data into MatTable
setAppliedLeaves(){
this.auth.getLeaves().subscribe(list => {
let array = list.map(item => {
return {
$key: item.key,
...item.payload.val()
};
});
this.listData = new MatTableDataSource(array);
this.listData.sort = this.sort;
this.listData.paginator = this.paginator;
});
}
I think the problem is the way you are sending the element to the function.
Here is an example of how I implemented a click - delete.
In the html file:
<ng-container matColumnDef="columndelete">
<th mat-header-cell *matHeaderCellDef>Delete</th>
<td mat-cell *matCellDef="let element">
<h5 (click)="delete(element)"><i class="fa fa-trash"></i>
</h5>
</td>
</ng-container>
and in the ts file:
delete(elm: Details) {
//logic
}
Details is the interface, the type of the element.
BTW, you might want to look at this link https://material.angular.io/components/table/overview,
instead of for example
<mat-table ...>
you should use
<table mat-table ...>
I'm working on a small attendance project and have some table component within some mat-tab components. When there is overflow from the table, it scrolls the full component, I only want it to scroll the table on the inner component.
I have tried adding "overflow: auto" in these sections:
on the (app-attendance-table) selector
inside table component
::ng-deep { .mat-tab-body { overflow: auto } }
on the (mat-tab-group) selector
wrapped (app-attendance-table) in an (ng-container) or (div) and adding overflow: auto
This is the outer component with tabs:
<ng-container>
<mat-form-field class="date">
<input
matInput
[matDatepicker]="picker"
placeholder="Select a Date"
(dateInput)="addEvent($event)"
[(ngModel)]="currentDate"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</ng-container>
<mat-tab-group>
<ng-container *ngFor="let grade of result; let i = index">
<mat-tab *ngIf="grade.length > 0" [label]="grades[i]">
<app-attendance-table [dataSource]="grade"></app-attendance-table>
</mat-tab>
</ng-container>
</mat-tab-group>
<div *ngIf="this.result.length < 1 && !this.loading">
No Records Found for The Date: {{ currentDate.toDateString() }}
</div>
<mat-spinner *ngIf="this.loading"> </mat-spinner>
</div>
This is the actual table component itself:
<mat-table #table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Student Name </mat-header-cell>
<mat-cell *matCellDef="let student"
><span
[ngClass]="{
absent: student.isAbsent() && !student.Reason,
finished: student.isAbsent() && student.Reason && !student.editing
}"
>
{{ student.Name }}
</span>
</mat-cell>
</ng-container>
<ng-container matColumnDef="grade">
<mat-header-cell *matHeaderCellDef> Student Grade </mat-header-cell>
<mat-cell
[ngClass]="{
absent: student.isAbsent() && !student.Reason,
finished: student.isAbsent() && student.Reason && !student.editing
}"
*matCellDef="let student"
>
{{ student.Grade }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="status">
<mat-header-cell *matHeaderCellDef> Status </mat-header-cell>
<mat-cell
[ngClass]="{
absent: student.isAbsent() && !student.Reason,
finished: student.isAbsent() && student.Reason && !student.editing
}"
*matCellDef="let student"
>
{{ student.Status }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="reason">
<mat-header-cell *matHeaderCellDef> Reason </mat-header-cell>
<mat-cell *matCellDef="let student">
<mat-form-field
class="reasons"
*ngIf="!student.isPresent()"
appearance="outline"
>
<mat-select
[(ngModel)]="student.Reason"
[disabled]="!student.editing"
placeholder="Select Reason"
(selectionChange)="makeChange(student)"
>
<mat-option
*ngFor="let reason of reasons; let i = index"
[value]="reason"
[disabled]="student.Reason === reason"
>
{{ reason }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-cell>
</ng-container>
<ng-container matColumnDef="comments">
<mat-header-cell *matHeaderCellDef> Comments </mat-header-cell>
<mat-cell *matCellDef="let student">
<mat-form-field *ngIf="!student.isPresent()">
<input
matInput
[(ngModel)]="student.Comments"
[disabled]="!student.editing"
(input)="makeChange(student)"
/>
</mat-form-field>
</mat-cell>
</ng-container>
<ng-container matColumnDef="edit">
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
<mat-cell *matCellDef="let student">
<button
*ngIf="!student.isPresent() && !student.editing"
mat-raised-button
color="primary"
(click)="startEditing(student)"
>
Edit
</button>
<button
*ngIf="!student.isPresent() && student.editing"
mat-raised-button
color="warn"
(click)="saveEdits(student)"
>
Finish
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</div>
To make sure the table is scrollable, wrap the table inside div with a fixed height and make its overflow: auto.
You can also check below links that has both page as well as table as scrollable.
https://angular-qfqpwr.stackblitz.io (Working Stackblitz)
https://material.angular.io/components/table/examples (Table with sticky header)
To extend on #Pankaj Prakash's answer, you should set the overflow property on your outer container to overflow: hidden.
Then wrap a div around the mat cells and set the property to overflow: scroll. If you only want a vertical scroll you could also set the following property instead: overflow-y: scroll
Big Picture
Hi,
I'm trying to use Angular Material in Angular 7.
I have to use a Material table with some data on it. My question is: Is there a way to change a MatCell content on click?
For example, consider the table in HTML above, with people's name, age and phone.
Angular Material Table HTML
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Phone">
</mat-form-field>
<mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort >
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
<mat-cell *matCellDef="let person" >{{person.name}}</mat-cell>
</ng-container>
<ng-container matColumnDef="age">
<mat-header-cell *matHeaderCellDef mat-sort-header>Age</mat-header-cell>
<mat-cell *matCellDef="let person">{{person.age}}</mat-cell>
</ng-container>
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef mat-sort-header>Phone Number</mat-header-cell>
<mat-cell *matCellDef="let person">{{person.phone}}</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 [length]="100" [pageSize]="5" [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
What works
Table to diplay data, pagination, filter and sorting headers are working great!
What I need
I want to do is when I click in the phone cell, insted of a text, a <input> tag takes {{person.phone}} place with the current number, allowing me to change the phone number and save the new one.
like this:
Desired MatCell content on click
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef mat-sort-header>Phone Number</mat-header-cell>
<mat-cell *matCellDef="let person">
<input matInput [value]="person.phone"(focusout)="changePhone($event.target.value, person)" placeholder="Phone">
</mat-cell>
</ng-container>
Can anyone help me please?
You can use *ngIf with some kind of flag for the selected phone number to swap between the two. So for example, if you have something like this in your component:
selectedPerson:Person;
...you could have something like this in your template:
...
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef mat-sort-header>Phone Number</mat-header-cell>
<mat-cell *matCellDef="let person">
<span *ngIf="selectedPerson !== person" (click)="selectedPerson = person">
{{person.phone}}
</span>
<input *ngIf="selectedPerson === person" matInput [value]="person.phone"(focusout)="changePhone($event.target.value, person)" placeholder="Phone">
</mat-cell>
</ng-container>
...
I have 6 column ie.. name, email, phone, company, status_1 and status_2.
status_1 has two option , ie.. "1" or "0"
status_2 also has two option , ie.. "1" or "0"
My Requirement : I need to change the color of the row
logic :
if(status_1 is "1" -> change to red)
else if (status_2 is "1" -> change to green)
else if (status_1 and status_2 is "1" -> give priority to status_1)
Mycode :
<div>
<mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef> E-Mail </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.email}} </mat-cell>
</ng-container>
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef> Phone </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.phone}} </mat-cell>
</ng-container>
<ng-container matColumnDef="company">
<mat-header-cell *matHeaderCellDef> Company </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.company.name}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</div>
I will display 4 columns , and colour of the row should be changed according to status
please help me
thank you.
Make the following change
html
change mat-row as
<mat-row *matRowDef="let row; columns: displayedColumns;" [ngStyle]="{'background-color': getRowColor(row)}"></mat-row>
ts
getRowColor(row){
if (row.status_1 === '1') {
return "red";
} else if (row.status_2 === '1') {
return "green";
}
}
You need to use [ngStyle] to specify your conditions. Assuming the entire listing you gave is your row, add the directive to your outermost <div>, like this:
<div [ngStyle]="{ 'background-color': status_1 === '1' ? 'red' : (status_2 === '1' ? 'green' : 'auto') }">
<mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef> E-Mail </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.email}} </mat-cell>
</ng-container>
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef> Phone </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.phone}} </mat-cell>
</ng-container>
<ng-container matColumnDef="company">
<mat-header-cell *matHeaderCellDef> Company </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.company.name}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</div>
I would argue the expression should be in a function, which will be nicer to read and easier to maintain. Given
getColor() {
if (status_1 === '1') {
return 'red';
} else if (status_2 === '1') {
return 'green';
}
}
You can change your <div> to use:
<div [ngStyle]="{ 'background-color': getColor() }">