Erstellen eines Project-Add-Ins, das REST mit einem lokalen Project Server-OData-Dienst verwendet

In diesem Artikel wird beschrieben, wie Sie ein Aufgabenbereich-Add-In für Project Professional erstellen, das Kosten- und Arbeitsdaten im aktiven Projekt mit den Durchschnittswerten für alle Projekte im aktuellen Project Web App-instance vergleicht. Das Add-In verwendet REST mit der jQuery-Bibliothek, um auf den ProjectData OData-Berichterstellungsdienst in Project Server zuzugreifen.

Der Code in diesem Artikel basiert auf einem von Saurabh Sanghvi und Arvind Iyer (Microsoft Corporation) entwickelten Beispiel.

Voraussetzungen

Im Folgenden sind die Voraussetzungen für das Erstellen eines Project-Aufgabenbereich-Add-Ins aufgeführt, das den ProjectData-Dienst einer Project Web App-instance in einer lokalen Installation von Project Server liest.

  • Project Professional ist erforderlich, um eine Verbindung mit Project Web App herzustellen. Auf dem Entwicklungscomputer muss Project Professional installiert sein, um das F5-Debuggen mit Visual Studio zu aktivieren.

    Hinweis

    Project Standard können auch Aufgabenbereich-Add-Ins hosten, sich aber nicht bei Project Web App anmelden.

  • Visual Studio 2015 mit Office Developer Tools für Visual Studio enthält Vorlagen zum Erstellen von Office- und SharePoint-Add-Ins. Stellen Sie sicher, dass Sie die neueste Version von Office Developer Tools installiert haben. Weitere Informationen finden Sie im Abschnitt Tools der Office-Add-Ins und SharePoint-Downloads.

  • Die Verfahren und Codebeispiele in diesem Artikel greifen auf den ProjectData-Dienst von Project Server in einer lokalen Domäne zu. Die jQuery-Methoden in diesem Artikel funktionieren nicht mit Project im Web.

    Vergewissern Sie sich, dass auf den ProjectData-Dienst von Ihrem Entwicklungscomputer aus zugegriffen werden kann.

Vorgehensweise 1: Stellen Sie sicher, dass auf den ProjectData-Dienst zugegriffen werden kann.

  1. Damit Ihr Browser die XML-Daten aus einer REST-Abfrage direkt anzeigen kann, deaktivieren Sie die Feedleseansicht. Informationen dazu, wie Sie dies in Internet Explorer tun, finden Sie unter Verfahren 1, Schritt 4 unter Abfragen von OData-Feeds für Project-Berichtsdaten.

  2. Fragen Sie den ProjectData-Dienst in Ihrem Browser mit der folgenden URL ab: http://ServerName /ProjectServerName /_api/ProjectData. Wenn die Project Web App-instance beispielsweise ist, zeigt http://MyServer/pwader Browser die folgenden Ergebnisse an.

    <?xml version="1.0" encoding="utf-8"?>
        <service xml:base="http://myserver/pwa/_api/ProjectData/"
        xmlns="https://www.w3.org/2007/app"
        xmlns:atom="https://www.w3.org/2005/Atom">
        <workspace>
            <atom:title>Default</atom:title>
            <collection href="Projects">
                <atom:title>Projects</atom:title>
            </collection>
            <collection href="ProjectBaselines">
                <atom:title>ProjectBaselines</atom:title>
            </collection>
            <!-- ... and 33 more collection elements -->
        </workspace>
        </service>
    
  3. Möglicherweise müssen Sie Ihre Netzwerkanmeldeinformationen eingeben, um die Ergebnisse anzeigen zu können. Wenn im Browser „Fehler 3, Zugriff verweigert“ angezeigt wird, besitzen Sie entweder keine Anmeldeberechtigung für die Project Web App-Instanz, oder es liegt ein Netzwerkproblem vor, für das Unterstützung von einem Administrator erforderlich ist.

Verwenden von Visual Studio zum Erstellen eines Aufgabenbereich-Add-Ins für Project

Office Developer Tools für Visual Studio enthält eine Vorlage für Aufgabenbereich-Add-Ins für Project. Wenn Sie eine Projektmappe mit dem Namen HelloProjectOData erstellen, enthält die Projektmappe die folgenden beiden Visual Studio-Projekte:

  • Das Add-In-Projekt nimmt den Namen der Projektmappe an. Sie enthält die XML-Manifestdatei für das Add-In und zielt auf die .NET Framework 4.5 ab. Prozedur 3 zeigt die Schritte zum Ändern des Manifests für das HelloProjectOData-Add-In .

  • Das Webprojekt heißt HelloProjectODataWeb. Es enthält die Webseiten, JavaScript-Dateien, CSS-Dateien, Bilder, Verweise und Konfigurationsdateien für den Webinhalt im Aufgabenbereich. Das Webprojekt ist auf .NET Framework 4 ausgerichtet. In Prozedur 4 und Prozedur 5 wird veranschaulicht, wie die Dateien im Webprojekt angepasst werden, um die Funktionalität des Add-ins HelloProjectOData zu erstellen.

Vorgehensweise 2: Erstellen des HelloProjectOData-Add-Ins für Project

  1. Führen Sie Visual Studio 2015 als Administrator aus, und wählen Sie dann auf der Startseite Neues Projekt aus.

  2. Erweitern Sie im Dialogfeld Neues Projekt die Knoten Vorlagen, Visual C# und Office/SharePoint, und wählen Sie dann Office-Add-Ins aus. Wählen Sie .NET Framework 4.5.2 in der Dropdownliste Zielframework oben im mittleren Bereich und dann Office-Add-In aus (siehe nächster Screenshot).

  3. Wählen Sie zum Ablegen beider Visual Studio-Projekte im selben Verzeichnis Projektmappenverzeichnis erstellen aus, und navigieren Sie zum gewünschten Speicherort.

  4. Geben Sie im Feld Name den NamenHelloProjectOData ein, und wählen Sie dann OK aus.

    Abbildung 1: Erstellen eines Office-Add-Ins

    Erstellen Sie ein Office-Add-In.

  5. Wählen Sie im Dialogfeld Add-In-Typ wählen die Option Aufgabenbereich , und wählen Sie Weiter (Siehe nächsten Screenshot).

    Abbildung 2: Auswählen des Typs des zu erstellenden Add-Ins

    Wählen Sie den Typ des zu erstellenden Add-Ins aus.

  6. Deaktivieren Sie im Dialogfeld Hostanwendungen auswählen alle Kontrollkästchen mit Ausnahme von Projekt (siehe folgenden Screenshot), und wählen Sie dann Fertig stellen aus.

    Abbildung 3: Auswählen der Hostanwendung

    Wählen Sie Project als einzige Hostanwendung aus.

    Visual Studio erstellt das Projekt HelloProjectOdata und das Projekt HelloProjectODataWeb .

Im Ordner AddIn (siehe folgenden Screenshot) ist die Datei „App.css“ für benutzerdefinierte CSS-Stile. Im Start-Unterordner enthält die Datei Home.html Verweise auf die CSS-Dateien und die JavaScript-Dateien, die das Add-In verwendet, sowie die HTML5-Inhalte für das Add-In. Darüber hinaus dient die Datei Home.js für Ihren benutzerdefinierten JavaScript-Code. Der Skripte-Ordner enthält die Dateien der jQuery-Bibliothek. Der Office-Unterordner enthält die JavaScript-Bibliotheken, wie z. B. office.js und project-15.js, sowie die Sprachbibliotheken für Standard-Zeichenfolgen in den Office-Add-Ins. Im Inhalte-Ordner enthält die Datei Office.css die Standardformate für alle Office-Add-Ins.

