Microsoft Office

Erkunden der JavaScript-API für Office: Datenzugriff und Ereignisse

Stephen Oliver
Eric Schmidt
 

Dieser Artikel ist der zweite aus einer Reihe mit exemplarischen Vorgehensweisen für die JavaScript-API für Office. Teil 1 (verfügbar unter msdn.microsoft.com/magazine/jj891051) bietet einen breiten Überblick über das Objektmodell. In diesem Artikel machen wir da weiter, wo wir im ersten Teil aufgehört haben: Wir beschäftigen uns mit dem Zugriff auf den Inhalt von Dateien und untersuchen das Ereignismodell.

In der gesamten Reihe verweisen wir häufig auf die Referenzdokumentation zur JavaScript-API für Office. Sie finden die offizielle Dokumentation, Codebeispiele und Communityressourcen im Apps für Office und SharePoint Developer Center auf MSDN (dev.office.com).

Zugreifen auf Inhalte einer Office-Datei über eine App für Office

Die JavaScript-API für Office bietet verschiedene Möglichkeiten des Zugriffs auf die Daten in einer Office-Datei: Sie können entweder die aktuell ausgewählten Daten einfügen oder abrufen oder aber die gesamte Datei abrufen. Diese Formen des Datenzugriffs mögen simpel erscheinen, und in der Tat sind beide Techniken recht einfach einzusetzen. Sie bieten aber auch ein hohes Maß an Flexibilität und Anpassung, sodass Ihnen für Ihre Apps viele Möglichkeiten zur Verfügung stehen.

Sie können mit der JavaScript-API für Office nicht nur auf ausgewählte Daten oder die gesamte Datei zugreifen, sondern auch Daten binden oder benutzerdefinierte XML-Teile im Dokument bearbeiten. Wir wollen uns diese Techniken für die Arbeit mit Office-Inhalten mal genauer anschauen.

Abrufen und Einfügen ausgewählter Daten

Wie bereits zuvor erwähnt, ermöglicht das Document-Objekt einer App den Zugriff auf die Daten in der Datei. Bei Aufgabenbereich- und Inhalts-Apps können wir die Methoden „Document.getSelectedDataAsync“ und „Document.setSelectedDataAsync“ zum Abrufen oder Einfügen von ausgewähltem Inhalt in einer Office-Datei verwenden.

Mit den beiden Methoden können verschiedene Datenformattypen bearbeitet werden, die wir beim Aufrufen steuern können. Die Methoden „getSelectedDataAsync“ und „setSelectedDataAsync“ weisen beide den Parameter „coercionType“ auf, der eine Konstante aus der Office.CoercionType-Enumeration verwendet. Mit dem Parameter „coercionType“ wird das Datenformat des einzufügenden oder abzurufenden Inhalts angegeben. Je nach dem Wert des Parameters „coercionType“ können wir die Daten als einfachen Text, als Tabelle, als Matrix, als HTML oder sogar als „rohes“ Office Open XML (OOXML) auswählen. (Zum Zeitpunkt der Veröffentlichung wird das Abrufen und Einfügen von Text als HTML oder OOXML nur in Word 2013 unterstützt.)

Bei der Verwendung von „getSelectedDataAsync“ und „setSelectedDataAsync“ müssen Sie aber nicht jedes Mal einen „coercionType“ angeben. Wenn möglich, wird der „coercionType“ aus dem Kontext abgeleitet. Wenn Sie z. B. ein Zeichenfolgenliteral an den Aufruf „setSelectedDataAsync“ übergeben, lautet der standardmäßige „coercionType“ „text“. Wenn Sie die gleichen Daten als ein Array von Arrays übergeben, lautet der standardmäßige „coercionType“ „matrix“.

Im Folgenden wird gezeigt, wie leistungsstark diese einfachen Methoden sind. Wir verwenden hier primär die setSelectedDataAsync-Methode. Wir beginnen mit Code, mit dem ein einfacher Text in ein Word-Dokument eingefügt wird:

// Define some data to set in the document.
var booksToRead = "Anabasis by Xenophon; \n" +
  "Socrates' Apology by Plato; \n" +
  "The Illiad by Homer.";
// Set some data to the document as simple text.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Text },
  function (result) {
    // Access the results, if necessary.
});

Abbildung 1 zeigt das Ergebnis.

Results of Inserting Data as Simple Text
Abbildung 1: Ergebnis nach dem Einfügen von Daten als einfachen Text

Jetzt ändern wir das Beispiel so ab, dass der Text mit dem „coercionType“ „matrix“ eingefügt wird. Eine Matrix ist ein Array von Arrays, das als einfacher Zellenbereich (Excel) oder einfache Tabelle (Word) eingefügt wird.

In Word fügt der Code eine nicht formatierte Tabelle mit zwei Spalten und ohne Überschriften ein. Jedes Element im Array der ersten Ebene stellt eine Zeile in der eingefügten Tabelle dar, jedes Element in einem Teilarray enthält die Daten für eine Zelle in der Zeile:

// Define a matrix of data to set in the document.
var booksToRead = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as an unformatted table.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Matrix },
  function (result) {
    // Access the results, if necessary.
});

Abbildung 2 zeigt das Ergebnis.

Results of Inserting Data as a Matrix
Abbildung 2: Ergebnis nach dem Einfügen von Daten als Matrix

Neben dem „coercionType“ „matrix“ können wir auch mit einem TableData-Objekt Daten als Tabelle abrufen oder einfügen. Damit können wir die Ergebnisse auch ein wenig formatieren – in diesem Fall wird eine Überschriftenzeile eingefügt. Auf die Überschriftenzeile und den Inhalt eines TableData-Objekts greifen wir über die headers- und rows-Eigenschaften zu.

Mit dem TableData-Objekt kann außerdem eine einzufügende Teilmenge der Daten über die startRow- und startColumn-Parameter angegeben werden. Dies ermöglicht Ihnen z. B. das Einfügen von Daten in eine einzige Spalte einer fünfspaltigen Tabelle. Die startRow- und startColumn-Parameter werden im nächsten Artikel dieser Reihe genauer beleuchtet.

Anmerkung: Wenn im Dokument eine Tabelle ausgewählt wurde, muss die Auswahlform den einzufügenden Daten entsprechen (es sei denn, Sie verwenden die startRow- und startColumn-Parameter). Wenn sich die einzufügenden Daten z. B. in einer 2-x-2-Tabelle befinden, im Dokument aber ein Bereich von 3-x-2-Zellen ausgewählt ist, schlägt der Vorgang fehl. Dies trifft auch beim Einfügen von Daten als Matrix zu.

Wie beim „coercionType“ „matrix“ geben die headers- und rows-Eigenschaften ein Array von Arrays zurück, bei dem jedes Element im ersten Array eine Datenzeile ist und jedes Element in einem Teilarray eine Datenzelle in der Tabelle enthält, wie Sie in Abbildung 3 sehen.

Abbildung 3: Einfügen von Daten in ein Dokument als Tabelle

// Define some tabular data to set in the document,
// including a header row.
var booksToRead = new Office.TableData();
booksToRead.headers = [["Author", "Title"]];
booksToRead.rows = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as a table with a header.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Table },
  function (result) {
    // Access the results, if necessary.
});

Abbildung 4 zeigt das Ergebnis des Codes aus Abbildung 3.

Results of Inserting Data as a Table
Abbildung 4: Ergebnis nach dem Einfügen von Daten als Tabelle

Im nächsten Beispiel fügen wir die gleichen Daten ein, diesmal jedoch mit HTML-Formatierung und unter Verwendung der Office.CoercionType.HTML-Koersion. Jetzt können wir den eingefügten Daten zusätzliche Formatierungen zuweisen, z. B. CSS-Stylesheets, wie in Abbildung 5 gezeigt.

Abbildung 5: Einfügen von Daten in ein Dokument als HTML

// Define some HTML data to set in the document,
// including header row, text formatting and CSS styles.
var booksToRead =
  "<table style='font-family:Segoe UI'>" +
    "<thead style='background-color:#283E75;color:white'>" +
      "<tr><th>Authors</th><th>Books</th></tr>" +
    "</thead>" +
    "<tbody>" +
      "<tr><td>Xenophon</td><td><u>Anabasis</u></td></tr>" +
      "<tr><td>Plato</td><td><u>Socrates' Apology</u></td></tr>" +
      "<tr><td>Homer</td><td><u>The Iliad</u></td></tr>" +
    "</tbody>" +
  "</table>";
// Set some data to the document as a table with styles applied.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Html },
    function (result) {
    // Access the results, if necessary.
});

Abbildung 6 zeigt das Ergebnis des Codes aus Abbildung 5.

Results of Inserting Data as HTML
Abbildung 6: Ergebnis nach dem Einfügen von Daten als HTML

Schließlich können wir den Text als OOXML in das Dokument einfügen, was uns viele Möglichkeiten der Datenanpassung bietet und wesentlich mehr komplexe Inhaltstypen in Word ermöglicht (z. B. SmartArt oder Inlinebilder).

Die Datentabelle, mit der wir hier arbeiten, sieht als OOXML und als Zeichenfolgenliteral wie der Code in Abbildung 7 aus (Hinweis: Aufgrund der Länge ist hier nur ein Teil der Tabelle zu sehen).

Abbildung 7: Ein OOXML-Ausschnitt, der eine Word-Tabelle wiedergibt, gespeichert als JavaScript-Zeichenfolgenliteral

var newTable = "<w:tbl>" +
  "<w:tblPr>" +
    "<w:tblStyle w:val=\"TableGrid\"/>" +
    "<w:tblW w:w=\"0\" w:type=\"auto\"/>" +
    "<w:tblBorders>" +
      "<w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "</w:tblBorders>" +
    "<w:tblLook w:val=\"04A0\" w:firstRow=\"1\" w:lastRow=\"0\"" +
      "w:firstColumn=\"1\" w:lastColumn=\"0\""  +
      "w:noHBand=\"0\" w:noVBand=\"1\"/>" +
  "</w:tblPr>" +
  "<w:tblGrid>" +
    "<w:gridCol w:w=\"4675\"/>" +
    "<w:gridCol w:w=\"4675\"/>" +
  "</w:tblGrid>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\""  +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Authors</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\"" +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Books</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Xenophon</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Anabasis</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  // The rest of the code has been omitted for the sake of brevity.
"</w:tbl>";

Bei dieser Technik sind gute Kenntnisse von XML und besonders der im OOXML-Standard (ECMA-376) beschriebenen Strukturen erforderlich. Beim Einfügen von OOXML in ein Dokument müssen die Daten als Zeichenfolge mit allen nötigen Informationen gespeichert sein (HTML-Dokumentobjekte können nicht eingefügt werden) – einschließlich der Beziehungen und zugehörigen Dokumentteile im Dateiformatpaket. Wenn Sie daher einen komplexeren Inhaltstyp mithilfe von OOXML in Word einfügen, sollten Sie daran denken, die OOXML-Daten gemäß der Best Practices für die OOXML-Verwendung und der Open Packaging Conventions anzupassen.

In Abbildung 8 haben wir dieses Problem gelöst, indem wir die Daten zunächst als OOXML abrufen, unsere Daten mit dem OOXML aus dem Dokument verketten (durch Bearbeiten der erhaltenen Daten und der neuen Daten als Zeichenfolgen) und dann das OOXML wieder in das Dokument einfügen. (Einer der Gründe für das Funktionieren des Codes ist hier natürlich, dass wir keinen Inhalt hinzugefügt haben, der das Hinzufügen oder Ändern von Beziehungen oder Dokumentteilen in der Datei erforderlich macht.)

Abbildung 8: Einfügen von Daten in ein Dokument als Tabelle mithilfe von OOXML

// Get the OOXML for the data at the point of insertion
// and add a table at the beginning of the selection.
Office.context.document.getSelectedDataAsync(
  Office.CoercionType.Ooxml,
  {
    valueFormat: Office.ValueFormat.Formatted,
    filterType: Office.FilterType.All
  },
  function (result) {
    if (result.status == "succeeded") {
      // Get the OOXML returned from the getSelectedDataAsync call.
      var selectedData = result.value.toString();
      // Define the new table in OOXML.
      var newTable = "<!--Details omitted for brevity.-->";
      // Find the '<w:body>' tag in the returned data—the tag
      // that represents the body content of the selection, contained
      // within the main document package part (/word/document.xml)—
      // and then insert the new table into the OOXML at that point.
      var newString = selectedData.replace(
        "<w:body>",
        "<w:body>" + newTable,
        "gi");
        // Insert the data back into the document with the table added.
        Office.context.document.setSelectedDataAsync(
          newString,
          { coercionType: Office.CoercionType.Ooxml },
          function () {
        });
    }
});

Abbildung 9 zeigt das Ergebnis des Codes aus Abbildung 8.

Results of Inserting Data as OOXML
Abbildung 9: Ergebnis nach dem Einfügen von Daten als OOXML

Anmerkung: Eine gute Übung zum Bearbeiten von OOXML über eine App ist das Hinzufügen des betreffenden Inhalts über die Benutzeroberfläche (z. B. Einfügen von SmartArt über Einfügen | Illustrationen | SmartArt), Abrufen des OOXML für den Inhalt mithilfe von „getSelectedDataAsync“ und das Prüfen der Ergebnisse. Weitere Informationen erhalten Sie auch im Blogbeitrag „Einfügen von Bildern mit Apps für Office“ unter bit.ly/SeU3MS.

Abrufen des gesamten Dateiinhalts

Das Abrufen oder Einfügen von Daten am Auswahlpunkt ist zwar praktisch, in manchen Szenarios muss aber der gesamte Dateiinhalt abgerufen werden. Eine App benötigt z. B. den gesamten Dokumenteninhalt als Text, um ihn zu analysieren und dann in einem Blasendiagramm darzustellen. In einem anderen Beispiel muss eine App den gesamten Dateiinhalt an einen Remotewebdienst senden, um ihn drucken oder faxen zu lassen.

Die JavaScript-API für Office bietet für diese Szenarios genau die richtigen Funktionen. Mithilfe der JavaScript-API kann eine App eine Kopie der Datei erstellen, in die sie eingefügt ist, die Kopie auf Datenabschnitte oder Segmente (bis zu 4 MB) aufteilen und dann die Daten in diesen Segmenten lesen.

