[Fixed] HTML Dropdown disappears after a value is set to null in Angular

Issue

I have two values power and mainPower which describe the same thing but mainPower saves an id of type Long in the backend and power saves all attributes of this dto. With the code below I can update a hero’s power, meaning I can change it from something to the other and also make it null (meaning no power).

The problem is that when a power is set to null or it was null before, the dropdown menu disappears. I don’t want the dropdown to disappear, I just want it to say "no power" when it is null and I should be able to change it from null to something.

hero-detail.component.html

<form #heroUpdateForm = "ngForm">
      {{diagnostic}}
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name"
               required
               *ngIf="hero" [(ngModel)]="hero.name" name="name">
      </div>

      <div class="form-group">
        <label for="desc">Description</label>
        <input type="text"  class="form-control" id="desc"
              *ngIf="hero" [(ngModel)]="hero.desc" name="desc">
      </div>

      <div class="form-group" *ngIf="hero?.power">
        <label for="power">Main Power</label>
        <select class="form-control"  id="power"
                required
                [(ngModel)]="hero.mainPower" name="power">
          <option [ngValue]="null">no power</option>
          <ng-container *ngFor="let power of powers">
            <option [value]="power.id">{{power.name}}</option>
          </ng-container>
        </select>
      </div>
    </form>

hero-detail.component.ts

export class HeroDetailComponent implements OnInit {
  public heroes: Hero[];
  public powers: Power[];
  submitted = false;
  private hero: Hero;

  constructor(
    private route: ActivatedRoute,
    private heroService: HeroService,
    private powerService: PowerService,
    private location: Location,
    private router: Router
  ) {}

  ngOnInit(): void {
    this.getHero();
    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
    this.getHeroes();
    this.getPowers();
  }

  getHero(): void{
    const id = +this.route.snapshot.paramMap.get('id');
    this.heroService.getHero(id).subscribe(
      (hero: Hero) => {
        this.hero = hero;
        if (hero.power != null) {
          this.powerService.getPowerById(hero.power).subscribe(
            (powerResponse) => {
              hero.power = powerResponse;
            }
          );
        }
      });
  }
  public getHeroes(): void {
    this.heroService.getHeroes().subscribe(
      (response: Hero[]) => {
        this.hero = response;
        },
      (error: HttpErrorResponse) => {
        alert(error.message);
      }
    );
  }

  public getPowers(): void {
    this.powerService.getAllPowers().subscribe(
      (response: Power[]) => {
        this.power = response;
      },
      (error: HttpErrorResponse) => {
        alert(error.message);
      }
    );
  }

  goBack(): void {
    this.location.back();
  }

  save(): void {
    this.heroService.updateHero(this.hero, this.hero.id)
      .subscribe(() => this.goBack());
  }
  onSubmit() {this.submitted = true;}

  get diagnostic() {return JSON.stringify(this.hero);}

Solution

Is obvious why it’ll disappear, look at your template again:

       <div class="form-group" *ngIf="hero?.power"> <!-- lookie here, if either hero or power is null this element along with everything in it will be gone!! -->
         <label for="power">Main Power</label>
         <select class="form-control"  id="power"    <!-- your select is inside the div above with the ng if -->
                required
                [(ngModel)]="hero.mainPower" name="power">
         <option [ngValue]="null">no power</option>
         <ng-container *ngFor="let power of powers">
           <option [value]="power.id">{{power.name}}</option>
         </ng-container>
       </select>

essentially the ngIf you have in the div surrounding the select element says the following:

  • if(ngIf) hero and power -> display element
  • if(ngIf) no hero and power -> remove element

You might not want to have *ngIf="hero?.power" perhaps you should just check if there is a hero *ngIf="hero" instead

Leave a Reply

(*) Required, Your email will not be published