Abbildung 4: Anzeigen der Standardwebprojektdateien in Projektmappen-Explorer

Zeigen Sie die Webprojektdateien im Projektmappen-Explorer an.

Das Manifest für das Projekt HelloProjectOData ist die HelloProjectOData.xml-Datei. Sie können das Manifest optional ändern, um eine Beschreibung des Add-Ins, einen Verweis auf ein Symbol, Informationen zu zusätzlichen Sprachen und andere Einstellungen hinzuzufügen. Prozedur 3 ändert einfach den Anzeigenamen und die Beschreibung des Add-Ins und fügt ein Symbol hinzu.

Weitere Informationen zum Manifest finden Sie unter Office-Add-Ins-Manifest und Schemareferenz für Office-Add-Ins-Manifeste.

Verfahren 3. Ändern des Add-In-Manifests

  1. Öffnen Sie in Visual Studio die Datei "HelloProjectOData.xml".

  2. Der Standardanzeigename ist der Name des Visual Studio-Projekts ("HelloProjectOData"). Ändern Sie beispielsweise den Standardwert des <DisplayName-Elements> in "Hello ProjectData".

  3. Die Standardbeschreibung lautet „HelloProjectODatHelloProjectOData". Ändern Sie beispielsweise den Standardwert des Description-Elements zu "Test-REST-Abfragen des ProjectData-Diensts".

  4. Fügen Sie ein Symbol hinzu, das in der Dropdownliste Office-Add-ins auf der Registerkarte PROJEKT des Menübands angezeigt wird. Sie können eine Symboldatei in der Visual Studio-Projektmappe hinzufügen oder eine URL für ein Symbol verwenden.

Die folgenden Schritte zeigen, wie Sie der Visual Studio-Projektmappe eine Symboldatei hinzufügen.

  1. Navigieren Sie Projektmappen-Explorer zum Ordner Images.

  2. Damit das Symbol in der Dropdownliste Office-Add-ins angezeigt wird, muss das Symbol ein Format von 32 x 32 Pixeln aufweisen. Verwenden Sie Ihr eigenes 32 x 32-Symbol; oder kopieren Sie das folgende Bild in eine Datei mit dem Namen NewIcon.png, und fügen Sie diese Datei dann dem HelloProjectODataWeb\Images Ordner hinzu.

    Symbol für die HelloProjectOData-App.

  3. Fügen Sie im HelloProjectOData.xml Manifest unterhalb des <Description-Elements> ein <IconUrl-Element> hinzu, wobei der Wert der Symbol-URL der relative Pfad zur 32x32-Symboldatei ist. Fügen Sie beispielsweise die folgende Zeile hinzu: <IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />. Die HelloProjectOData.xml Manifestdatei enthält nun Folgendes (Ihr <ID-Wert> unterscheidet sich):

    <?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" xsi:type="TaskPaneApp">
        <!--IMPORTANT! Id must be unique for each add-in. If you copy this manifest ensure that you change this id to your own GUID. -->
        <Id>c512df8d-a1c5-4d74-8a34-d30f6bbcbd82</Id>
        <Version>1.0</Version>
        <ProviderName> [Provider name]</ProviderName>
        <DefaultLocale>en-US</DefaultLocale>
        <DisplayName DefaultValue="Hello ProjectData" />
        <Description DefaultValue="Test REST queries of the ProjectData service"/>
        <IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />
        <SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />
        <Hosts>
            <Host Name="Project" />
        </Hosts>
        <DefaultSettings>
            <SourceLocation DefaultValue="~remoteAppUrl/AddIn/Home/Home.html" />
        </DefaultSettings>
        <Permissions>ReadWriteDocument</Permissions>
    </OfficeApp>
    

Erstellen des HTML-Inhalts für das HelloProjectOData-Add-In

Das HelloProjectOData-Add-In ist ein Beispiel, das Debugging und Fehlerausgabe enthält. es ist nicht für die Verwendung in der Produktion vorgesehen. Bevor Sie mit dem Codieren des HTML-Inhalts beginnen, entwerfen Sie die Benutzeroberfläche und Benutzeroberfläche für das Add-In, und beschreiben Sie die JavaScript-Funktionen, die mit dem HTML-Code interagieren. Weitere Informationen finden Sie unter Entwurfsrichtlinien für Office-Add-Ins.

Im Aufgabenbereich wird oben der Add-In-Anzeigename angezeigt, der den Wert des <DisplayName-Elements> im Manifest darstellt. Das body-Element in der HelloProjectOData.html-Datei enthält die anderen Benutzeroberflächenelemente wie folgt:

  • Ein Untertitel gibt die allgemeine Funktion oder den allgemeinen Typ des Vorgangs an, z. B. ODATA-REST-ABFRAGE.

  • Die Schaltfläche Get ProjectData Endpoint (ProjectData-Endpunkt abrufen ) ruft die setOdataUrl Funktion auf, um den Endpunkt des ProjectData-Diensts abzurufen und in einem Textfeld anzuzeigen. Ist Project nicht mit Project Web App verbunden, wird von dem Add-in ein Fehlerhandler zum Anzeigen einer Popupmeldung aufgerufen.

  • Die Schaltfläche Alle Projekte vergleichen ist deaktiviert, bis das Add-In einen gültigen OData-Endpunkt erhält. Wenn Sie die Schaltfläche auswählen, wird die retrieveOData Funktion aufgerufen, die eine REST-Abfrage verwendet, um Projektkosten- und Arbeitsdaten aus dem ProjectData-Dienst abzurufen.

  • In einer Tabelle werden die durchschnittlichen Werte für Projektkosten, Ist-Kosten, Arbeit und abgeschlossenen Prozentsatz angezeigt. Die Tabelle vergleicht darüber hinaus die derzeitigen Werte des aktuellen Projekts mit dem Durchschnitt. Ist der aktuelle Wert höher als der Durchschnitt für alle Projekte, wird der Wert rot dargestellt. Ist der aktuelle Wert niedriger als der Durchschnitt, wird er grün dargestellt. Ist der aktuelle Wert nicht verfügbar, wird in der Tabelle NV in Blau angezeigt.

    Die retrieveOData Funktion ruft die parseODataResult -Funktion auf, die Werte für die Tabelle berechnet und anzeigt.

    Hinweis

    In diesem Beispiel werden die Kosten- und Arbeitsdaten für das aktive Projekt aus den veröffentlichen Werten abgeleitet. Wenn Sie Werte in Project ändern, enthält der ProjectData-Dienst die Änderungen erst, wenn das Projekt veröffentlicht wird.

Prozedur 4. Erstellen des HTML-Inhalts

  1. Fügen Sie im head-Element der Home.html-Datei alle zusätzlichen Linkelemente für CSS-Dateien hinzu, die ihr Add-In verwendet. Die Visual Studio-Projektvorlage enthält eine Verknüpfung zur Datei „App.css", die für benutzerdefinierte CSS-Stile verwendet werden kann.

  2. Fügen Sie alle zusätzlichen Skriptelemente für JavaScript-Bibliotheken hinzu, die ihr Add-In verwendet. Die Projektvorlage enthält Links für die dateien jQuery- [version].js, office.js und MicrosoftAjax.js dateien im Ordner Scripts .

    Hinweis

    Ändern Sie vor der Bereitstellung des Add-ins den Verweis auf „office.js“ zum Verweis auf das Netzwerk für die Inhaltsübermittlung (Content Delivery Network, CDN). Der CDN-Verweis gibt die aktuelle Version an und bietet eine bessere Leistung.

    Das HelloProjectOData-Add-In verwendet auch eine SurfaceErrors.js-Datei , die Fehler in einer Popupnachricht anzeigt. Kopieren Sie den Code aus dem AbschnittSurfaceErrors.js Datei dieses Artikels in den Ordner Scripts\Office des Projekts HelloProjectODataWeb als neue Datei mit dem Namen SurfaceErrors.js.

    Im Folgenden finden Sie den aktualisierten HTML-Code für das head-Element mit der zusätzlichen Zeile für die SurfaceErrors.js-Datei .

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <title>Test ProjectData Service</title>
    
    <link rel="stylesheet" type="text/css" href="../Content/Office.css" />
    
    <!-- Add your CSS styles to the following file. -->
    <link rel="stylesheet" type="text/css" href="../Content/App.css" />
    
    <!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. -->
    <!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> -->
    <script src="../Scripts/jquery-1.7.1.js"></script>
    
    <!-- Use the CDN reference to office.js when deploying your add-in. -->
    <!--<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>-->
    
    <!-- Use the local script references for Office.js to enable offline debugging -->
    <script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
    <script src="../Scripts/Office/1.0/Office.js"></script>
    
    <!-- Add your JavaScript to the following files. -->
    <script src="../Scripts/HelloProjectOData.js"></script>
    <script src="../Scripts/SurfaceErrors.js"></script>
    </head>
    <body>
    <!-- See the code in Step 3. -->
    </body>
    </html>
    
  3. Löschen Sie im Body-Element den vorhandenen Code aus der Vorlage, und fügen Sie dann den Code für die Benutzeroberfläche hinzu. Soll ein Element mit Daten gefüllt oder durch eine jQuery-Anweisung bearbeitet werden, muss das Element ein eindeutiges id-Attribut enthalten. Im folgenden Code werden die id-Attribute für die elemente button, span und td (Tabellenzellendefinition), die jQuery-Funktionen verwenden, fett formatiert dargestellt.

    Der folgende HTML-Code fügt ein Grafikbild (NewLogo.png) hinzu, bei dem es sich um ein Firmenlogo handeln kann.

    <body>
        <div id="SectionContent">
        <div id="odataQueries">
            ODATA REST QUERY
        </div>
        <div id="odataInfo">
            <button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button>
            <br /><br />
            <span class="rest" id="projectDataEndPoint">Endpoint of the 
                <strong>ProjectData</strong> service</span>
            <br />
        </div>
        <div id="compareProjectData">
            <button class="button-wide" disabled="disabled" id="compareProjects"
            onclick="retrieveOData()">Compare All Projects</button>
            <br />
        </div>
        </div>
        <div id="corpInfo">
            <table class="infoTable" aria-readonly="True" style="width: 100%;">
                <tr>
                    <td class="heading_leftCol"></td>
                    <td class="heading_midCol"><strong>Average</strong></td>
                    <td class="heading_rightCol"><strong>Current</strong></td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Cost</strong></td>
                    <td class="row_midCol" id="AverageProjectCost">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectCost">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Actual Cost</strong></td>
                    <td class="row_midCol" id="AverageProjectActualCost">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectActualCost">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Work</strong></td>
                    <td class="row_midCol" id="AverageProjectWork">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectWork">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project % Complete</strong></td>
                    <td class="row_midCol" id="AverageProjectPercentComplete">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectPercentComplete">&amp;nbsp;</td>
                </tr>
            </table>
        </div>
        <img alt="Corporation" class="logo" src="../../images/NewLogo.png" />
        <br />
        <textarea id="odataText" rows="12" cols="40"></textarea>
    </body>
    

Erstellen des JavaScript-Codes für das Add-In

Die Vorlage für ein Project-Aufgabenbereich-Add-In enthält Standardinitialisierungscode, der dazu dient, grundlegende Aktionen zum Abrufen und Festlegen von Daten in einem Dokument für ein Office-Add-In zu veranschaulichen, das die allgemeinen APIs verwendet. Da Project keine Aktionen unterstützt, die in das aktive Projekt schreiben, und das HelloProjectOData-Add-In die -Methode nicht verwendetgetSelectedDataAsync, können Sie das Skript innerhalb der Office.initialize Funktion löschen und die Funktion und getData die setData Funktion in der Standarddatei HelloProjectOData.js löschen.

JavaScript enthält globale Konstanten für die REST-Abfrage und in verschiedenen Funktionen verwendete globale Variablen. Die Schaltfläche ProjectData-Endpunkt abrufen ruft die setOdataUrl Funktion auf, die die globalen Variablen initialisiert und bestimmt, ob Project mit Project Web App verbunden ist.

Der Rest der HelloProjectOData.js Datei enthält zwei Funktionen: Die retrieveOData Funktion wird aufgerufen, wenn der Benutzer Alle Projekte vergleichen auswählt, und die parseODataResult Funktion berechnet Mittelwerte und füllt dann die Vergleichstabelle mit Werten auf, die für Farbe und Einheiten formatiert sind.

