Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use addHandlerAsync from Office JS with Angular

I am creating an Excel Add-in using Angular and have created bindings for various cells. I have a form that is displaying the data within those bound cells, and I want the form to update when data in the cells change. In the OfficeJS library, it references using:

addHandlerAsync(Office.EventType.BindingDataChanged, <handler method>)

https://dev.office.com/reference/add-ins/shared/binding.addhandlerasync

However, when I attempt to do that in TS inside of an Angular component or service, the handler cannot call any methods from the component or service when it is triggered. It can only run Office JS methods within the handler. How do I make it so I can trigger a component or service method from within the handler?

An example of a use case is if you wanted to trigger something in your add-in every time something changed in any of your cell bindings in the spreadsheet, such as display something in your add-in. This would seem like a general operation that would be supported. And the documentation shows something similar in JS:

function addHandler() {
Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingDataChanged, dataChanged);
}
function dataChanged(eventArgs) {
    write('Bound data changed in binding: ' + eventArgs.binding.id);
}
// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

However, when I attempt to do something similar:

 createHandlerOnA1(): Promise<IOfficeResult> {
        return new Promise((resolve, reject) => {
            this.workbook.bindings.getByIdAsync(this.bindingName, (result: Office.AsyncResult) => {
                if(result.status === Office.AsyncResultStatus.Failed) {
                    reject({
                        error: 'failed to get binding by id'
                    });
                } else {
                    result.value.addHandlerAsync(Office.EventType.BindingDataChanged, this.changeEvent, (handlerResult: Office.AsyncResult) => {
                        if(handlerResult.status === Office.AsyncResultStatus.Failed) {
                            reject({
                                error: 'failed to set a handler'
                            });
                        } else {
                            // Successful 
                            resolve({
                                success: 'successfully set handler'
                            });
                        }
                    })
                }
            })
        })
    }

  addHandler() {
    this.createHandlerOnA1()
    .then((result: any) => {
      this.feedback = result.success;
    }, (result: IOfficeResult) => {
      this.feedback = result.error;
    });
  }

  changeFeedback() {
    this.feedback = 'hello'
  }

  // Excel methods
  changeEvent(eventArgs: any) {
      Excel.run(async (context) => {
            const data = [
                ["Hello World"]
            ];
      const range = context.workbook.getSelectedRange()
      range.values = data;
      await context.sync();
    });
    // Handler cannot run this
    // this.changeFeedback(); 
  }

The function, changeFeedback() can't be run from within the changeEvent(eventArgs: any) handler. I have made an example repo here to demonstrate the problem:

https://github.com/brandonkoch6/addHandlerAsyncError

I've attempted to change the reference to the handler so that it maintains the binding of 'this' by doing it like this:

addHandlerAsync(Office.EventType.BindingDataChanged, (eventArgs:any) => this.changeEvent(eventArgs))

addHandlerAsync(Office.EventType.BindingDataChanged, this.changeEvent.bind(this))

Those don't fix it. So, I'm a bit at a loss of what to do.

Any help would be appreciated. I think it has something to do with the context object of 'this' when the handler is run. There is an [options] parameter as part of adding the handler, but I can't seem to find all of the options available in the documentation:

https://dev.office.com/docs/add-ins/develop/asynchronous-programming-in-office-add-ins#passing-optional-parameters-to-asynchronous-methods

like image 850
codex Avatar asked Apr 15 '26 23:04

codex


1 Answers

The problem is because the code is run out of angular zone.

You need let the code to run inside of the zone. Check my code, and this article to understand how it works.

import { NgZone } from '@angular/core';

// ...

constructor(private zone: NgZone) { }

myFunction() {
  this.zone.run(() => {
    // the code
  });
}

P.S. I created an issue here. I will update the document soon when I get time.

like image 164
Hongbo Miao Avatar answered Apr 18 '26 12:04

Hongbo Miao



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!