Das Abrufen des gesamten Dateiinhalts umfasst in der Regel drei Schritte:

  1. In Word oder PowerPoint eingefügte Apps rufen die Document.getFileAsync-Methode auf, die ein File-Objekt zurückgibt, das einer Kopie der Datei entspricht.
  2. Sobald die App einen Verweis auf die Datei hat, kann sie die File.getSliceAsync-Methode aufrufen und durch Übergeben von Segmentindizes auf Segmente in der Datei zuzugreifen. Wenn dies mit einer for-Schleife umgesetzt wird, muss beim aufrufenden Code auf den Umgang mit Closures geachtet werden.
  3. Abschließend sollte die App das File-Objekt schließen, indem sie die File.closeAsync-Methode aufruft. Nur zwei Dateien dürfen sich gleichzeitig im Arbeitsspeicher befinden. Beim Öffnen einer dritten Datei mit „Document.getFileAsync“ wird die Meldung „Interner Fehler aufgetreten“ ausgegeben.

In Abbildung 10 rufen wir ein Word-Dokument in 1-KB-Segmenten auf, durchlaufen jedes Dateisegment und schließen die Datei abschließend.

Abbildung 10: Abrufen des gesamten Dateiinhalts als Text und Durchlaufen der Segmente

// Get all of the content from a Word document in 1KB chunks of text.
function getFileData() {
  Office.context.document.getFileAsync(
  Office.FileType.Text,
  {
    sliceSize: 1000
  },
  function (asyncResult) {
    if (asyncResult.status === 'succeeded') {
      var myFile = asyncResult.value,
        state = {
          file: myFile,
          counter: 0,
          sliceCount: myFile.sliceCount
        };
      getSliceData(state);
    }
  });
}
// Get a slice from the file, as specified by
// the counter contained in the state parameter.
function getSliceData(state) {
  state.file.getSliceAsync(
    state.counter,
    function (result) {
    var slice = result.value,
      data = slice.data;
    state.counter++;
    // Do something with the data.
    // Check to see if the final slice in the file has
    // been reached—if not, get the next slice;
    // if so, close the file.
    if (state.counter < state.sliceCount) {
      getSliceData(state);
    }
    else {
      closeFile(state);
    }
  });
}
// Close the file when done with it.
function closeFile(state) {
  state.file.closeAsync(
    function (results) {
      // Inform the user that the process is complete.
  });
}

Weitere Informationen über die Vorgehensweise zum Abrufen des gesamten Dateiinhalts aus einer App für Office finden Sie auf der Dokumentationsseite „Vorgehensweise: Abrufen des gesamten Dokuments aus einer App für PowerPoint“ unter bit.ly/12Asi4x.

Abrufen von Aufgabendaten, Ansichtsdaten und Ressourcendaten aus einem Projekt

Für in Project eingefügte Aufgabenbereich-Apps enthält die JavaScript-API für Office weitere Methoden, um Daten für das aktive Projekt und die ausgewählte Aufgabe, Ressource oder Ansicht zu lesen. Das Skript „project-15.js“ erweitert „office.js“ und fügt außerdem Auswahländerungsereignisse für Aufgaben, Ressourcen und Ansichten hinzu. Wenn der Benutzer z. B. eine Aufgabe in der Teamplaneransicht auswählt, kann eine App folgende Informationen an einem zentralen Ort integrieren und anzeigen: die verbleibende Arbeit für diese Aufgabe, die an dieser Aufgabe Beteiligten und zugehörige Projekte in anderen SharePoint-Aufgabenlisten oder in Project Server, die sich auf den Zeitplan auswirken können.

Eine in ein Projekt eingefügte Aufgabenbereich-App verfügt nur über Lesezugriff auf den Inhalt des Projekts. Da eine Aufgabenbereich-App aber im Prinzip eine Webseite ist, kann sie mithilfe von JavaScript und Protokollen wie Representational State Transfer (REST) aus externen Anwendungen lesen und in sie schreiben. Die Dokumentation zu Apps für Office- und SharePoint enthält z. B. eine Beispiel-App für Project Professional, die „jQuery“ mit dem OData-Berichterstellungsdienst in Project verwendet, um die Gesamtkosten und die Arbeitsdaten für das aktive Projekt mit den durchschnittlichen Werten für alle Projekte in einer Project Web App zu vergleichen (siehe Abbildung 11).

A Task Pane App that Uses jQuery with an OData Reporting Service
Abbildung 11: Eine Aufgabenbereich-App, die „jQuery“ mit einem OData-Berichterstellungsdienst verwendet

Weitere Informationen finden Sie auf der Dokumentationsseite „Vorgehensweise: Erstellen einer Project-App, die REST mit einem lokalen Project Server-OData-Dienst verwendet“ unter bit.ly/T80W2H.

Da „ProjectDocument“ das Document-Objekt erweitert, erfasst das Office.context.document-Objekt einen Verweis auf das aktive Projekt – ähnlich wie in andere Hostanwendungen eingefügte Apps. Die in Project verfügbaren asynchronen Methoden weisen ähnliche Signaturen wie andere Methoden in der JavaScript-API für Office auf. Die getProjectFieldAsync-Methode hat beispielsweise drei Parameter:

  • fieldId: Gibt das Feld an, das im Objekt für den Rückrufparameter zurückgegeben werden soll. Die Office.ProjectProjectFields-Enumeration umfasst 12 Felder, wie z. B. die Projekt-GUID, das Startdatum, das Enddatum und (sofern vorhanden) die Project Server-URL oder die URL der SharePoint-Aufgabenliste.
  • asyncContext: (Optional) Ein benutzerdefinierter Typ, der im asyncResult-Objekt zurückgegeben wird.
  • callback: Enthält einen Verweis auf eine Funktion, die bei Rückgabe des Aufrufs ausgeführt wird und Optionen zum Handhaben von Erfolg oder Fehlern umfasst.

Wie Sie in Abbildung 12 sehen, werden die Methoden für Apps in Project ähnlich wie die für in anderen Anwendungen gehostete Apps verwendet. In dem Skriptfragment ruft eine lokal definierte Funktion eine Routine auf, durch die eine Fehlermeldung in der App angezeigt wird. Das Skript verwendet nicht den asyncContext-Parameter.

Abbildung 12: Abrufen der GUID eines Felds aus einem in ein Projekt eingefügten Aufgabenbereich

var _projectUid = "";
// Get the GUID of the active project.
function getProjectGuid() {
  Office.context.document.getProjectFieldAsync(
    Office.ProjectProjectFields.GUID,
    function (asyncResult) {
      if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
        _projectUid = asyncResult.value.fieldValue;
        }
      else {
        // Display error message to user.
      }
    }
  );
}

Die getProjectFieldAsync-Methode kann zwar nur 12 Felder für das allgemeine Projekt abrufen, die getTaskFieldAsync-Methode kann aber mithilfe der ProjectTaskFields-Enumeration ein beliebiges der 282 unterschiedlichen Felder für eine Aufgabe abrufen. Und die getResourceFieldAsync-Methode kann mithilfe der ProjectResourceFields-Enumeration eines der 200 Felder für eine Ressource abrufen. Zu den weiteren allgemeinen Methoden im ProjectDocument-Objekt zählen „getSelectedDataAsync“, die ausgewählte Textdaten in einer der unterstützten Ansichten zurückgibt, und „getTaskAsync“, die verschiedene Elemente allgemeiner Daten für eine ausgewählte Aufgabe zurückgibt. Die Aufgabenbereich-Apps können 16 verschiedene Ansichten in Project verarbeiten.

Aufgabenbereich-Apps können außerdem Ereignishandler hinzufügen oder entfernen, wenn der Benutzer eine Ansicht ändert oder eine Aufgabe oder Ressource auswählt.

Ereignisse in einer App für Office

Die JavaScript-API für Office ermöglicht Ihnen mithilfe von Ereignissen die Erstellung von Apps mit höherer Reaktionsfähigkeit. Im Ereignismodell für die API werden vier wichtige Ereignisszenarios unterstützt, die grundlegend für die Entwicklung von Apps für Office sind (dies wird später genauer erläutert). Wenn Sie diese vier Szenarios kennen und verstehen, verfügen Sie damit auch über ein profundes Verständnis des Ereignismodells für die API.

Das Ereignismodell für die Apps für Office ist außerdem durchgehend einheitlich, sodass eine Kenntnis des allgemeinen Designs für das Ereignishandling Ihr Verständnis dieses wichtigen Konzepts vervollständigt.

Im allgemeinen Design für Ereignisse in der Apps für Office-API sind folgenden Objekten Ereignisse zugeordnet:

  • Bindung
  • CustomXMLPart
  • Dokument
  • RoamingSettings (Mail-Apps)
  • Einstellungen

Neben den zugeordneten Ereignissen weist jedes aufgeführte Objekt zwei Methoden zum Verarbeiten von Ereignissen auf:

  • addHandlerAsync
  • removeHandlerAsync

Da mit der removeHandlerAsync-Methode einfach nur ein Handler von einem Ereignis gelöst wird und da ihre Signatur fast mit der von „addHandlerAsync“ identisch ist, konzentrieren wir uns im nächsten Abschnitt nur auf „addHandlerAsync“.

Anmerkung: Es gibt einen sehr wichtigen Unterschied zwischen der remove­HandlerAsync- und der addHandlerAsync-Methode. Der handler-Parameter ist bei „removeHandlerAsync“ optional. Ist keiner angegeben, werden alle Handler für den angegebenen Ereignistyp entfernt.

Die AddHandlerAsync-Methode

Die addHandlerAsync-Methode verbindet einen Ereignishandler mit dem angegebenen Ereignis und weist die gleiche Signatur für jedes Objekt auf, das sie implementiert:

objectName.addHandlerAsync(eventType, handler [, options], callback);

Schauen wir uns jetzt die Parameter für diese Methode an.

EventType-Parameter Der erforderliche eventType-Parameter verwendet eine EventType-Enumeration, die der Methode mitteilt, welcher Ereignistyp verbunden werden soll.

Handler-Parameter Auf den eventType-Parameter folgt der handler-Parameter. Bei dem Handler kann es sich entweder um eine benannte Funktion oder eine anonyme Inlinefunktion handeln. Wie beim Ereignismodell vieler Programmiersprachen ruft die Laufzeit der Apps für Office den Handler auf und übergibt als einzigen Parameter ein Ereignisobjektargument. Wenn Sie eine anonyme Inlinefunktion für den handler-Parameter verwenden, besteht außerdem der einzige Weg, den Handler zu entfernen, darin, alle Handler mit „removeHandler­Async“ aus dem Ereignis zu entfernen und den handler-Parameter nicht anzugeben.

Options-Parameter Bei allen asynchronen Funktionen in der Apps für Office-API können Sie ein Objekt angeben, das optionale Param­eter enthält. Bei den addHandlerAsync-Methoden können Sie jedoch nur den optionalen Parameter „asyncContext“ angeben. Mit diesem Parameter können Sie über die asynchrone Methode, die Sie innerhalb des Rückrufs abrufen können, alle gewünschten Daten übergeben.

Callback-Parameter Der Rückruf funktioniert wie überall sonst in der Apps for Office-API mit einer entscheidenden Ausnahme: der value-Eigenschaft des AsyncResult-Objekts. Wie bereits zuvor in diesem Artikel beschrieben, übergibt die Laufzeit ein Async­Result-Objekt, wenn sie einen Rückruf aufruft, und Sie verwenden die value-Eigenschaft des AsyncResult-Objekts, um den Rückgabewert des asynchronen Aufrufs zu erhalten. Bei den Rückrufen in der addHandlerAsync-Methode ist der Wert des Async­Result-Objekts immer undefiniert.

In Abbildung 13 wird gezeigt, wie die addHandlerAsync-Methode für das DocumentSelectionChanged-Ereignis im Code umgesetzt wird (im Code wird davon ausgegangen, dass Sie über ein <div>-Element mit dem id-Attributwert „message“ verfügen).

Abbildung 13: Verbinden eines Ereignishandlers für das DocumentSelection­Changed-Ereignis unter Verwendung der Document.addHandlerAsync-Methode

