Issues with setting initial value for ion-select

I am passing some data to a modal window in my Ionic/Angular app, but I can not get the select option to be pre-selected based on the data.

My data that is passed in is structured like this:

this.data = {
  albums: ["39902"],
  user_id: "49"
}

This is some mock data that comes from my API:

{
    "albums": [{
        "album": [{
            "albumID": "27508",
            "userID": "49",
            "albumName": "Marvel Comics",
            "count": "123"
        }]
    }, {
        "album": [{
            "albumID": "27509",
            "userID": "49",
            "albumName": "Baseball",
            "count": "77"
        }]
    }, {
        "album": [{
            "albumID": "33670",
            "userID": "49",
            "albumName": "Basketball",
            "count": "4"
        }]
    }, {
        "album": [{
            "albumID": "39902",
            "userID": "49",
            "albumName": "Football",
            "count": "331"
        }]
    }, {
        "album": [{
            "albumID": "39968",
            "userID": "49",
            "albumName": "Hockey",
            "count": "1,552"
        }]
    }, {
        "album": [{
            "albumID": "40585",
            "userID": "49",
            "albumName": "Test Album Name",
            "count": "0"
        }]
    }]
}

In my modal, I call my API service to get the data like this:

this.api.getAlbums().subscribe(response => {
  this.albums = response['albums'];
});

In my template, I try and display the selected values like this:

<ion-item lines="full">
      <ion-label class="ion-text-wrap">Album</ion-label>
      <ion-select multiple="true" [(ngModel)]="data.albums">
        <ion-select-option *ngFor="let item of albums" [value]="item.album[0].albumID">{{item.album[0].albumName}}</ion-select-option>
      </ion-select>
    </ion-item>

When the page loads, the value for the drop down is not selected, however, if I click on the drop down, then the correct value is selected.

Is there a way to get the selected value from this.data to appear selected in the dropdown?

Bonus points for anyone who can also help remove those silly ellipses from the drop down’s in Ionic.

I also wanted note, that if I set this.albums statically, and do not grab the data from my API service, then the item is correctly selected when the page loads.

I am confused by the presence of both data and albums. Can we simplify the code so that only one of those properties exists in the controller?

My apologies for my original post not being more clear. I will see if I can try and clear up some of the confusion.

data is passed in from the parent of the modal. It represents the metadata for the image that is going to be edited. One of items that can be edited is which album the image belongs to. In data there is an albums array that may have one (or more) album IDs associated to it.

albums is retrieved from my API service, and returns an array of all of the users albums. The idea being that the ion-select would have initial value selected if any an album ID in data.albums matched any of the album IDs in albums.album[0].albumID.

I guess given what you’ve said, probably the least disruptive way to deal with this would be to inject a ChangeDetectorRef in the constructor of your modal and call its detectChanges method once albums has been populated from the API call.

From your perspective, what would be the preferred or best approach for dealing with this?

The first thing I would do is to lighten the load off your poor components by massaging this data into a more easily-digested format:

album.service.ts

export interface Album {
  id: string;
  userId: string;
  name: string;
  count: number;
}

interface WiredAlbum {
  albumID: string;
  userID: string;
  albumName: string;
  count: string;
}

interface WiredAlbumSet {
  albums: { album: WiredAlbum[] }[];
}

@Injectable()
export class AlbumService {
  private mockData: WiredAlbumSet = {
    albums: [{
      album: [{
        albumID: "27508",
        userID: "49",
        albumName: "Marvel Comics",
        count: "123"
      }]
    }, {
      album: [{
        albumID: "27509",
        userID: "49",
        albumName: "Baseball",
        count: "77"
      }]
    }, {
      album: [{
        albumID: "33670",
        userID: "49",
        albumName: "Basketball",
        count: "4"
      }]
    }, {
      album: [{
        albumID: "39902",
        userID: "49",
        albumName: "Football",
        count: "331"
      }]
    }, {
      album: [{
        albumID: "39968",
        userID: "49",
        albumName: "Hockey",
        count: "1,552"
      }]
    }, {
      album: [{
        albumID: "40585",
        userID: "49",
        albumName: "Test Album Name",
        count: "0"
      }]
    }]
  };

  private unwireAlbum(wa: WiredAlbum): Album {
    return {
      id: wa.albumID,
      userId: wa.userID,
      name: wa.albumName,
      count: +wa.count,
    };
  }

  private unwireAlbumSet(aset: WiredAlbumSet): Album[] {
    return aset.albums.map(wa => this.unwireAlbum(wa.album[0]));
  }

  albumsByUser(uid: string): Observable<Album[]> {
    return of(this.mockData).pipe(
      delay(2000),
      map(was => this.unwireAlbumSet(was))
    );
  }
}

albumsByUser uses the data from your post, and the 2 second delay is there to simulate a network request so you can watch the other stuff do its job. I’m throwing the rest of your UI into my app component, but the same logic should work anywhere. I tried to mimic your HTML closely as well, so you shouldn’t have too much to change.

app.component.html

<ion-app>
  <ion-content>
    <ion-item lines="full">
      <ion-label class="ion-text-wrap">Album</ion-label>
      <ion-select multiple="true" [(ngModel)]="selectedAlbumIds" [disabled]="loading">
        <ion-select-option *ngFor="let album of allAlbums"
                           [value]="album.id">{{album.name}}</ion-select-option>
      </ion-select>
    </ion-item>
    <ion-item *ngIf="loading">
      <ion-progress-bar type="indeterminate"></ion-progress-bar>
    </ion-item>
    <ion-item>
      <ion-label>{{selectedAlbumIds | json}}</ion-label>
    </ion-item>
  </ion-content>
</ion-app>

The basic idea is that we can’t reasonably present the select until we know what all the choices can be, so we disable it while the request for all available albums for this user is pending, along with a progress bar for fun. A third item shows the selection for easy debugging.

app.component.ts

export class AppComponent {
  userId = "49";
  selectedAlbumIds = ["39902"];
  allAlbums: Album[] = [];
  loading = false;

  constructor(private albumer: AlbumService) {
    this.loading = true;
    this.albumer.albumsByUser(this.userId)
      .pipe(finalize(() => this.loading = false))
      .subscribe(albums => this.allAlbums = albums);
  }
}

If the userId property is changing, the logic that is in the constructor could be moved elsewhere (like ngOnChanges) to reflect that.

When I run this, I see “Football” populated as soon as the select is ready, and changes I make to the selection are reflected in the third item.

Does this work like you are desiring?