简体   繁体   中英

Angular 6 material data table with radio buttons

I have an Angular 6 material data table, and within each row for the table I have a group of radio buttons. An event is fired to make an API call when a radio button is selected for a particular row. All works well to this point. However, if the data table has more than one page and I navigate to the second page, and then return to the first page, the radio button that is shown as selected is the one that was originally selected when the page first loaded; not the updated selection. When I refresh the page, the data reloads, and the correct radio button is selected (the API call successfully updates the database). Not sure if that's making sense. Here's an example of a row in the table:

在此处输入图片说明

When the page first loads, the Intermediate option is selected in this group of radio buttons. If I were to select the Beginner option, the radio button for Beginner is selected as expected, and the value is successfully saved to the database via an API call. When I navigate to page two, then come back to page one, the radio button selected is for Intermediate, not Beginner. It seems that the state is being lost when I go to page 2.

I removed the (change) and [checked] attributes from the radio buttons to see what would happen. At that point no options were selected when the page first loaded. When I made a selection, navigated to page 2, then returned back to page 1, the radio buttons were back in their original state (none of them selected). I also changed the radio buttons to native HTML radio buttons, and I saw the same behavior. I see no errors in the console.

Here's my component html

<div class="mat-elevation-z8">
  <table id="dataTable" mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="name">
      <th class="headerCell" mat-header-cell *matHeaderCellDef>Technology</th>
      <td style="width: 375px;" mat-cell *matCellDef="let data">{{data.name}}</td>
      <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <ng-container matColumnDef="none">
        <th class="headerCell" mat-header-cell *matHeaderCellDef>None</th>
        <td class="radioButtonCell" mat-cell *matCellDef="let data">
          <mat-radio-button [name]="data.id" value="1" [checked]="data.level == 0 || data.level == 1 || evaluations.length == 0" (change)="saveDataLevel($event, data.dataDescription.dataGroupId)"></mat-radio-button>
        </td>
        <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <ng-container matColumnDef="beginner">
        <th class="headerCell" mat-header-cell *matHeaderCellDef>Beginner</th>
        <td class="radioButtonCell" mat-cell *matCellDef="let data">
          <mat-radio-button [name]="data.id" value="2" [checked]="data.level == 2" (change)="saveDataLevel($event, data.dataDescription.dataGroupId)"></mat-radio-button>
          <div class="infoSubscript"><img class="icon" src="/src/assets/open-iconic/svg/info.svg" alt="Information" (click)="openDialog(data.name, data.dataDescription.dataGroupId, 2)" /></div>
        </td>
        <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <ng-container matColumnDef="intermediate">
        <th class="headerCell" mat-header-cell *matHeaderCellDef>Intermediate</th>
        <td class="radioButtonCell" mat-cell *matCellDef="let data">
          <mat-radio-button [name]="data.id" value="3" [checked]="data.level == 3" (change)="saveDataLevel($event, data.dataDescription.dataGroupId)"></mat-radio-button>
          <div class="infoSubscript"><img class="icon" src="/src/assets/open-iconic/svg/info.svg" alt="Information" (click)="openDialog(data.name, data.dataDescription.dataGroupId, 3)" /></div>
        </td>
        <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <ng-container matColumnDef="expert">
        <th class="headerCell" mat-header-cell *matHeaderCellDef>Advanced</th>
        <td class="radioButtonCell" mat-cell *matCellDef="let data">
            <mat-radio-button [name]="data.id" value="4" [checked]="data.level == 4" (change)="saveDataLevel($event, data.dataDescription.dataGroupId)"></mat-radio-button>
            <div class="infoSubscript"><img class="icon" src="/src/assets/open-iconic/svg/info.svg" alt="Information" (click)="openDialog(data.name, data.dataDescription.dataGroupId, 4)" /></div>
        </td>
        <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <ng-container matColumnDef="description">
        <th class="headerCell" mat-header-cell *matHeaderCellDef>Description</th>
        <td mat-cell *matCellDef="let data">{{data.dataDescription.description}}</td>
        <td mat-footer-cell *matFooterCellDef>&nbsp;</td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
    <tr mat-footer-row *matFooterRowDef="displayedColumns"></tr>
  </table>

  <mat-paginator [pageSizeOptions]="[10, 20, 30]" showFirstLastButtons></mat-paginator>
</div>

Here's my component ts file

export class TestComponent implements OnInit {
    displayedColumns: string[] = ['name', 'none', 'beginner', 'intermediate', 'advanced', 'description'];
    dataSource: MatTableDataSource<IData>;
    assessments: IAssessment[];
    dataGroupId: string = "";
    dataGroupName: string = "";

    @ViewChild(MatPaginator) paginator: MatPaginator;

    constructor(private dataService: Data, private route: ActivatedRoute, private snackBar: MatSnackBar, private dialog: MatDialog) { }

    ngOnInit() {
      this.route.queryParams.subscribe(params => {
        this.dataGroupId = params["data"];
        this.dataGroupName = params["group"];

        if (this.dataGroupId && this.dataGroupName) {
          this.getDataForGroup();
        }
      });
    }

    getDataForGroup() {
      this.dataSource = null;
      this.dataService.getDataForGroup(this.dataGroupId)
      .subscribe(s => {
        this.assessments = s.assessments;
        this.dataSource = new MatTableDataSource<IData>(s.data);
        this.paginator.firstPage();
        this.dataSource.paginator = this.paginator;
      });
    }

    saveDataLevel(event, dgrId) {
      var dataId = event.source.name;
      var level = event.source.value;
      this.dataService.saveDataLevel(dataId, level, dgrId)
        .subscribe(data => {
          let message = data ? "Data level successfully saved." : "Failed to save data level. Please try again.";
          this.snackBar.open(message, '', {
            duration: 2000,
            horizontalPosition: 'right',
            verticalPosition: 'top'
          });
        });
    }

    openDialog(dataName, dgrId, levelId) {
      this.dataService.getDataDescription(dgrId, levelId)
        .subscribe(rd => {
          this.dialog.open(LevelDescDialog, {
            width: '600px',
            data: { 
              name: dataName,
              levelDescription: rd 
            }
          });
      });
    }
  }

Edit: Here is a StackBlitz URL with a similar setup: https://stackblitz.com/edit/angular-s2akjp . Behavior is consistent.

That's because you're not saving the change on the RadioButton to the list.

To save the change, add (change)="setAssessmentLevel(assessment, 1)" to each of the radio buttons.

And then add a setAssessmentLevel(assessment, level) method to your Component Class that will persist the level value to your list:

setAssessmentLevel(assessment, value) {
  this.assessments[this.assessments.indexOf(assessment)] = {
    ...assessment,
    level: value
  };
}

Here's a Sample StackBlitz for your ref.

You need to set the value in the array, pass the assessment and the event to a method and set the value.

Add (change)="setValue(assessment, $event.value)" to each of your mat-radio-buttons

<mat-radio-button (change)="setValue(assessment, $event.value)" [name]="assessment.id" value="1" [checked]="assessment.level == 1"></mat-radio-button>

Add this method to component

setValue(assessment,assessmentLevel){
    assessment.level = assessmentLevel
  }

The question has been already answered.

My answer is for singular usage: in order to make it possible to only one radio-button is selected, remove the radio-button-group tag and use it alone, as follows:

<mat-radio-button [value]="row"></mat-radio-button>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM