Q:

Office.js appointment is still marked as changed in saveAsync callback

I use the Office javascript API to build an Outlook add-in.  I have a problem with some code that saves an appointment and then closes the appointment window.  I would expect that there is no unsaved changes after saving the appointment through the API, but I still get the "Discard changes" confirm dialog box after running the code.  The problem only appears when editing an existing appointment and when adding custom properties to the appointment before saving it. 
Here is the important part of my (typescript) code:
private saveAppointmentAndSetProperty(appointment: any): Observable<any> {
    const subject = new Subject<any>();

Office.context.mailbox.item.loadCustomPropertiesAsync(
        result => {
            if (this.isErrorResult(result)) {
                subject.error(result.error.message);
            } else {
                const properties = result.value;
                properties.set('MY_PROPERTY', true);
                this.saveCustomProperties(properties, subject,
                    () => this.saveAppointment(appointment, subject));
            }
        }
    );

    return subject;
}

private saveCustomProperties(properties: any, subject: Subject<any>, callback: () => void): void {
    properties.saveAsync(result => {
        if (this.isErrorResult(result)) {
            subject.error(result.error.message);
        } else {
            callback();
        }
    });
}

private saveAppointment(appointment: any, subject: Subject<any>): void {
    appointment.saveAsync((asyncResult) => {
        if (this.isErrorResult(asyncResult)) {
            subject.error(asyncResult.error.message);
        } else {
            appointment.close();

            subject.next(asyncResult.value);
            subject.complete();
        }
    });
}

saveAppointmentAndSetProperty() is the entry to the code.  If I instead execute the code in saveAppointment I don't see any problems. 

A:

This is a known issue in OWA for item.saveAsync.  The following criteria is needed in order to reproduce the issue:

Must be an existing appointment
The appointment must have attendees

item.saveAsync() will return the correct item ID but will not persist changes in the compose form to the service and clear the dirty state.  
Saving custom properties should persist properties to the service.  If you are not relying on APIs that make additional changes to the compose form (e.g. changes to the body, subject, location, attendees, etc.)  for existing appointments with attendees, you can invoke saveAsync on CustomProperties.  This will persist changes to custom properties to the service without displaying a dialog to discard changes assuming you don't invoke item.saveAsync() after.