Prozedur 5. Erstellen des JavaScript-Codes

  1. Löschen Sie den gesamten Code in der Standarddatei HelloProjectOData.js, und fügen Sie dann die globalen Variablen und Office.initialize die Funktion hinzu. Variablennamen, die alle Großbuchstaben sind, implizieren, dass es sich um Konstanten handelt; Sie werden später mit der _pwa Variablen verwendet, um die REST-Abfrage in diesem Beispiel zu erstellen.

    let PROJDATA = "/_api/ProjectData";
    let PROJQUERY = "/Projects?";
    let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'";
    let QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
    let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost";
    let _pwa;           // URL of Project Web App.
    let _projectUid;    // GUID of the active project.
    let _docUrl;        // Path of the project document.
    let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData
    
    // Ensure the Office.js library is loaded.
    Office.onReady(function() {
        // Office is ready.
        $(document).ready(function () {
            // The document is ready.
        });
    });
    
  2. Fügen Sie und verwandte Funktionen hinzu setOdataUrl . Die setOdataUrl Funktion ruft getProjectGuid und getDocumentUrl auf, um die globalen Variablen zu initialisieren. In der getProjectFieldAsync-Methode aktiviert die anonyme Funktion für den callback-Parameter die Schaltfläche Alle Projekte vergleichen mithilfe der removeAttr -Methode in der jQuery-Bibliothek und zeigt dann die URL des ProjectData-Diensts an. Ist Project nicht mit Project Web App verbunden, wird von der Funktion ein Fehler ausgegeben, der als Popupmeldung angezeigt wird. Die SurfaceErrors.js Datei enthält die throwError -Funktion.

    Hinweis

    Wenn Sie Visual Studio auf dem Project Server-Computer ausführen, heben Sie die Auskommentierung des Codes nach der Zeile auf, die die _pwa globale Variable initialisiert, um das Debuggen mit F5 zu verwenden. Um die Verwendung der jQuery-Methode ajax beim Debuggen auf dem Project Server-Computer zu aktivieren, müssen Sie den localhost Wert für die PWA-URL festlegen. Wenn Sie Visual Studio auf einem Remotecomputer ausführen, ist die localhost URL nicht erforderlich. Before you deploy the add-in, comment out that code.

    function setOdataUrl() {
        Office.context.document.getProjectFieldAsync(
            Office.ProjectProjectFields.ProjectServerUrl,
            function (asyncResult) {
                if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                    _pwa = String(asyncResult.value.fieldValue);
    
                    // If you debug with Visual Studio on a local Project Server computer, 
                    // uncomment the following lines to use the localhost URL.
                    //let localhost = location.host.split(":", 1);
                    //let pwaStartPosition = _pwa.lastIndexOf("/");
                    //let pwaLength = _pwa.length - pwaStartPosition;
                    //let pwaName = _pwa.substr(pwaStartPosition, pwaLength);
                    //_pwa = location.protocol + "//" + localhost + pwaName;
    
                    if (_pwa.substring(0, 4) == "http") {
                        _odataUrl = _pwa + PROJDATA;
                        $("#compareProjects").removeAttr("disabled");
                        getProjectGuid();
                    }
                    else {
                        _odataUrl = "No connection!";
                        throwError(_odataUrl, "You are not connected to Project Web App.");
                    }
                    getDocumentUrl();
                    $("#projectDataEndPoint").text(_odataUrl);
                }
                else {
                    throwError(asyncResult.error.name, asyncResult.error.message);
                }
            }
        );
    }
    
    // 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 {
                    throwError(asyncResult.error.name, asyncResult.error.message);
                }
            }
        );
    }
    
    // Get the path of the project in Project web app, which is in the form <>\ProjectName .
    function getDocumentUrl() {
        _docUrl = "Document path:\r\n" + Office.context.document.url;
    }
    
  3. Fügen Sie die retrieveOData -Funktion hinzu, die Werte für die REST-Abfrage verkettet und dann die ajax Funktion in jQuery aufruft, um die angeforderten Daten aus dem ProjectData-Dienst abzurufen. Die support.cors Variable ermöglicht cors (Cross-Origin Resource Sharing) mit der ajax Funktion. Wenn die support.cors Anweisung fehlt oder auf falsefestgelegt ist, gibt die ajax Funktion den Fehler No transport (Kein Transport ) zurück.

    Hinweis

    Der folgende Code funktioniert mit einer lokalen Installation von Project Server. Für Project im Web können Sie OAuth für die tokenbasierte Authentifizierung verwenden. Weitere Informationen finden Sie unter Behandeln von Richtlinieneinschränkungen aufgrund desselben Ursprungs in Office-Add-Ins.

    ajax Im Aufruf können Sie entweder den headers-Parameter oder den beforeSend-Parameter verwenden. Der complete-Parameter ist eine anonyme Funktion, sodass er sich im gleichen Bereich wie die Variablen in befindet retrieveOData. Die Funktion für den vollständigen Parameter zeigt Ergebnisse im odataText -Steuerelement an und ruft auch die parseODataResult -Methode auf, um die JSON-Antwort zu analysieren und anzuzeigen. Der error-Parameter gibt die benannte getProjectDataErrorHandler Funktion an, die eine Fehlermeldung in das odataText Steuerelement schreibt und die throwError Funktion auch verwendet, um eine Popupmeldung anzuzeigen.

    // Functions to get and parse the Project Server reporting data./
    
    // Get data about all projects on Project Server,
    // by using a REST query with the ajax method in jQuery.
    function retrieveOData() {
        let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2;
        let accept = "application/json; odata=verbose";
        accept.toLocaleLowerCase();
    
        // Enable cross-origin scripting (required by jQuery 1.5 and later).
        // This does not work with Project on the web.
        $.support.cors = true;
    
        $.ajax({
            url: restUrl,
            type: "GET",
            contentType: "application/json",
            data: "",      // Empty string for the optional data.
            //headers: { "Accept": accept },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("ACCEPT", accept);
            },
            complete: function (xhr, textStatus) {
                // Create a message to display in the text box.
                let message = "\r\ntextStatus: " + textStatus +
                    "\r\nContentType: " + xhr.getResponseHeader("Content-Type") +
                    "\r\nStatus: " + xhr.status +
                    "\r\nResponseText:\r\n" + xhr.responseText;
    
                // xhr.responseText is the result from an XmlHttpRequest, which
                // contains the JSON response from the OData service.
                parseODataResult(xhr.responseText, _projectUid);
    
                // Write the document name, response header, status, and JSON to the odataText control.
                $("#odataText").text(_docUrl);
                $("#odataText").append("\r\nREST query:\r\n" + restUrl);
                $("#odataText").append(message);
    
                if (xhr.status != 200 &amp;&amp; xhr.status != 1223 &amp;&amp; xhr.status != 201) {
                    $("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>");
                }
            },
            error: getProjectDataErrorHandler
        });
    }
    
    function getProjectDataErrorHandler(data, errorCode, errorMessage) {
        $("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n"
        + errorMessage);
        throwError(errorCode, errorMessage);
    }
    
  4. Fügen Sie die parseODataResult Funktion hinzu, die die JSON-Antwort vom OData-Dienst deserialisiert und verarbeitet. Die parseODataResult Funktion berechnet die Durchschnittswerte der Kosten- und Arbeitsdaten mit einer Genauigkeit von einer oder zwei Dezimalstellen, formatiert Werte mit der richtigen Farbe und fügt eine Einheit ( $, Stunden oder %) hinzu und zeigt dann die Werte in den angegebenen Tabellenzellen an.

    Wenn die GUID des aktiven Projekts mit dem ProjectId Wert übereinstimmt, wird die myProjectIndex Variable auf den Projektindex festgelegt. Wenn myProjectIndex angibt, dass das aktive Projekt auf Project Server veröffentlicht wird, formatiert und zeigt die parseODataResult Methode Kosten- und Arbeitsdaten für dieses Projekt an. Wenn das aktive Projekt nicht veröffentlicht wird, werden Werte für das aktive Projekt in blau als NV angezeigt.

    // Calculate the average values of actual cost, cost, work, and percent complete
    // for all projects, and compare with the values for the current project.
    function parseODataResult(oDataResult, currentProjectGuid) {
        // Deserialize the JSON string into a JavaScript object.
        let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
        let len = res.d.results.length;
        let projActualCost = 0;
        let projCost = 0;
        let projWork = 0;
        let projPercentCompleted = 0;
        let myProjectIndex = -1;
        for (i = 0; i < len; i++) {
            // If the current project GUID matches the GUID from the OData query,  
            // store the project index.
            if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) {
                myProjectIndex = i;
            }
            projCost += Number(res.d.results[i].ProjectCost);
            projWork += Number(res.d.results[i].ProjectWork);
            projActualCost += Number(res.d.results[i].ProjectActualCost);
            projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted);
        }
        let avgProjCost = projCost / len;
        let avgProjWork = projWork / len;
        let avgProjActualCost = projActualCost / len;
        let avgProjPercentCompleted = projPercentCompleted / len;
    
        // Round off cost to two decimal places, and round off other values to one decimal place.
        avgProjCost = avgProjCost.toFixed(2);
        avgProjWork = avgProjWork.toFixed(1);
        avgProjActualCost = avgProjActualCost.toFixed(2);
        avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);
    
        // Display averages in the table, with the correct units.
        document.getElementById("AverageProjectCost").innerHTML = "$"
            + avgProjCost;
        document.getElementById("AverageProjectActualCost").innerHTML
            = "$" + avgProjActualCost;
        document.getElementById("AverageProjectWork").innerHTML
            = avgProjWork + " hrs";
        document.getElementById("AverageProjectPercentComplete").innerHTML
            = avgProjPercentCompleted + "%";
    
        // Calculate and display values for the current project.
        if (myProjectIndex != -1) {
            let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
            let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
            let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost);
            let myProjPercentCompleted =
            Number(res.d.results[myProjectIndex].ProjectPercentCompleted);
    
            myProjCost = myProjCost.toFixed(2);
            myProjWork = myProjWork.toFixed(1);
            myProjActualCost = myProjActualCost.toFixed(2);
            myProjPercentCompleted = myProjPercentCompleted.toFixed(1);
    
            document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost;
    
            if (Number(myProjCost) <= Number(avgProjCost)) {
                document.getElementById("CurrentProjectCost").style.color = "green"
            }
            else {
                document.getElementById("CurrentProjectCost").style.color = "red"
            }
    
            document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost;
    
            if (Number(myProjActualCost) <= Number(avgProjActualCost)) {
                document.getElementById("CurrentProjectActualCost").style.color = "green"
            }
            else {
                document.getElementById("CurrentProjectActualCost").style.color = "red"
            }
    
            document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs";
    
            if (Number(myProjWork) <= Number(avgProjWork)) {
                document.getElementById("CurrentProjectWork").style.color = "red"
            }
            else {
                document.getElementById("CurrentProjectWork").style.color = "green"
            }
    
            document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%";
    
            if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) {
                document.getElementById("CurrentProjectPercentComplete").style.color = "red"
            }
            else {
                document.getElementById("CurrentProjectPercentComplete").style.color = "green"
            }
        }
        else {
            document.getElementById("CurrentProjectCost").innerHTML = "NA";
            document.getElementById("CurrentProjectCost").style.color = "blue"
    
            document.getElementById("CurrentProjectActualCost").innerHTML = "NA";
            document.getElementById("CurrentProjectActualCost").style.color = "blue"
    
            document.getElementById("CurrentProjectWork").innerHTML = "NA";
            document.getElementById("CurrentProjectWork").style.color = "blue"
    
            document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA";
            document.getElementById("CurrentProjectPercentComplete").style.color = "blue"
        }
    }
    

Testen des HelloProjectOData-Add-Ins

Zum Testen und Debuggen des HelloProjectOData-Add-Ins mit Visual Studio müssen Project Professional auf dem Entwicklungscomputer installiert sein. Um verschiedene Testszenarien zu ermöglichen, stellen Sie sicher, dass Sie auswählen können, ob Project für Dateien auf dem lokalen Computer geöffnet wird oder ob eine Verbindung mit Project Web App hergestellt wird. Im Folgenden sind Beispielschritte aufgeführt.

  1. Wählen Sie auf der Registerkarte Datei in der Backstage-Ansicht die Registerkarte Info und dann Konten verwalten aus.

  2. Im Dialogfeld Project Web App-Konten kann die Liste Verfügbare Konten zusätzlich zum lokalen Computerkonto mehrere Project Web App-Konten enthalten. Wählen Sie im Abschnitt Beim Start die Option Konto auswählen aus.

  3. Schließen Sie Project, sodass die Anwendung von Visual Studio zum Debuggen des Add-ins gestartet werden kann.

Grundlegende Tests sollten Folgendes umfassen:

  • Führen Sie das Add-in in Visual Studio aus, und öffnen Sie dann ein veröffentlichtes Projekt aus Project Web App, das Kosten- und Arbeitsdaten enthält. Stellen Sie sicher, dass das Add-In den ProjectData-Endpunkt anzeigt und die Kosten- und Arbeitsdaten in der Tabelle ordnungsgemäß anzeigt. Sie können mit der Ausgabe im odataText-Steuerelement die REST-Abfrage sowie weitere Informationen überprüfen.

  • Führen Sie das Add-In erneut aus, wobei Sie beim Starten von Project im Dialogfeld Anmelden das Profil für den lokalen Computer auswählen. Öffnen Sie eine lokale .mpp Datei, und testen Sie dann das Add-In. Stellen Sie sicher, dass das Add-In eine Fehlermeldung anzeigt, wenn Sie versuchen, den ProjectData-Endpunkt abzurufen.

  • Führen Sie das Add-in erneut aus, und erstellen Sie ein Projekt, das Aufgaben mit Kosten- und Arbeitsdaten enthält. Sie können das Projekt in Project Web App speichern, veröffentlichen Sie es aber nicht. Überprüfen Sie, ob das Add-in Daten aus Project Server, jedoch NV für das aktuelle Projekt anzeigt.

Verfahren 6. Testen des Add-Ins

  1. Führen Sie Project Professional aus, stellen Sie eine Verbindung mit Project Web App her, und erstellen Sie dann ein Testprojekt. Weisen Sie Vorgänge lokalen Ressourcen oder Unternehmensressourcen zu, legen Sie für einige Vorgänge verschiedene Werte für den Prozentsatz abgeschlossen fest, und veröffentlichen Sie dann das Projekt. Beenden Sie Project, wodurch Visual Studio Project zum Debuggen des Add-Ins starten kann.

  2. Drücken Sie in Visual Studio F5. Melden Sie sich bei Project Web App an, und öffnen Sie das im vorherigen Schritt erstellte Projekt. Das Projekt kann im schreibgeschützten Modus oder im Bearbeitungsmodus geöffnet werden.

  3. Wählen Sie auf der Registerkarte PROJEKT des Menübands in der Dropdownliste Office-Add-Insdie Option Hello ProjectData aus (siehe Abbildung 5). Die Schaltfläche Alle Projekte vergleichen sollte deaktiviert sein.

    Abbildung 5: Starten des HelloProjectOData-Add-Ins

    Testen Sie die App HelloProjectOData.

  4. Wählen Sie im Aufgabenbereich Hallo ProjectData die Option ProjectData-Endpunkt abrufen aus. In der Zeile projectDataEndPoint sollte die URL des ProjectData-Diensts angezeigt werden, und die Schaltfläche Alle Projekte vergleichen sollte aktiviert sein (siehe Abbildung 6).

  5. Wählen Sie Alle Projekte vergleichen aus. Das Add-In kann angehalten werden, während es Daten aus dem ProjectData-Dienst abruft, und dann sollte es den formatierten Durchschnitt und die aktuellen Werte in der Tabelle anzeigen.

    Abbildung 6 Anzeigen der Ergebnisse der REST-Abfrage

    Zeigen Sie die Ergebnisse der REST-Abfrage an.

  6. Überprüfen Sie die Ausgabe im Textfeld. Es sollten der Dokumentpfad, die REST-Abfrage, status Informationen und JSON-Ergebnisse aus den Aufrufen ajax von und parseODataResultangezeigt werden. Die Ausgabe hilft, Code in der parseODataResult Funktion projCost += Number(res.d.results[i].ProjectCost);wie zu verstehen, zu erstellen und zu debuggen.

    Es folgt ein Beispiel für die Ausgabe mit Zeilenumbrüchen und Leerzeichen, die dem Text zur Übersichtlichkeit hinzugefügt wurden, für drei Projekte in einer Project Web App-instance.

    Document path: <>\WinProj test1
    
    REST query:
    http://sphvm-37189/pwa/_api/ProjectData/Projects?$filter=ProjectName ne 'Timesheet Administrative Work Items'
        &amp;$select=ProjectId, ProjectName, ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost
    
    textStatus: success
    ContentType: application/json;odata=verbose;charset=utf-8
    Status: 200
    
    ResponseText:
    {"d":{"results":[
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')",
        "type":"ReportingData.Project"},
        "ProjectId":"ce3d0d65-3904-e211-96cd-00155d157123",
        "ProjectActualCost":"0.000000",
        "ProjectCost":"0.000000",
        "ProjectName":"Task list created in PWA",
        "ProjectPercentCompleted":0,
        "ProjectWork":"16.000000"},
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')",
        "type":"ReportingData.Project"},
        "ProjectId":"c31023fc-1404-e211-86b2-3c075433b7bd",
        "ProjectActualCost":"700.000000",
        "ProjectCost":"2400.000000",
        "ProjectName":"WinProj test 2",
        "ProjectPercentCompleted":29,
        "ProjectWork":"48.000000"},
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')",
        "type":"ReportingData.Project"},
        "ProjectId":"dc81fbb2-b801-e211-9d2a-3c075433b7bd",
        "ProjectActualCost":"1900.000000",
        "ProjectCost":"5200.000000",
        "ProjectName":"WinProj test1",
        "ProjectPercentCompleted":37,
        "ProjectWork":"104.000000"}
    ]}}
    
  7. Beenden Sie das Debuggen (drücken Sie UMSCHALT+F5), und drücken Sie dann erneut F5, um eine neue Instanz von Project auszuführen. Wählen Sie im Dialogfeld Anmeldung das lokale Computerprofil, nicht Project Web App aus. Erstellen oder öffnen Sie eine lokale MPP-Projektdatei, öffnen Sie den Aufgabenbereich Hello ProjectData, und wählen Sie dann ProjectData-Endpunkt abrufen aus. Das Add-In sollte den Fehler Keine Verbindung! anzeigen (siehe Abbildung 7), und die Schaltfläche Alle Projekte vergleichen sollte deaktiviert bleiben.

    Abbildung 7: Verwenden des Add-Ins ohne Project Web App-Verbindung

    Verwenden Sie die App ohne Project Web App-Verbindung.

  8. Beenden Sie das Debuggen, und drücken Sie erneut F5. Melden Sie sich bei Project Web App an, und erstellen Sie ein Projekt, das Kosten- und Arbeitsdaten enthält. Sie können das Projekt speichern, veröffentlichen Sie es aber nicht.

    Wenn Sie im Aufgabenbereich Hello ProjectDatadie Option Alle Projekte vergleichen auswählen, sollte für Felder in der Spalte Aktuell ein blauer NA angezeigt werden (siehe Abbildung 8).

    Abbildung 8: Vergleichen eines nicht veröffentlichten Projekts mit anderen Projekten

    Vergleichen sie ein nicht veröffentlichtes Projekt mit anderen Projekten.

Auch wenn Ihr Add-in in den vorherigen Tests ordnungsgemäß ausgeführt wurde, sollten weitere Tests durchgeführt werden. Beispiel:

  • Öffnen Sie ein Projekt in Project Web App, das keine Kosten- oder Arbeitsdaten für die Aufgaben enthält. In der Spalte Aktuell sollte als Wert "Null" angezeigt werden.

  • Testen Sie ein Projekt, das keine Aufgaben enthält.

  • Wenn Sie das Add-in ändern und veröffentlichen, sollten Sie mit dem veröffentlichten Add-in erneut ähnliche Tests ausführen. Informationen zu weiteren Aspekten finden Sie unter Nächste Schritte.

Hinweis

Es gibt Grenzwerte für die Datenmenge, die in einer Abfrage des ProjectData-Diensts zurückgegeben werden kann. die Datenmenge variiert je nach Entität. Beispielsweise hat der Projects Entitätssatz einen Standardgrenzwert von 100 Projekten pro Abfrage, aber der Risks Entitätssatz hat einen Standardgrenzwert von 200. For a production installation, the code in the HelloProjectOData example should be modified to enable queries of more than 100 projects. For more information, see Next steps and Querying OData feeds for Project reporting data.

Beispielcode für das HelloProjectOData-Add-in

HelloProjectOData.html-Datei

Der folgende Code befindet sich in der Datei Pages\HelloProjectOData.html des Projekts HelloProjectODataWeb.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <title>Test ProjectData Service</title>

        <link rel="stylesheet" type="text/css" href="../Content/Office.css" />

        <!-- Add your CSS styles to the following file. -->
        <link rel="stylesheet" type="text/css" href="../Content/App.css" />

        <!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. -->
        <!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> -->
        <script src="../Scripts/jquery-1.7.1.js"></script>

        <!-- Use the CDN reference to Office.js when deploying your add-in -->
        <!--<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>-->

        <!-- Use the local script references for Office.js to enable offline debugging -->
        <script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
        <script src="../Scripts/Office/1.0/Office.js"></script>

        <!-- Add your JavaScript to the following files. -->
        <script src="../Scripts/HelloProjectOData.js"></script>
        <script src="../Scripts/SurfaceErrors.js"></script>
    </head>
    <body>
        <div id="SectionContent">
        <div id="odataQueries">
            ODATA REST QUERY
        </div>
        <div id="odataInfo">
            <button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button>
            <br />
            <br />
            <span class="rest" id="projectDataEndPoint">Endpoint of the 
            <strong>ProjectData</strong> service</span>
            <br />
        </div>
        <div id="compareProjectData">
            <button class="button-wide" disabled="disabled" id="compareProjects"
            onclick="retrieveOData()">
            Compare All Projects</button>
            <br />
        </div>
        </div>
        <div id="corpInfo">
        <table class="infoTable" aria-readonly="True" style="width: 100%;">
            <tr>
            <td class="heading_leftCol"></td>
            <td class="heading_midCol"><strong>Average</strong></td>
            <td class="heading_rightCol"><strong>Current</strong></td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Cost</strong></td>
            <td class="row_midCol" id="AverageProjectCost">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectCost">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Actual Cost</strong></td>
            <td class="row_midCol" id="AverageProjectActualCost">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectActualCost">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Work</strong></td>
            <td class="row_midCol" id="AverageProjectWork">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectWork">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project % Complete</strong></td>
            <td class="row_midCol" id="AverageProjectPercentComplete">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectPercentComplete">&amp;nbsp;</td>
            </tr>
        </table>
        </div>
        <img alt="Corporation" class="logo" src="../../images/NewLogo.png" />
        <br />
        <textarea id="odataText" rows="12" cols="40"></textarea>
    </body>
</html>

HelloProjectOData.js-Datei

Der folgende Code befindet sich in der Datei Scripts\Office\HelloProjectOData.js des Projekts HelloProjectODataWeb.

/* File: HelloProjectOData.js
* JavaScript functions for the HelloProjectOData example task pane app.
* October 2, 2012
*/

let PROJDATA = "/_api/ProjectData";
let PROJQUERY = "/Projects?";
let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'";
let QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost";
let _pwa;           // URL of Project Web App.
let _projectUid;    // GUID of the active project.
let _docUrl;        // Path of the project document.
let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData

// The initialize function is required for all add-ins.
Office.initialize = function (reason) {
    // Checks for the DOM to load using the jQuery ready method.
    $(document).ready(function () {
        // After the DOM is loaded, app-specific code can run.
    });
}