Office.initialize = function (reason) {
  $(document).ready(function () {       
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged, onDocSelectionChanged,
        addHandlerCallback);
      Office.context.document.addHandlerAsync(
        Office.EventType.DocumentSelectionChanged, onDocSelectionChanged2,
        addHandlerCallback2);
  });
};
function onDocSelectionChanged(docSelectionChangedArgs) {
  write("onDocSelectionChanged invoked each event.");
}
function onDocSelectionChanged2(docSelectionChangedArgs) {
  write("onDocSelectionChanged2 invoked each event.");
}
function addHandlerCallback(asyncResult) {
  write("addHandlerCallback only called once on app initialize.");
}
function addHandlerCallback2(asyncResult) {
  write("addHandlerCallback2 only called once on app initialize.");
}
function write(message) {$('#message').append(message + "\n");

Beim Initialisieren der App verbindet der Code in Abbildung 13 die onDocSelectionChanged- und onDocSelectionChanged2-Handlerfunktionen mit dem Document­SelectionChanged-Ereignis, sodass mehr als ein Ereignishandler für das gleiche Ereignis vorhanden sein kann. Beide Handler schreiben einfach in das <div>-Element „message“, wenn das DocumentSelectionChanged-Ereignis ausgelöst wird.

Die Aufrufe an „addHandlerAsync“ enthalten außerdem die Rückrufe „addHandlerCallback“ und „addHandlerCallback2“. Die Rückrufe schreiben auch in das <div>-Element „message“, werden aber nur einmal aufgerufen, wenn „addHandlerAsync“ abgeschlossen wird.

Auf die gleiche Weise können Sie auch die addHandlerAsync-Methode verwenden, um Ereignishandler für jedes Ereignis in der JavaScript-API für Office zu verbinden.

Schlüsselszenarios im Ereignismodell für Apps für Office

Wie bereits erwähnt, gibt es in der JavaScript-API für Office vier Schlüsselszenarios, die Sie im Ereignismodell für Apps für Office zur Orientierung nutzen können. Alle Ereignisse in der API entsprechen einem dieser vier zentralen Ereignisszenarios:

  • Office.initialize-Ereignisse
  • Auswahländerungsereignisse auf Dokumentebene
  • Auswahl- und Datenänderungsereignisse auf Bindungsebene
  • Einstellungsänderungsereignisse

Office.initialize-Ereignisse Eines der häufigsten Ereignisse in der JavaScript-API für Office ist das Office.initialize-Ereignis. Das initialize-Ereignis tritt bei jeder erstellten App für Office auf. Im Prinzip ist es der erste Teil Ihres Codes, der von der Laufzeit ausgeführt wird.

Wenn Sie sich den Startcode anschauen, den Visual Studio 2012 für ein neues App für Office-Projekt zur Verfügung stellt, stellen Sie fest, dass mit den ersten Zeilen des Startcodes in der Datei „ProjectName.js“ ein Ereignishandler für das Office.initialize-Ereignis verknüpft wird, wie hier gezeigt:

// This function is run when the app is ready to
// start interacting with the host application;
// it ensures the DOM is ready before adding click handlers to buttons.
Office.initialize = function (reason) { /* handler code */ };

Wie Sie bereits aus dem Abschnitt zur Objektmodellhierarchie im vorherigen Artikel wissen, ist das Office-Objekt das erste Objekt in der JavaScript-API für Office und gibt die Instanz Ihrer App zur Laufzeit wieder. Das Initialize-Ereignis wird ausgelöst, wenn die Apps für Office-Laufzeit vollständig geladen wurde und bereit zur Interaktion mit Ihrer App ist. Der Ereignishandler des Initialize-Ereignisses ist also eigentlich der „Handshake“, der zwischen Ihrer App und der Laufzeit durchgeführt werden muss, bevor der restliche Code ausgeführt werden kann.

Die Funktion, die Sie als Handler für das Office.initialize-Ereignis bereitstellen müssen, verwendet ein einziges Argument – eine InitializationReason-Enumeration. Die Initialization­Reason-Enumeration hat nur zwei Enumerationen: inserted und documentOpened:

  • Mit „inserted“ wird angegeben, dass die App initialisiert wird, da sie gerade in das Dokument eingefügt wurde.
  • Mit „documentOpened“ wird angegeben, dass die App initialisiert wird, da das Dokument geöffnet wurde, in der sich die App befindet.

Die Laufzeit übergibt die InitializationReason-Enumeration als einziges Argument für die Handlerfunktion. Von hier aus können Sie die Verzweigung des Codes je nach Grund festlegen.

Ein Vorschlag für die Umsetzung:

Office.initialize = function (reason) {
  // Display initialization reason.
  if (reason == "inserted")
  write("The app was just inserted.");
  if (reason == "documentOpened")
  write(
    "The app is already part of the document.");
}
// Function that writes to a div with
// id='message' on the page.
function write(message){
  document.getElementById(
  'message').innerText += message;
}

Anmerkung: Beim vorherigen Codeausschnitt wird davon ausgegangen, dass Sie über ein <div>-Element mit dem id-Attributwert „message“ verfügen.

Sie müssen zwar nichts in die Funktion einfügen, die Sie als Handler zur Verfügung stellen, aber die Funktion muss vorhanden sein, da die App ansonsten beim Starten einen Fehler ausgibt.

Der Ereignishandler für das Office.initialize-Ereignis eignet sich übrigens auch gut zum Initialisieren anderer Frameworks, die Sie möglicherweise in Ihrer App verwenden möchten, wie z. B. „jQuery“. Im Startcode von Visual Studio für neue Apps für Office-Projekte sehen Sie wieder einen Code wie in Abbildung 14.

Abbildung 14: Initialisieren anderer Frameworks im Office.initialize-Ereignishandler

Office.initialize = function (reason) {
  $(document).ready(function () {
    $('#getDataBtn').click(function () { getData('#selectedDataTxt'); });
    // If setSelectedDataAsync method is supported
    // by the host application, setDatabtn is hooked up
    // to call the method, else setDatabtn is removed.
    if (Office.context.document.setSelectedDataAsync) {
        $('#setDataBtn').click(function () { setData('#selectedDataTxt'); });
    }
    else {
      $('#setDataBtn').remove();
    }
  });
};

Das jQuery-Ereignis „.ready“ wird innerhalb des Office.initialize-Ereignishandlers verarbeitet. Damit wird sichergestellt, dass die JavaScript-API für Office geladen und bereit ist, bevor der Aufruf durch den JQuery-Code erfolgt.

Auswahländerungsereignisse auf Dokumentebene Auswahländerungsereignisse auf Dokumentebene treten auf, wenn die Auswahl im Dokument die Position ändert. Wenn der Benutzer z. B. von der aktuellen Auswahl in einem Word-Dokument zu einem anderen Textbereich, Objekt oder einer anderen Position im Dokument wechselt, wird ein Ereignis auf Dokumentebene für die Auswahländerung ausgelöst.

Der folgende Code zeigt, wie auf eine Änderung der aktuellen Auswahl reagiert wird:

function addEventHandlerToDocument() {
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged,
      MyHandler);
  }
  function MyHandler(eventArgs) {
    doSomethingWithDocument(eventArgs.document);

Auswahl- und Datenänderungsereignisse auf Bindungsebene Bindungen im Apps für Office-Objektmodell stellen eine Möglichkeit dar, auf einen bestimmten Bereich in einem Dokument (oder in einer Tabelle) einheitlich zugreifen zu können, indem eine Verknüpfung (oder Bindung) zu diesem eindeutig benannten Bereich im Dokument hergestellt wird. Um mit Bindungen zu arbeiten, müssen Sie zunächst eine Bindung mithilfe der API-Methoden erstellen. Sie können dann mithilfe ihres eindeutigen Bezeichners auf diese spezifische Bindung verweisen.

Bindungen lösen auch Ereignisse aus, und Ihre App kann auf diese Ereignisse je nach Bedarf reagieren. Bindungen lösen insbesondere ein Ereignis aus, wenn sich die Auswahl im Bindungsbereich ändert und wenn sich Daten innerhalb des Bindungsbereichs ändern. In den folgenden zwei Codeausschnitten wird gezeigt, wie Änderungen an der Auswahl und den Daten in einer bestimmten Bindung verarbeitet werden (bei beiden Codeausschnitten wird davon ausgegangen, dass ein <div>-Element mit dem id-Attributwert „message“ vorliegt).

Reaktion auf das Binding.bindingSelectionChanged-Ereignis:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingSelectionChanged,
    onBindingSelectionChanged);
}
function onBindingSelectionChanged(eventArgs) {
  write(eventArgs.binding.id + " has been selected.");
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

Reaktion auf das Binding.bindingDataChanged-Ereignis:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingDataChanged, onBindingDataChanged);
}
function onBindingDataChanged(eventArgs) {
  write("Data has 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;
}

Einstellungsänderungsereignisse Das Apps für Office-Objektmodell bietet Entwicklern die Möglichkeit, Einstellungen für ihre App beizubehalten. Das Settings-Objekt fungiert als Eigenschaftsbehälter, in dem benutzerdefinierte App-Einstellungen als Schlüssel/Wert-Paare gespeichert werden. Dem Settings-Objekt ist auch ein Ereignis zugeordnet (Settings.settingsChanged), das ausgelöst wird, wenn eine gespeicherte Einstellung geändert wird.

Weitere Informationen zum Settings.settingsChanged-Ereignis finden Sie in der MSDN-Dokumentation für die JavaScript-API für Office unter bit.ly/U92Sbe.

Ausblick: Komplexere Themen

In diesem zweiten Artikel der Reihe haben wir die Grundlagen für das Einfügen und Abrufen von Office-Dateiinhalten über eine App für Office beleuchtet. Wir haben gezeigt, wie Auswahldaten abgerufen und eingefügt werden und wie alle Dateidaten abgerufen werden. Wir haben uns angeschaut, wie Projekt-, Aufgaben-, Ressourcen- und Ansichtsdaten über eine App für Projekt abgerufen werden. Zum Schluss haben wir noch die Ereignisse in der JavaScript-API für Office und deren Einbeziehung in den Code behandelt.

Anmerkung: Wir möchten uns bei Jim Corbin bedanken, Redakteur im Bereich Programmierung in der Office Division, der einen großen Beitrag zum Thema Apps für Project geleistet hat.

Als Nächstes schauen wir uns dann etwas komplexere Themen im Rahmen der JavaScript-API für Office an: Datenbindungen und benutzerdefinierte XML-Elemente.

Stephen Oliver arbeitet als Redakteur im Bereich Programmierung in der Office Division und ist Microsoft Certified Professional Developer (SharePoint 2010). Er schreibt die Entwicklerdokumentation für Excel Services, Word Automation Services und PowerPoint Automation Services und hat bei der Betreuung und dem Entwurf der Excel Mashup-Website unter ExcelMashup.com mitgewirkt.

Eric Schmidt arbeitet als Redakteur im Bereich Programmierung in der Office Division. Er hat verschiedene Codebeispiele für Apps für Office erstellt, darunter das beliebte Codebeispiel „Beibehalten benutzerdefinierter Einstellungen“. Außerdem hat er Artikel verfasst und Videos erstellt, in denen andere Produkte und Technologien im Bereich der Office-Programmierbarkeit erläutert werden.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Mark Brewster, Shilpa Kothari und Juan Balmori Labra
Mark Brewster erwarb 2008 einen Bachelor of Science in Mathematik und Computerwissenschaften an der Universität Arizona und entwickelt seit vier Jahren Software bei Microsoft. In seiner Freizeit fährt er gerne und professionell Fahrrad, hört Musik und trinkt gerne Bier.

Shilpa Kothari (Bhavsar) ist Software Engineer in Test bei Microsoft. Sie hat an verschiedenen Microsoft-Produkten mitgearbeitet, unter anderem Bing Mobile, Visual Studio und Office. Sie beschäftigt sich am liebsten mit den Themen Qualitätssicherung für Software und Benutzerfreundlichkeit und kann unter shilpak@microsoft.com erreicht werden.

Juan Balmori Labra ist Program Manager und hat die letzten drei Jahre an der Microsoft Office JavaScript-API gearbeitet. Zuvor arbeitete er an der Office 2010-Version mit Business Connectivity Services und Duet. Bevor er sich seinen Traum erfüllen und nach Redmond ziehen konnte, arbeitete Juan Balmori Labra bei Microsoft Mexico als führender Architekt für Beratungsdienste im öffentlichen Sektor.