word.run 在服务器环境中不工作

word.run did not work in server enviroment

绑定对象 (Word)

URL: https://dev.office.com/reference/add-ins/shared/bindings.bindings

  1. 创建变量绑定:

    • 使用 Office.context.document.getSelectedDataAsync(),asyncResult.value(响应)使用 TextBinding 添加 Office.context.document.bindings.addFromSelectionAsync() 到当前选择
  2. 填充内容

    • 使用 Binding.setDataAsync 方法,我们将数据写入由指定绑定对象表示的文档的绑定部分。 此时的问题是我们无法设置 {coercionType: "html"} 以在文档的绑定部分插入富文本,它适用于文本。

问题:

  1. 无法在 Online word 中使用 setDataAsync() 插入富文本
  2. 无法使用 bindingId 主动选择内容,这将帮助我们使用 Office.context.document.setSelectedDataAsync() 将数据写入文档中的当前选择(富文本适用于此 API ).使用此方法前需要解除绑定
  3. 无法通过在 Office.initialize 之后将函数传递到 Word.run() 方法来执行 Word.run(函数(上下文){});

没有控制台错误或警告,但 Word.run() 块在尝试通过服务器执行代码时从未运行。本地开发环境在这种情况下有效。

Error Reference Picture

清单

    <?xml version="1.0" encoding="UTF-8"?>
 <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp">
  <!-- BeginBasicSettings: Add-in metadata, used for all versions of Office unless override provided -->

  <!--IMPORTANT! Id must be unique for your add-in, if you clone this manifest ensure that you change this id to your own GUID -->
  <Id>010861c8-0558-472c-b350-f7795e27cfa5</Id>

  <!--Version. Updates from the store only get triggered if there is a version change -->
  <Version>1.0.0.0</Version>
  <ProviderName>Parrot365 : [QA]</ProviderName>
  <DefaultLocale>en-US</DefaultLocale>
  <!-- The display name of your add-in. Used on the store and various placed of the Office UI such as the add-ins dialog -->
  <DisplayName DefaultValue="Parrot365 : [QA]" />
  <Description DefaultValue="Parrot365 : QA Mode"/>

  <!--Icon for your add-in. Used on installation screens and the add-ins dialog -->
  <IconUrl DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_32px.png" />
  <HighResolutionIconUrl DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_128px.png" />
  <SupportUrl DefaultValue="http://support.wittyparrot.com/support/home" />

  <!--BeginTaskpaneMode integration. Office 2013 and any client that doesn't understand commands will use this section.
    This section will also be used if there are no VersionOverrides -->
  <Hosts>
    <Host Name="Document" />
    <Host Name="Workbook" />
    <Host Name="Presentation" />
  </Hosts>
  <DefaultSettings>
    <SourceLocation DefaultValue="https://qaparrot365.wittyparrot.com?source=msoffice" />
  </DefaultSettings>
  <!--EndTaskpaneMode integration -->

  <Permissions>ReadWriteDocument</Permissions>

  <!--BeginAddinCommandsMode integration-->
  <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
    <!-- Optional, override the description of the Add-in -->
    <Description resid="residToolTip" />
    <!--Required, hosts node. Each host can have a different set of commands -->
    <Hosts>
      <!--Specific host. Workbook=Excel, Document=Word, Presentation=PowerPoint -->
      <Host xsi:type="Document">
        <!-- Form factor. Currenly only DesktopFormFactor is supported. We will add TabletFormFactor and PhoneFormFactor in the future-->
        <DesktopFormFactor>

          <!--GetStarted information used on the callout that appears when installing the add-in.
            Ensure you have build 16.0.6769 or above for GetStarted section to work-->
          <GetStarted>
                        <!--Title of the Getting Started callout. resid points to a ShortString resource -->
                        <Title resid="Witty.GetStarted.Title"/>

                        <!--Description of the Getting Started callout. resid points to a LongString resource -->
                        <Description resid="Witty.GetStarted.Description"/>

                        <!--Not used right now but you need to provide a valid resource. We will add code in the future to consume this URL.
                            resid points to a Url resource -->
                        <LearnMoreUrl resid="Witty.GetStarted.LearnMoreUrl"/>
          </GetStarted>
          <FunctionFile resid="residDesktopFuncUrl" />
          <!--PrimaryCommandSurface==Main Office Ribbon-->
          <ExtensionPoint xsi:type="PrimaryCommandSurface">
            <OfficeTab id="TabHome">
              <!--Group. Ensure you provide a unique id. Recommendation for any IDs is to namespace using your companyname-->
              <Group id="Witty.Citations.Group1Id1">
                <!--Label for your group. resid must point to a ShortString resource -->
                <Label resid="residLabel4" />
                <!--Icons. Required sizes 16,31,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX -->
                <!--Use PNG icons and remember that all URLs on the resources section must use HTTPS -->
                <Icon>
                  <bt:Image size="16" resid="icon1_16x16" />
                  <bt:Image size="32" resid="icon1_32x32" />
                  <bt:Image size="80" resid="icon1_80x80" />
                </Icon>
                 <!--Control. It can be of type "Button" or "Menu" -->
                <Control xsi:type="Button" id="Button3Id1">
                  <!--Label for your button. resid must point to a ShortString resource -->
                  <Label resid="residLabel3" />
                  <Supertip>
                    <!--ToolTip title. resid must point to a ShortString resource -->
                    <Title resid="residLabel" />
                     <!--ToolTip description. resid must point to a LongString resource -->
                    <Description resid="residToolTip" />
                  </Supertip>
                  <Icon>
                    <bt:Image size="16" resid="icon1_16x16" />
                    <bt:Image size="32" resid="icon3_32x32" />
                    <bt:Image size="80" resid="icon1_80x80" />
                  </Icon>
                  <!--This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFuncion or ShowTaskpane-->
                  <Action xsi:type="ShowTaskpane">
                    <!--Provide a url resource id for the location that will be displayed on the taskpane -->
                    <SourceLocation resid="residUnitConverterUrl" />
                  </Action>
                </Control>
              </Group>
            </OfficeTab>
          </ExtensionPoint>
        </DesktopFormFactor>
      </Host>

      <Host xsi:type="Workbook">
        <!-- Form factor. Currenly only DesktopFormFactor is supported. We will add TabletFormFactor and PhoneFormFactor in the future-->
        <DesktopFormFactor>

          <!--GetStarted information used on the callout that appears when installing the add-in.
            Ensure you have build 16.0.6769 or above for GetStarted section to work-->
          <GetStarted>
                        <!--Title of the Getting Started callout. resid points to a ShortString resource -->
                        <Title resid="Witty.GetStarted.Title"/>

                        <!--Description of the Getting Started callout. resid points to a LongString resource -->
                        <Description resid="Witty.GetStarted.Description"/>

                        <!--Not used right now but you need to provide a valid resource. We will add code in the future to consume this URL.
                            resid points to a Url resource -->
                        <LearnMoreUrl resid="Witty.GetStarted.LearnMoreUrl"/>
          </GetStarted>
          <FunctionFile resid="residDesktopFuncUrl" />
          <!--PrimaryCommandSurface==Main Office Ribbon-->
          <ExtensionPoint xsi:type="PrimaryCommandSurface">
            <OfficeTab id="TabHome">
              <!--Group. Ensure you provide a unique id. Recommendation for any IDs is to namespace using your companyname-->
              <Group id="Witty.Citations.Group1Id1">
                <!--Label for your group. resid must point to a ShortString resource -->
                <Label resid="residLabel4" />
                <!--Icons. Required sizes 16,31,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX -->
                <!--Use PNG icons and remember that all URLs on the resources section must use HTTPS -->
                <Icon>
                  <bt:Image size="16" resid="icon1_16x16" />
                  <bt:Image size="32" resid="icon1_32x32" />
                  <bt:Image size="80" resid="icon1_80x80" />
                </Icon>
                 <!--Control. It can be of type "Button" or "Menu" -->
                <Control xsi:type="Button" id="Button3Id1">
                  <!--Label for your button. resid must point to a ShortString resource -->
                  <Label resid="residLabel3" />
                  <Supertip>
                    <!--ToolTip title. resid must point to a ShortString resource -->
                    <Title resid="residLabel" />
                     <!--ToolTip description. resid must point to a LongString resource -->
                    <Description resid="residToolTip" />
                  </Supertip>
                  <Icon>
                    <bt:Image size="16" resid="icon1_16x16" />
                    <bt:Image size="32" resid="icon3_32x32" />
                    <bt:Image size="80" resid="icon1_80x80" />
                  </Icon>
                  <!--This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFuncion or ShowTaskpane-->
                  <Action xsi:type="ShowTaskpane">
                    <!--Provide a url resource id for the location that will be displayed on the taskpane -->
                    <SourceLocation resid="residUnitConverterUrl" />
                  </Action>
                </Control>
              </Group>
            </OfficeTab>
          </ExtensionPoint>
        </DesktopFormFactor>
      </Host>

      <Host xsi:type="Presentation">
        <!-- Form factor. Currenly only DesktopFormFactor is supported. We will add TabletFormFactor and PhoneFormFactor in the future-->
        <DesktopFormFactor>

          <!--GetStarted information used on the callout that appears when installing the add-in.
            Ensure you have build 16.0.6769 or above for GetStarted section to work-->
          <GetStarted>
                        <!--Title of the Getting Started callout. resid points to a ShortString resource -->
                        <Title resid="Witty.GetStarted.Title"/>

                        <!--Description of the Getting Started callout. resid points to a LongString resource -->
                        <Description resid="Witty.GetStarted.Description"/>

                        <!--Not used right now but you need to provide a valid resource. We will add code in the future to consume this URL.
                            resid points to a Url resource -->
                        <LearnMoreUrl resid="Witty.GetStarted.LearnMoreUrl"/>
          </GetStarted>
          <FunctionFile resid="residDesktopFuncUrl" />
          <!--PrimaryCommandSurface==Main Office Ribbon-->
          <ExtensionPoint xsi:type="PrimaryCommandSurface">
            <OfficeTab id="TabHome">
              <!--Group. Ensure you provide a unique id. Recommendation for any IDs is to namespace using your companyname-->
              <Group id="Witty.Citations.Group1Id1">
                <!--Label for your group. resid must point to a ShortString resource -->
                <Label resid="residLabel4" />
                <!--Icons. Required sizes 16,31,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX -->
                <!--Use PNG icons and remember that all URLs on the resources section must use HTTPS -->
                <Icon>
                  <bt:Image size="16" resid="icon1_16x16" />
                  <bt:Image size="32" resid="icon1_32x32" />
                  <bt:Image size="80" resid="icon1_80x80" />
                </Icon>
                 <!--Control. It can be of type "Button" or "Menu" -->
                <Control xsi:type="Button" id="Button3Id1">
                  <!--Label for your button. resid must point to a ShortString resource -->
                  <Label resid="residLabel3" />
                  <Supertip>
                    <!--ToolTip title. resid must point to a ShortString resource -->
                    <Title resid="residLabel" />
                     <!--ToolTip description. resid must point to a LongString resource -->
                    <Description resid="residToolTip" />
                  </Supertip>
                  <Icon>
                    <bt:Image size="16" resid="icon1_16x16" />
                    <bt:Image size="32" resid="icon3_32x32" />
                    <bt:Image size="80" resid="icon1_80x80" />
                  </Icon>
                  <!--This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFuncion or ShowTaskpane-->
                  <Action xsi:type="ShowTaskpane">
                    <!--Provide a url resource id for the location that will be displayed on the taskpane -->
                    <SourceLocation resid="residUnitConverterUrl" />
                  </Action>
                </Control>
              </Group>
            </OfficeTab>
          </ExtensionPoint>
        </DesktopFormFactor>
      </Host>
    </Hosts>
    <Resources>
      <bt:Images>
        <bt:Image id="icon1_16x16" DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/widget_logo.png">
        </bt:Image>
        <bt:Image id="icon1_32x32" DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_32px.png">
        </bt:Image>
        <bt:Image id="icon1_80x80" DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_80px.png">
        </bt:Image>
        <bt:Image id="icon2_32x32" DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_32px.png">
        </bt:Image>
        <bt:Image id="icon3_32x32" DefaultValue="https://qaparrot365.wittyparrot.com/resources/assets/images/wp_logo_32px.png">
        </bt:Image>
      </bt:Images>
      <bt:Urls>
         <bt:Url id="residDesktopFuncUrl" DefaultValue="https://qaparrot365.wittyparrot.com?source=msoffice">
        </bt:Url>
        <bt:Url id="residUnitConverterUrl" DefaultValue="https://qaparrot365.wittyparrot.com?source=msoffice">
        </bt:Url>
          <!--LearnMore URL currently not used -->
        <bt:Url id="Witty.GetStarted.LearnMoreUrl" DefaultValue="https://qaparrot365.wittyparrot.com">
        </bt:Url>
      </bt:Urls>
      <bt:ShortStrings>
        <bt:String id="residLabel" DefaultValue="Launch Widget">
        </bt:String>
        <bt:String id="residLabel3" DefaultValue="Parrot365">
        </bt:String>
        <bt:String id="residLabel4" DefaultValue=" ">
        </bt:String>
        <bt:String id="Witty.GetStarted.Title" DefaultValue="Parrot365 Widget Loaded Successfully">
        </bt:String>
      </bt:ShortStrings>
      <bt:LongStrings>
        <bt:String id="residToolTip" DefaultValue="Parrot365 add-in provides improves productivity, accuracy and consistency in communication.">
        </bt:String>
         <bt:String id="Witty.GetStarted.Description" DefaultValue="Get going by opening the Home tab on the Ribbon then click Parrot365 button">
        </bt:String>
      </bt:LongStrings>
    </Resources>
  </VersionOverrides>