// Set the global variables, enable the Compare All Projects button,
// and display the URL of the ProjectData service.
// Display an error if Project is not connected with Project Web App.
function setOdataUrl() {
    Office.context.document.getProjectFieldAsync(
        Office.ProjectProjectFields.ProjectServerUrl,
        function (asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                _pwa = String(asyncResult.value.fieldValue);

                // If you debug with Visual Studio on a local Project Server computer,
                // uncomment the following lines to use the localhost URL.
                //let localhost = location.host.split(":", 1);
                //let pwaStartPosition = _pwa.lastIndexOf("/");
                //let pwaLength = _pwa.length - pwaStartPosition;
                //let pwaName = _pwa.substr(pwaStartPosition, pwaLength);
                //_pwa = location.protocol + "//" + localhost + pwaName;

                if (_pwa.substring(0, 4) == "http") {
                    _odataUrl = _pwa + PROJDATA;
                    $("#compareProjects").removeAttr("disabled");
                    getProjectGuid();
                }
                else {
                    _odataUrl = "No connection!";
                    throwError(_odataUrl, "You are not connected to Project Web App.");
                }
                getDocumentUrl();
                $("#projectDataEndPoint").text(_odataUrl);
            }
            else {
                throwError(asyncResult.error.name, asyncResult.error.message);
            }
        }
    );
}

// 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 {
                throwError(asyncResult.error.name, asyncResult.error.message);
            }
        }
    );
}

// Get the path of the project in Project web app, which is in the form <>\ProjectName .
function getDocumentUrl() {
    _docUrl = "Document path:\r\n" + Office.context.document.url;
}

//  Functions to get and parse the Project Server reporting data./

// Get data about all projects on Project Server,
// by using a REST query with the ajax method in jQuery.
function retrieveOData() {
    let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2;
    let accept = "application/json; odata=verbose";
    accept.toLocaleLowerCase();

    // Enable cross-origin scripting (required by jQuery 1.5 and later).
    // This does not work with Project on the web.
    $.support.cors = true;

    $.ajax({
        url: restUrl,
        type: "GET",
        contentType: "application/json",
        data: "",      // Empty string for the optional data.
        //headers: { "Accept": accept },
        beforeSend: function (xhr) {
            xhr.setRequestHeader("ACCEPT", accept);
        },
        complete: function (xhr, textStatus) {
            // Create a message to display in the text box.
            let message = "\r\ntextStatus: " + textStatus +
                "\r\nContentType: " + xhr.getResponseHeader("Content-Type") +
                "\r\nStatus: " + xhr.status +
                "\r\nResponseText:\r\n" + xhr.responseText;

            // xhr.responseText is the result from an XmlHttpRequest, which 
            // contains the JSON response from the OData service.
            parseODataResult(xhr.responseText, _projectUid);

            // Write the document name, response header, status, and JSON to the odataText control.
            $("#odataText").text(_docUrl);
            $("#odataText").append("\r\nREST query:\r\n" + restUrl);
            $("#odataText").append(message);

            if (xhr.status != 200 &amp;&amp; xhr.status != 1223 &amp;&amp; xhr.status != 201) {
                $("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>");
            }
        },
        error: getProjectDataErrorHandler
    });
}

function getProjectDataErrorHandler(data, errorCode, errorMessage) {
    $("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n"
        + errorMessage);
    throwError(errorCode, errorMessage);
}

// Calculate the average values of actual cost, cost, work, and percent complete
// for all projects, and compare with the values for the current project.
function parseODataResult(oDataResult, currentProjectGuid) {
    // Deserialize the JSON string into a JavaScript object.
    let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
    let len = res.d.results.length;
    let projActualCost = 0;
    let projCost = 0;
    let projWork = 0;
    let projPercentCompleted = 0;
    let myProjectIndex = -1;

    for (i = 0; i < len; i++) {
        // If the current project GUID matches the GUID from the OData query,  
        // then store the project index.
        if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) {
            myProjectIndex = i;
        }
        projCost += Number(res.d.results[i].ProjectCost);
        projWork += Number(res.d.results[i].ProjectWork);
        projActualCost += Number(res.d.results[i].ProjectActualCost);
        projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted);

    }
    let avgProjCost = projCost / len;
    let avgProjWork = projWork / len;
    let avgProjActualCost = projActualCost / len;
    let avgProjPercentCompleted = projPercentCompleted / len;

    // Round off cost to two decimal places, and round off other values to one decimal place.
    avgProjCost = avgProjCost.toFixed(2);
    avgProjWork = avgProjWork.toFixed(1);
    avgProjActualCost = avgProjActualCost.toFixed(2);
    avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);

    // Display averages in the table, with the correct units. 
    document.getElementById("AverageProjectCost").innerHTML = "$"
        + avgProjCost;
    document.getElementById("AverageProjectActualCost").innerHTML
        = "$" + avgProjActualCost;
    document.getElementById("AverageProjectWork").innerHTML
        = avgProjWork + " hrs";
    document.getElementById("AverageProjectPercentComplete").innerHTML
        = avgProjPercentCompleted + "%";

    // Calculate and display values for the current project.
    if (myProjectIndex != -1) {

        let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
        let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
        let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost);
        let myProjPercentCompleted = Number(res.d.results[myProjectIndex].ProjectPercentCompleted);

        myProjCost = myProjCost.toFixed(2);
        myProjWork = myProjWork.toFixed(1);
        myProjActualCost = myProjActualCost.toFixed(2);
        myProjPercentCompleted = myProjPercentCompleted.toFixed(1);

        document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost;

        if (Number(myProjCost) <= Number(avgProjCost)) {
            document.getElementById("CurrentProjectCost").style.color = "green"
        }
        else {
            document.getElementById("CurrentProjectCost").style.color = "red"
        }

        document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost;

        if (Number(myProjActualCost) <= Number(avgProjActualCost)) {
            document.getElementById("CurrentProjectActualCost").style.color = "green"
        }
        else {
            document.getElementById("CurrentProjectActualCost").style.color = "red"
        }

        document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs";

        if (Number(myProjWork) <= Number(avgProjWork)) {
            document.getElementById("CurrentProjectWork").style.color = "red"
        }
        else {
            document.getElementById("CurrentProjectWork").style.color = "green"
        }

        document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%";

        if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) {
            document.getElementById("CurrentProjectPercentComplete").style.color = "red"
        }
        else {
            document.getElementById("CurrentProjectPercentComplete").style.color = "green"
        }
    }
    else {    // The current project is not published.
        document.getElementById("CurrentProjectCost").innerHTML = "NA";
        document.getElementById("CurrentProjectCost").style.color = "blue"

        document.getElementById("CurrentProjectActualCost").innerHTML = "NA";
        document.getElementById("CurrentProjectActualCost").style.color = "blue"

        document.getElementById("CurrentProjectWork").innerHTML = "NA";
        document.getElementById("CurrentProjectWork").style.color = "blue"

        document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA";
        document.getElementById("CurrentProjectPercentComplete").style.color = "blue"
    }
}

App.css-Datei

Der folgende Code befindet sich in der Datei Content\App.css des Projekts HelloProjectODataWeb.

/*
*  File: App.css for the HelloProjectOData app.
*  Updated: 10/2/2012
*/

body
{
    font-size: 11pt;
}
h1
{
    font-size: 22pt;
}
h2
{
    font-size: 16pt;
}

/******************************************************************
Code label class
******************************************************************/

.rest 
{
    font-family: 'Courier New';
    font-size: 0.9em;
}

/******************************************************************
Button classes
******************************************************************/

.button-wide {
    width: 210px;
    margin-top: 2px;
}
.button-narrow 
{
    width: 80px;
    margin-top: 2px;
}

/******************************************************************
Table styles
******************************************************************/

.infoTable
{
    text-align: center; 
    vertical-align: middle
}
.heading_leftCol
{
    width: 20px;
    height: 20px;
}
.heading_midCol
{
    width: 100px;
    height: 20px;
    font-size: medium; 
    font-weight: bold; 
}
.heading_rightCol
{
    width: 101px;
    height: 20px;
    font-size: medium;
    font-weight: bold;
}
.row_leftCol
{
    width: 20px;
    font-size: small;
    font-weight: bold;
}
.row_midCol
{
    width: 100px;
}
.row_rightCol
{
    width: 101px;
}
.logo
{
    width: 135px;
    height: 53px;
}

SurfaceErrors.js-Datei

Der folgende Code enthält eine throwError Funktion, die ein Toast -Objekt erstellt.

/*
 * Show error messages in a "toast" notification.
 */

// Throws a custom defined error.
function throwError(errTitle, errMessage) {
    try {
        // Define and throw a custom error.
        let customError = { name: errTitle, message: errMessage }
        throw customError;
    }
    catch (err) {
        // Catch the error and display it to the user.
        Toast.showToast(err.name, err.message);
    }
}

// Add a dynamically-created div "toast" for displaying errors to the user.
let Toast = {

    Toast: "divToast",
    Close: "btnClose",
    Notice: "lblNotice",
    Output: "lblOutput",

    // Show the toast with the specified information.
    showToast: function (title, message) {

        if (document.getElementById(this.Toast) == null) {
            this.createToast();
        }

        document.getElementById(this.Notice).innerText = title;
        document.getElementById(this.Output).innerText = message;

        $("#" + this.Toast).hide();
        $("#" + this.Toast).show("slow");
    },

    // Create the display for the toast.
    createToast: function () {
        let divToast;
        let lblClose;
        let btnClose;
        let divOutput;
        let lblOutput;
        let lblNotice;

        // Create the container div.
        divToast = document.createElement("div");
        let toastStyle = "background-color:rgba(220, 220, 128, 0.80);" +
            "position:absolute;" +
            "bottom:0px;" +
            "width:90%;" +
            "text-align:center;" +
            "font-size:11pt;";
        divToast.setAttribute("style", toastStyle);
        divToast.setAttribute("id", this.Toast);

        // Create the close button.
        lblClose = document.createElement("div");
        lblClose.setAttribute("id", this.Close);
        let btnStyle = "text-align:right;" +
            "padding-right:10px;" +
            "font-size:10pt;" +
            "cursor:default";
        lblClose.setAttribute("style", btnStyle);
        lblClose.appendChild(document.createTextNode("CLOSE "));

        btnClose = document.createElement("span");
        btnClose.setAttribute("style", "cursor:pointer;");
        btnClose.setAttribute("onclick", "Toast.close()");
        btnClose.innerText = "X";
        lblClose.appendChild(btnClose);

        // Create the div to contain the toast title and message.
        divOutput = document.createElement("div");
        divOutput.setAttribute("id", "divOutput");
        let outputStyle = "margin-top:0px;";
        divOutput.setAttribute("style", outputStyle);

        lblNotice = document.createElement("span");
        lblNotice.setAttribute("id", this.Notice);
        let labelStyle = "font-weight:bold;margin-top:0px;";
        lblNotice.setAttribute("style", labelStyle);

        lblOutput = document.createElement("span");
        lblOutput.setAttribute("id", this.Output);

        // Add the child nodes to the toast div.
        divOutput.appendChild(lblNotice);
        divOutput.appendChild(document.createElement("br"));
        divOutput.appendChild(lblOutput);
        divToast.appendChild(lblClose);
        divToast.appendChild(divOutput);

        // Add the toast div to the document body.
        document.body.appendChild(divToast);
    },

    // Close the toast.
    close: function () {
        $("#" + this.Toast).hide("slow");
    }
}

Nächste Schritte

Wenn HelloProjectOData ein Produktions-Add-In wäre, das in AppSource verkauft oder in einem SharePoint-App-Katalog verteilt werden soll, würde es anders entworfen. Das Textfeld würde beispielsweise keine Debuggingausgabe und wahrscheinlich keine Schaltfläche zum Abrufen des ProjectData-Endpunkts enthalten. Außerdem müssten Sie die retrieveOData Funktion umschreiben, um Project Web App-Instanzen mit mehr als 100 Projekten zu verarbeiten.

Das Add-In sollte zusätzliche Fehlerüberprüfungen sowie Logik zum Abfangen und Erläutern oder Anzeigen von Edgefällen enthalten. Wenn beispielsweise eine Project Web App-instance 1.000 Projekte mit einer durchschnittlichen Dauer von fünf Tagen und durchschnittlichen Kosten von 2400 USD aufweist und das aktive Projekt das einzige Projekt ist, das eine Dauer von mehr als 20 Tagen hat, würde der Kosten- und Arbeitsvergleich schief ausfallen. Dies könnte mit einem Häufigkeitsdiagramm angezeigt werden. Sie können Optionen hinzufügen, um die Dauer anzuzeigen, Projekte mit ähnlicher Länge zu vergleichen oder Projekte aus denselben oder verschiedenen Abteilungen zu vergleichen. Oder fügen Sie eine Möglichkeit hinzu, mit der der Benutzer aus einer Liste der anzuzeigenden Felder auswählen kann.

Für andere Abfragen des ProjectData-Diensts gibt es Grenzwerte für die Länge der Abfragezeichenfolge, die sich auf die Anzahl der Schritte auswirkt, die eine Abfrage von einer übergeordneten Auflistung bis zu einem Objekt in einer untergeordneten Auflistung ausführen kann. Beispielsweise funktioniert eine zweistufige Abfrage von Projekten zu Vorgängen zum Aufgabenelement, aber eine dreistufige Abfrage wie Projekte zu Vorgängen an Zuordnungselement kann die standardmäßige maximale URL-Länge überschreiten. Weitere Informationen finden Sie unter Abfragen von OData-Feeds für Project-Berichtsdaten.

Wenn Sie das HelloProjectOData-Add-In für die Produktion ändern, führen Sie die folgenden Schritte aus.

  • Ändern Sie in der Datei "HelloProjectOData.html" für eine bessere Leistung den Verweis "office.js" vom lokalen Projekt zum CDN-Verweis:

    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
    
  • Schreiben Sie die retrieveOData Funktion um, um Abfragen von mehr als 100 Projekten zu ermöglichen. Sie können beispielsweise die Anzahl der Projekte mit der Abfrage ~/ProjectData/Projects()/$count abrufen und die Operatoren $skip und $top in der REST-Abfrage für Projektdaten verwenden. Führen Sie mehrere Abfragen in einer Schleife aus, und ermitteln Sie dann den Durchschnitt der Daten aus den einzelnen Abfragen. Jede Abfrage für Projektdaten hat folgendes Format:

    ~/ProjectData/Projects()?skip= [numSkipped]&amp;$top=100&amp;$filter=[filter]&amp;$select=[field1,field2, ???????]

    Weitere Informationen finden Sie unter OData-Systemabfrageoptionen mithilfe des REST-Endpunkts. Sie können auch den Befehl Set-SPProjectOdataConfiguration in Windows PowerShell verwenden, um die Standardseitengröße für eine Abfrage des Entitätssatzes Projekte (oder einen der 33 Entitätssätze) zu überschreiben. Weitere Informationen finden Sie unter ProjectData – OData-Dienstreferenz für Project.

  • Informationen zum Bereitstellen des Add-Ins finden Sie unter Veröffentlichen Ihres Office-Add-Ins.

Weitere Artikel