Search code examples
angularmat-select

Angular Dropdown not Rendering


Seems im having another issue of data not displaying for my mat-select on Muscle Group Name, even though my API call is pulling the database to my component. Im sure whatever i am missing is probably pretty simple so hopefully someone can spot it.

Im aware of the other issues in the code such as my form and a few other things right now just trying to get the data to display.

HTML:

<p>add-exercise works!</p>

<form class="contentContainer" [formGroup]="editFieldsForm" (ngSubmit)="submitEdit()">
    <div fxLayout="column">
        <div fxLayout="row"
            fxLayoutGap="20px"
            fxlaypitAlign="space-between"
            fxjLayout.sm="column"
            fxLayout.xs="column">
            <div fxFlex="1 1 auto">
                <mat-form-field class="fullWidthField">
                    <mat-label>Exercise Name</mat-label>
                    <input type="text" matInput formControlName="exerciseName" required/> 
                </mat-form-field>
            </div>

            <div fxFlex="1 1 auto">
                <mat-form-field appearance="fill">
                    <mat-label>Muscle Group</mat-label>
                    <mat-select>
                    <mat-option *ngFor="let muscleGroup of muscleGroups" [Value]="muscleGroup">
                        {{muscleGroup}}
                    </mat-option>
                    </mat-select>
                </mat-form-field>
            </div>

        </div>
    </div>
    <div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="25px">
        <div>
            <button type="submit" mat-raised-button [disabled]="editFieldsForm.invalid">Submit</button>
        </div>
        <div>
            <button type="button" mat-raised-button (click)="cancelEdit()">Cancel</button>
        </div>
    </div>
</form>

TS

import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { finalize } from 'rxjs/operators';

import { IExercises } from 'src/app/models/IExcercises';
import { IMuscleGroups } from 'src/app/models/IMuscleGroups';
import { WorkoutService } from 'src/app/services/workout.services';

@Component({
  selector: 'app-add-exercise',
  templateUrl: './add-exercise.component.html',
  styleUrls: ['./add-exercise.component.css']
})
export class AddExerciseComponent implements OnInit {

  @Input() exerciseData: IExercises;
  @Output() editClosed = new EventEmitter();
  @Output() recordUpdated = new EventEmitter<boolean>();
  saving = false;
  updatedexerciseData: IExercises;
  errorMessage = '';
  isLoadingData = false;

  muscleGroup = {} as IMuscleGroups[];

  editFieldsForm: FormGroup;

  constructor(private workoutService: WorkoutService) {   }

  ngOnInit() {
    this.loadDropdown();

    this.editFieldsForm = new FormGroup({
      exerciseName: new FormControl(this.exerciseData.exerciseName, Validators.compose([Validators.required, Validators.maxLength(40)]))
    });
   }

  submitEdit() {
    this.saving = true;
    try {
      this.updatedexerciseData = this.updatedexerciseData;
      this.updatedexerciseData.exerciseId = -1;
      this.updatedexerciseData.exerciseName = this.editFieldsForm.get('exerciseName').value;
      //this.updatedexerciseData.muscleGroupId = getMuscleGroupId(this.editFieldsForm.get('muscleGroupName').value);
    }
    catch (error) {
      console.error(error);
      this.errorMessage = 'An error prevented the record from being submitted.';
      this.saving = false;
      return;
    } 

    this.workoutService.updateExercises(this.updatedexerciseData)
    .pipe(
      finalize(() => { this.saving = false })
    )
    .subscribe(
      () => this.completeFormSubmission(),
      (error: Error) => this.errorMessage = error.message
    );
  }

  completeFormSubmission() {
    this.editClosed.emit();
    this.recordUpdated.emit(true);
  }

  cancelEdit() {
    this.editClosed.emit();
  }

  loadDropdown() {
    this.workoutService.getMuscleGroups().pipe(
      finalize(() => this.isLoadingData = false)
    )
      .subscribe((muscleGroupsData: IMuscleGroups[]) => {
        this.muscleGroup = muscleGroupsData;
        console.log(this.muscleGroup);
      },
        (error: Error) => this.errorMessage = error.message);
    }
}

Solution

  • Issue 1: Wrong naming for muscleGroups

    As the explanation in the comment, in HTML you are iterate muscleGroups for generating <mat-option>.

    <mat-option *ngFor="let muscleGroup of muscleGroups" [Value]="muscleGroup">
      {{muscleGroup}}
    </mat-option>
    

    But muscleGroups variable was not found in AddExerciseComponent. And you name the variable as muscleGroup.


    Issue 2: [Value] is not a valid Input property for <mat-option>

    You will get this error message for using [Value] in <mat-option>:

    Can't bind to 'Value' since it isn't a known property of 'mat-option'.


    Solution

    1. Rename muscleGroup to muscleGroups in AddExerciseComponent.
    2. Change to [value] for <mat-option>.
    3. (Optional) Not to display with {{muscleGroup}}. This will display [object Object] in HTML. Either display specify property from muscleGroup or use json pipe to display full object.

    add-exercise.component.ts

    <mat-option *ngFor="let muscleGroup of muscleGroups" [value]="muscleGroup">
      {{muscleGroup | json}}
    </mat-option>
    

    add-exercise.component.ts

    export class AddExerciseComponent implements OnInit {
    
      ...
    
      muscleGroups = {} as IMuscleGroups[];
    
      loadDropdown() {
        this.workoutService
          .getMuscleGroups()
          .pipe(finalize(() => (this.isLoadingData = false)))
          .subscribe(
            (muscleGroupsData: IMuscleGroups[]) => {
              this.muscleGroups = muscleGroupsData;
              console.log(this.muscleGroups);
            },
            (error: Error) => (this.errorMessage = error.message)
          );
      }
    }
    

    Sample Solution on StackBlitz