</OfficeApp>

'use strict';
var count = 0;
(function() {

 angular.module('wpoffice')
  .directive('wittyWordVariable', function() {

   return {
    restrict: 'E',
    replace: true,
    templateUrl: 'app/components/witty-word-variable/witty-word-variable_template.html',
    scope: {},
    bindToController: true,
    controller: 'WordVariableController',
    controllerAs: 'WordVariableCtrl'
   };
  })
  .controller('WordVariableController', WordVariableController);

 function WordVariableController($q, ngNotify) {
  var vm = this;
  vm.init = init;
  vm.getSelectedWordFromDocument = getSelectedWordFromDocument;
  vm.populateWordVar = populateWordVar;

  function init() {
            console.log('Initialized WordVariableController');
   vm.inputBoxObjects = [];
   vm.varValues = [];
   vm.onloadVariablesFound = [];
   if (Office.context.document) {
    getAllSelectedContentControl();
   } else {
    ngNotify.set('Please reload Parrot365', 'error');
   }
  }

        /*
        *getAllSelectedContentControl----it will load the variable and  corresponding text box
        *@param --- no param
        */
  function getAllSelectedContentControl() {
            console.log('getAllSelectedContentControl');
            console.log(Word);
            Word.run(function(context) {
                console.log('inside getSelectedContentControl 2');
                    var thisDocument = context.document;
                    context.load(thisDocument, 'contentControls/id, contentControls/text, contentControls/tag');
                    return context.sync().then(function() {
                        console.log('returned getSelectedContentControl');
                        if (thisDocument.contentControls.items.length !== 0) {
                            console.log(thisDocument.contentControls.items.length);
                            for (var i = 0; i < thisDocument.contentControls.items.length; i++) {
                                var variableLabel = thisDocument.contentControls.items[i].text;
                                var tagId = thisDocument.contentControls.items[i].tag;
                                if (tagId) {
                                    getVarArray(variableLabel,tagId).then(function(arrayObj) {
                                        createInputboxes(arrayObj);
                                    });
                                }
                            }
                        } else {
                            console.log('Content is empty');
                        }
                    });
                }).then(function() {
                    console.log('completed');
                })
                .catch(function(error) {
                    console.log('Error: ' + error);
                    if (error instanceof OfficeExtension.Error) {
                        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
                        console.log('Error code and message: ' + error.toString());
                    }
                });
  }




        /*
        *getVarArray----creates object on load
        *@param variablevalue---value of variable
        *@param  TagId -----id of variable
        */
  function getVarArray(variablevalue,tagId)
        {
   var deferred = $q.defer();
   var allBindings = [];
            var tagPrefix, variableLabel;

    if (tagId && tagId.lastIndexOf("__") != -1) {
     tagPrefix = tagId.substr(0,tagId.lastIndexOf('__'));
                    var index = tagId.indexOf("_");
                    if(index) {
                        variableLabel = tagId.substr(0,index);
                    }
    }

    var temp = {
     'id': tagId,
     'variableLabel': variableLabel,
     'tagPrefix': tagPrefix,
                    'value': variablevalue
    };

    var indexOfBinding = _.findIndex(allBindings, {
     variableLabel: variableLabel,
     tagPrefix: tagPrefix
    });

    if (indexOfBinding === -1) {
     allBindings.push(temp);
    }

    deferred.resolve(allBindings);

   return deferred.promise;
  }

        /*
        *createInputboxes ----call createVariable function which will create scope obj
        */
  function createInputboxes(arrayOfBindinds)
        {
   angular.forEach(arrayOfBindinds, function(binding) {
    console.log(binding);
                var textBoxValue = binding.value ? binding.value:'Enter Text';
                console.log(binding.variableLabel+'===='+binding.tagPrefix+'===='+textBoxValue+'===='+binding.id);
    createVariable (binding.variableLabel,binding.tagPrefix,textBoxValue,binding.id);
   });
  }



  /*getSelectedWord - Get Selected Data from the document which  user has  selected manually.
   *@no param
   *called on user selection
   *
   */
  function getSelectedWordFromDocument()
        {
            console.log('abc');
         Word.run(function(context) {
            console.log('inside word.run');
   var range = context.document.getSelection();
   var ContentControlForSelection = range.insertContentControl();
   ContentControlForSelection.load('text');
   return context.sync().then(function() {
    var variableLabel = ContentControlForSelection.text;
    var tagPrefix = variableLabel + '_tag';
    ContentControlForSelection.tag = tagPrefix + '__' + count;
    //var conditionalVariable = 'Onseletion';
    //createVariable(variableLabel, tagPrefix);
                console.log('variable created with'+variableLabel);
                getVarArrayOnSelection(ContentControlForSelection.tag,variableLabel, tagPrefix).then(function(bindings) {
                    console.log(bindings);
                    createInputboxes(bindings);
                });

   });

  })
  .catch(function(error) {
   console.log('Error: ' + error);
   if (error instanceof OfficeExtension.Error) {
    console.log('Debug info: ' + JSON.stringify(error.debugInfo));
   }
  });
    }



        /*
        *getVarArrayOnSelection --- it creates obj for selected word in document
        *@param--tagId
        *@param--userSelectedText
        *@param--tagPrefix
        */
  function getVarArrayOnSelection(tagId,userSelectedText,tagPrefix)
        {
   var deferred = $q.defer();
   var allBindings = [];

   var temp = {
                'id': tagId,
    'variableLabel': userSelectedText,
    'tagPrefix': tagPrefix
   };

   var indexOfBinding = _.findIndex(allBindings, {
    variableLabel: userSelectedText,
    tagPrefix: tagPrefix
   });
   if (indexOfBinding === -1) {
    allBindings.push(temp);
   }

   deferred.resolve(allBindings);
   return deferred.promise;
  }


  function createVariable(variableLabel, tagPrefix, newValue, id) {
            console.log(variableLabel+'===='+ tagPrefix+'===='+ newValue+'===='+ id);
   var flag = false;
   var inputObj = {
    id: id,
    label: variableLabel,
    tag: tagPrefix,
    value: newValue
   };

   if (vm.inputBoxObjects.length > 0) {
    var index = _.findIndex(vm.inputBoxObjects, {
     label: variableLabel
    });
    if (index === -1) {
     vm.inputBoxObjects.push(inputObj);
    } else {
     console.log('it already exists in the array');
    }
   } else {
    vm.inputBoxObjects.push(inputObj);
   }
  }

  /*populateWordVar
   *@TextBoxId = Text Box Id
   *@tagPrefix = unique identifier
   **/
  function populateWordVar(obj, $event, $index)
        {
   console.log(obj);
            var TextboxValue = ($event.target.value === '' ? obj.label : $event.target.value);

            console.log(TextboxValue);

            Word.run(function (context) {

                var contentControlsWithTag = context.document.contentControls.getByTag(obj.id);

                context.load(contentControlsWithTag, 'text');

                return context.sync().then(function () {
                    if (contentControlsWithTag.items.length === 0) {
                        console.log("There isn't a content control with a tag in this document.");
                    } else {
                        console.log('The first content control with the tag has this text: ' + contentControlsWithTag.items[0].text);
                        for (var i = 0;i<contentControlsWithTag.items.length;i++){
                        contentControlsWithTag.items[i].insertHtml(TextboxValue, 'Replace');
                        }
                    }
                });
            })
            .catch(function (error) {
                console.log('Error: ' + JSON.stringify(error));
                if (error instanceof OfficeExtension.Error) {
                    console.log('Debug info: ' + JSON.stringify(error.debugInfo));
                }
            });
  }




 }
})();
<div class="flex layout-column pad-box-10 profile-container" ng-init="WordVariableCtrl.init()">

  <div class="layout-row">
    <button class="ms-Button ms-Button--primary" ng-click="WordVariableCtrl.getSelectedWordFromDocument ()">
        <span class="ms-Button-label">Create Variable</span>
      </button>
  </div>

  <div class="layout-row">
    <div class="layout-column">
      <div ng-if="WordVariableCtrl.inputBoxObjects.length > 0" class="layout-row flex layout-align-start-center margin-d-10" id="innerDiv" data-ng-repeat="inputBox in WordVariableCtrl.inputBoxObjects">
        <label class="ms-Label margin-r-10" for="{{inputBox.id}}" style="width:40%;overflow: hidden;display: inline-block;
    text-overflow: ellipsis;white-space: nowrap;">{{inputBox.label}}</label>
        <input type="text" name="" placeholder="Enter value" value="{{inputBox.value}}" id={{inputBox.id}} ng-blur="WordVariableCtrl.populateWordVar(inputBox,$event,$index)" class="ms-TextField-field flex flex-rem send-email-input">
      </div>
    </div>
  </div>
</div>

您的清单声明您的 add-in 适用于 Word、PowerPoint 和 Excel,但它指向的代码正在利用 Word Api。您应该确保您的清单声明对 Word API 的依赖并删除 Excel 和 PowerPoint。这将确保 add-in 仅显示在受支持的位置。

代码被大大缩小了,因此很难弄清楚它到底在做什么。我怀疑您的问题是您正在使用 Word API 1.3 或 1.4 进行调用,Word Online 尚不支持这些版本。这可以解释为什么您在桌面上使用 Word 时看到正确的行为,但在与 Word Online 一起使用时却看不到。我建议查看代码以确保您不会依赖任何 new methods introduced with 1.3

您还将参数从清单传递到应用程序。您的 add-in 的源页面不应依赖于参数。此处的指导是为您的 add-in 使用独特的登录页面。在初始化 add-in 并建立 Word 和 add-in 之间的通信通道后,您可以将用户重定向到新的 URI。

关于设置数据,我已经在你的另一个问题中回答了这个问题: