7.9: Setting Main Photo in Angular

Adding setMainPhoto() Service Method

In user.service.ts, add a new method called setMainPhoto():

setMainPhoto(userId: number, id: number) {
    return this._http.post(this.baseUrl + 'users/' + userId + '/photos/' + id + '/setMain', {});
}

Adding setMainPhoto() Method to PhotoEditorComponent

In photo-editor.component.ts add a method to use the service above:

setMainPhoto(photo: Photo) {
    this._userService.setMainPhoto(this._authService.decodedToken.nameid, photo.id).subscribe(() => {
      console.log('successfully set to main');
    }, error => {
      this._alertify.error(error);
    });
}

Use the Method in the Template

Next, we'll use our new methods in the photo-editor.component.html template:

<div class="row">
  <div class="col-md-2" *ngFor="let photo of photos">
    <img class="thumbnail" src="{{ photo.url }}" alt="{{ photo.description }}">
    <div class="row justify-content-center">
      <button 
        type="button"
        class="btn btn-sm btn-secondary"
        (click)="setMainPhoto(photo)"
        [ngClass]="photo.isMain ? 'btn-secondary active' : 'btn-default'"
        [disabled]="photo.isMain">Main</button>
      <button type="button" class="btn btn-sm btn-warning"><i class="fa fa-trash-o"></i></button>
    </div>
  </div>
</div>

We added a few things here.

  1. (click)="setMainPhoto(photo) - to trigger our method on a click event

  2. [ngClass]="photo.isMain ? 'btn-secondary active' : 'btn-default'" - to toggle classes based on whether or not a photo is the main photo.

  3. [disabled]="photo.isMain" - to disable the button if the photo is already the main photo.

Our button should "work" now. However, if a user clicks the Main button - it won't be immediately reflected in the app. A user will have to reload the page to see the changes. This isn't ideal. We'll fix this next.

Dynamically Updating Main Photo

We need to do something similar to what we're already doing in our API on our frontend to update the photo array without having to refresh the page.

We'll use a JavaScript library called UnderscoreJS to make the array manipulation much more simple. It's probably not a perfect analogy, but Underscore provides similar functionality to LINQ when working with collections.

npm install underscore --save
npm install @types/underscore --save-dev

In photo-editor.component.ts, make the following changes:

import * as _ from 'underscore';

// ...
export class PhotoEditorComponent implements OnInit {
    currentMain: Photo;

//  ...

setMainPhoto(photo: Photo) {
    this._userService.setMainPhoto(this._authService.decodedToken.nameid, photo.id).subscribe(() => {
      this.currentMain = _.findWhere(this.photos, { isMain: true });        //  1.
      this.currentMain.isMain = false;                                      //  2.
      photo.isMain = true;                                                  //  3.
    }, error => {
      this._alertify.error(error);
    });
}

In the setMainPhoto() method - we're doing the same thing as our API - except we're working with the Photos[] in our component.

  1. Finding the photo in our photos array that is set to isMain

  2. Setting the photo currently set to main to false

  3. Setting the photo the user clicked on to main.

We're making progress! If we click the Main button - we see our selection being applied on the buttons immediately. However, we're still not seeing the Photo changing in the profile card on the left. This involves passing data from a child component (PhotoEditorComponent) to a parent component (MemberEditComponent) we'll work on that next.

Updating Profile Picture Dynamically

First, let's add an Output property that is an EventEmitter to the photo-editor.component.ts class:

@Output() getMemberPhotoChange = new EventEmitter<string>();

Now, when we call the setMainPhoto() method, we need to "emit" the photoUrl:

setMainPhoto(photo: Photo) {
    this._userService.setMainPhoto(this._authService.decodedToken.nameid, photo.id).subscribe(() => {
      this.currentMain = _.findWhere(this.photos, { isMain: true });
      this.currentMain.isMain = false;
      photo.isMain = true;
      this.getMemberPhotoChange.emit(photo.url);                //  <--- Added
    }, error => {
      this._alertify.error(error);
    });
}

Now, we can use this output property and event emitter in the parent component: MemberEditComponent:

In member-edit.component.html add this to the <app-photo-editor> element.

<app-photo-editor [photos]="user.photos" (getMemberPhotoChange)="updateMainPhoto($event)"></app-photo-editor>

Now we need to implement the updateMainPhoto() method in our member-edit.component.ts file:

updateMainPhoto(photoUrl) {
    this.user.photoUrl = photoUrl;
}

Test this in your Angular application. Log in as a user with multiple photos and try switching the main photos. The profile picture in the panel on the left should update as well now!

Last updated