Uploading the avatar image at creation time

As we have seen so far, to upload and attach a file to a resource, it must already exist. How we can create a resource with a file attached? How can we create a contact that includes an avatar image?

To do so, we will need to create the resource in two steps. In the first step, we create the resource itself, and then in a second step we can upload all files we want to that resource. Yes, it's not possible to do this in a single server connection, at least without encoding the files you want to send:

Uploading the avatar image at creation time

Figure 5.4 Create contact process

The preceding figure shows how the process is done. Note that the model is responsible for handling these connections while the controller orchestrates the order of the communication and error handling. As we have seen previously, the ContactEditor triggers several events that the view can use to show to the user what's happening.

The views can be left as is; we should only modify the ContactEditor controller by changing how the saveContact() method behaves. However, we want to keep the feature of uploading the image as the user makes the selection. If the Contact model is new, this feature will break the application because no valid endpoint exists to upload the avatar:

class ContactEditor {
// ...

showEditor(contact) {
    // ...

    // When avatar is selected, we can save it inmediatly if the
    // contact already exists on the server, otherwise just
    // remember the file selected
this.listenTo(contactPreview, 'avatar:selected', blob => {
this.avatarSelected = blob;

      if (!contact.isNew()) {
this.uploadAvatar(contact);
      }
    });
  }
}

When an avatar is selected, instead of immediately uploading the file to the server, we check if the contact is new or not. If the model is not new, we can perform the upload by calling the uploadAvatar()method; otherwise, we keep a reference to the blob object in the avatarSelected attribute that the uploadAvatar() method will use when it is called.

The saveContact() method is responsible for orchestrating the algorithm described in the previous figure:

// apps/contacts/contactEditor.js
class ContactEditor {
saveContact(contact) {
varphonesData = this.phones.toJSON();
varemailsData = this.emails.toJSON();

contact.set({
      phones: phonesData,
      emails: emailsData
    });

    if (!contact.isValid(true)) {
      return;
    }

varwasNew = contact.isNew();

    // The avatar attribute is read-only
    if (contact.has('avatar')) {
contact.unset('avatar');
    }

    function notifyAndRedirect() {
      // Redirect user to contact list after save
App.notifySuccess('Contact saved');
App.router.navigate('contacts', true);
    }

contact.save(null, {
      success: () =>{
        // If we are not creating an user it's done
        if (!wasNew) {
notifyAndRedirect();
          return;
        }

        // On user creation send the avatar to the server too
this.uploadAvatar(contact, {
          success: notifyAndRedirect
        });
      },
error() {
        // Show error message if something goes wrong
App.notifyError('Something goes wrong');
      }
    });
  }
  // ...
}

Before calling the save() method in the Contact model, it's necessary to save whether the model is new or not; if we call this method after the save, the isNew() method will always return true.

If the model wasn't new, then any changes in the avatar image were already uploaded by the 'avatar:selected' event handler, so we don't need to upload the image again. But if the image was new, then we should upload the avatar by calling the uploadAvatar() method; note that the method accepts an options object to register callbacks. This is necessary to provide feedback to the user; when the upload is done it calls the notifyAndRedirect() function to show a notification message and returns to the list of contacts.

We will need to change the implementation of uploadAvatar() to include the callbacks described earlier and to instead receive the blob as soon as it uses the avatarSelected attribute:

// apps/contacts/contactEditor.js
uploadAvatar(contact, options) {
  // Tell to others that upload will start
this.trigger('avatar:uploading:start');

contact.uploadAvatar(this.avatarSelected, {
    progress: (length, uploaded, percent) => {
      // Tell to others that upload is in progress
this.trigger('avatar:uploading:progress',
                   length, uploaded, percent);
      if (options &&_.isFunction(options.success)) {
options.success();
      }
    },
    success: () => {
      // Tell to others that upload was done successfully
this.trigger('avatar:uploading:done');
    },
    error: err => {
      // Tell to others that upload was error
this.trigger('avatar:uploading:error', err);
    }
  });
}

The method is basically the same; we just add the options callbacks and change the source of the blob object.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.228.138