So wird’s gemacht: Erstellen einer benutzerdefinierten Datenquelle (HTML)

[ Dieser Artikel richtet sich an Windows 8.x- und Windows Phone 8.x-Entwickler, die Windows-Runtime-Apps schreiben. Wenn Sie für Windows 10 entwickeln, finden Sie weitere Informationen unter neueste Dokumentation]

Die Windows-Bibliothek für JavaScript enthält verschiedene einsatzbereite Datenquellenobjekte, die Sie verwenden können, um ein ListView- oder FlipView-Objekt mit verschiedenen Datentypen aufzufüllen. Mit dem WinJS.Binding.List-Objekt können Sie auf Arrays und JSON-Daten zugreifen, und mit dem StorageDataSource-Objekt können Sie auf Informationen zum Dateisystem zugreifen.

Sie sind nicht nur auf diese Datenquellen begrenzt. Sie können eine eigene Datenquelle erstellen, die auf andere Datentypen zugreift, beispielsweise eine XML-Datei oder einen Webdienst. In diesem Thema wird gezeigt, wie Sie eine benutzerdefinierte Datenquelle implementieren, die auf einen Webdienst zugreift. Die Datenquelle stellt mithilfe von XHR eine Verbindumg mit dem Bing-Bildersuchdienst her und zeigt das Ergebnis in einem ListView-Objekt an.

(Da für den Bing-Dienst jede App einen eigenen App-ID-Schlüssel benötigt, müssen Sie einen Schlüssel abrufen, damit Sie diesen Code verwenden können. Weitere Informationen zum Abrufen eines App-ID-Schlüssels finden Sie im Bing Developer Center.)

Zum Erstellen einer benutzerdefinierten Datenquelle benötigen Sie Objekte, die die Schnittstellen IListDataAdapter und IListDataSource implementieren. WinJS enthält ein VirtualizedDataSource-Objekt, das IListDataSourceimplementiert. Sie müssen die Schnittstelle lediglich von diesem Objekt übernehmen und den Basiskonstruktor an IListDataAdapter übergeben. Sie müssen ein eigenes Objekt erstellen, das die IListDataAdapter-Schnittstelle implementiert.

Die IListDataAdapter-Schnittstelle interagiert direkt mit der Datenquelle, um Elemente abzurufen oder zu aktualisieren. IListDataSource stellt eine Verbindung mit einem Steuerelement her und ändert IListDataAdapter.

Voraussetzungen

Anweisungen

Schritt 1: Erstellen einer JavaScript-Datei für eine benutzerdefinierte Datenquelle

  1. Verwenden Sie Microsoft Visual Studio, um dem Projekt eine JavaScript-Datei hinzuzufügen. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner js des Projekts, und wählen Sie Hinzufügen > Neues Element aus. Das Dialogfeld Neues Element hinzufügen wird angezeigt.
  2. Wählen Sie JavaScript-Datei aus. Geben Sie als Namen "bingImageSearchDataSource.js" ein. Klicken Sie auf Hinzufügen, um die Datei zu erstellen. Visual Studio erstellt eine leere JavaScript-Datei mit dem Namen bingImageSearchDataSource.js.

Schritt 2: Erstellen eines IListDataAdapter-Objekts

Im nächsten Schritt erstellen Sie ein Objekt, das die IListDataAdapter-Schnittstelle implementiert. Ein IListDataAdapter-Objekt ruft Daten aus einer Datenquelle ab und stellt sie einer IListDataSource-Schnittstelle bereit.

Die IListDataAdapter-Schnittstelle unterstützt Lese- und Schreibzugriff und Änderungsbenachrichtigungen. Sie müssen jedoch nicht die gesamte Schnittstelle implementieren: Sie können eine einfache schreibgeschützte IListDataAdapter-Schnittstelle erstellen, indem Sie nur die Methoden itemsFromIndex und getCount implementieren.

  1. Öffnen Sie die im vorherigen Schritt erstellte JavaScript-Datei bingImageSearchDataSource.js.

  2. Erstellen Sie eine anonyme Funktion, und aktivieren Sie den Strict-Modus.

    Wie unter Codieren einfacher Apps beschrieben empfiehlt es sich, den JavaScript-Code zu kapseln, indem Sie ihn mit einer anonymen Funktion umschließen, und den Strict-Modus zu verwenden.

    (function () {
        "use strict"; 
    
  3. Verwenden Sie die WinJS.Class.define-Funktion, um Ihre Implementierung von IListDataAdapter zu erstellen. Die WinJS.Class.define-Funktion übernimmt als ersten Parameter den Klassenkonstruktor.

    Dieser IListDataAdapter stellt eine Verbindung mit dem Bing-Suchdienst her. Die Bing-API-Suchabfrage erwartet bestimmte Daten. Wir werden diese Daten sowie zusätzliche Daten im IListDataAdapter als Klassenmember speichern:

    • _minPageSize: Die minimale Anzahl von Elementen pro Seite.
    • _maxPageSize: Die maximale Anzahl von Elementen pro Seite.
    • _maxCount: Die maximale Anzahl von Elementen, die zurückgegeben werden sollen.
    • _devKey: Die App-ID. Die Bing-API benötigt, einen AppID-Schlüssel, um die Anwendung zu identifizieren.
    • _query: Die Suchzeichenfolge.

    Erstellen Sie einen Konstruktor, der eine AppID für die Bing-API und eine Suchabfrage übernimmt und Werte für die anderen Member bereitstellt.

    
        // Definition of the data adapter
        var bingImageSearchDataAdapter = WinJS.Class.define(
            function (devkey, query) {
    
                // Constructor
                this._minPageSize = 10;  // based on the default of 10
                this._maxPageSize = 50;  // max request size for bing images
                this._maxCount = 1000;   // limit on the bing API
                this._devkey = devkey;
                this._query = query;
            },
    
  4. Als nächsten Parameter erwartet die WinJS.Class.define-Funktion ein Objekt, das die Instanzmember der Klasse enthält. Dieses Objekt verwenden Sie, um die Methoden itemsFromIndex und getCount zu implementieren.

    Erstellen Sie die öffnende geschweifte Klammer für dieses Objekt.

            // IListDataDapter methods
            // These methods define the contract between the IListDataSource and the IListDataAdapter.
            {
    
    1. Implementieren Sie die itemsFromIndex-Methode. Die itemsFromIndex-Methode stellt eine Verbindung mit der Datenquelle her und gibt die angeforderten Daten als IFetchResult-Schnittstelle zurück. Die itemsFromIndex-Methode akzeptiert drei Parameter: den Index eines abzurufenden Elements, die Anzahl der vor diesem Element abzurufenden Elemente und die Anzahl der nach diesem Element abzurufenden Elemente.

                  itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                      var that = this;
      
    2. Vergewissern Sie sich, dass das angeforderte Element (requestIndex) kleiner ist als die Höchstanzahl der abzurufenden Elemente. Wenn dies nicht der Fall ist, geben Sie einen Fehler zurück.

                      if (requestIndex >= that._maxCount) {
                          return Promise.wrapError(new WinJS.ErrorFromName(UI.FetchError.doesNotExist));
                      }
      
    3. Verwenden Sie requestIndex, countBefore und countAfter, um den Index des ersten Elements und die Größe der Anforderung zu berechnen. Die Parameter countBefore und countAfter sind Empfehlungen für die Menge der abzurufenden Daten: Sie müssen nicht alle Elemente abrufen, nach denen Sie gefragt werden. In diesem Beispiel hat Bing eine maximale Anforderungsgröße von 50 Elementen. Daher müssen wir unsere Anforderungsgröße entsprechend begrenzen.

      Normalerweise wird bei einer Anforderung nach einem oder zwei Elementen vor oder nach dem angeforderten Element und nach einer größeren Anzahl von der Gegenseite gefragt. Das müssen wir also berücksichtigen, wenn wir überlegen, was wir den Server fragen möchten.

                      var fetchSize, fetchIndex;
      
                      // See which side of the requestIndex is the overlap.
                      if (countBefore > countAfter) {
                          // Limit the overlap
                          countAfter = Math.min(countAfter, 10);
      
                          // Bound the request size based on the minimum and maximum sizes.
                          var fetchBefore = Math.max(
                              Math.min(countBefore, that._maxPageSize - (countAfter + 1)),
                              that._minPageSize - (countAfter + 1)
                              );
                          fetchSize = fetchBefore + countAfter + 1;
                          fetchIndex = requestIndex - fetchBefore;
                      } else {
                          countBefore = Math.min(countBefore, 10);
                          var fetchAfter = Math.max(Math.min(countAfter, that._maxPageSize - (countBefore + 1)), that._minPageSize - (countBefore + 1));
                          fetchSize = countBefore + fetchAfter + 1;
                          fetchIndex = requestIndex - countBefore;
                      }
      
    4. Erstellen Sie die Anforderungszeichenfolge.

                      // Create the request string. 
                      var requestStr = "http://api.bing.net/json.aspx?"
                      + "AppId=" + that._devkey
                      + "&Query=" + that._query
                      + "&Sources=Image"
                      + "&Version=2.0"
                      + "&Market=en-us"
                      + "&Adult=Strict"
                      + "&Filters=Aspect:Wide"
                      + "&Image.Count=" + fetchSize
                      + "&Image.Offset=" + fetchIndex
                      + "&JsonType=raw";
      
    5. Verwenden Sie WinJS.xhr, um die Anforderung zu senden. Die WinJS.xhr-Funktion gibt ein Promise-Objekt zurück, das das Ergebnis enthält. Sie können das Ergebnis verarbeiten, indem Sie die then-Methode des Promise-Objekts verwenden.

                      return WinJS.xhr({ url: requestStr }).then(
      
    6. Erstellen Sie einen Rückruf für einen erfolgreichen WinJS.xhr-Vorgang. Diese Funktion verarbeitet die Ergebnisse und gibt sie als IFetchResult-Element zurück. Das IFetchResult-Element enthält drei Eigenschaften:

      • items: Ein Array von IItem-Objekten, die die Abfrageergebnisse darstellen.
      • offset: Der Index des Anforderungselements im Elementarray.
      • totalCount: Die Gesamtanzahl der Elemente im Elementarray.

      Jedes IItem-Objekt muss eine Schlüsseleigenschaft haben, die einen Bezeichner für das Element enthält, und eine Dateneigenschaft, die die Daten des Elements enthält:

      { key: key1, data : { field1: value, field2: value, ... }}

      Das Array der IItem-Objekte würde so aussehen:

      [{ key: key1, data : { field1: value, field2: value, ... }}, { key: key2, data : {...}}, ...];

                          function (request) {
                              var results = [], count;
      
                              // Use the JSON parser on the results (it's safer than using eval).
                              var obj = JSON.parse(request.responseText);
      
                              // Verify that the service returned images.
                              if (obj.SearchResponse.Image !== undefined) {
                                  var items = obj.SearchResponse.Image.Results;
      
                                  // Create an array of IItem objects:
                                  // results =[{ key: key1, data : { field1: value, field2: value, ... }}, { key: key2, data : {...}}, ...];
                                  for (var i = 0, itemsLength = items.length; i < itemsLength; i++) {
                                      var dataItem = items[i];
                                      results.push({
                                          key: (fetchIndex + i).toString(),
                                          data: {
                                              title: dataItem.Title,
                                              thumbnail: dataItem.Thumbnail.Url,
                                              width: dataItem.Width,
                                              height: dataItem.Height,
                                              linkurl: dataItem.Url
                                          }
                                      });
                                  }
      
                                  // Get the count.
                                  count = obj.SearchResponse.Image.Total;
      
                                  return {
                                      items: results, // The array of items.
                                      offset: requestIndex - fetchIndex, // The index of the requested item in the items array.
                                      totalCount: Math.min(count, that._maxCount), // The total number of records. Bing will only return 1000, so we cap the value.
                                  };
                              } else {
                                  return WinJS.UI.FetchError.doesNotExist;
                              }
                          },
      
    7. Erstellen Sie einen Rückruf für einen nicht erfolgreichen WinJS.xhr-Vorgang.

                          // Called if the WinJS.xhr funtion returned an error. 
                          function (request) {
                              return WinJS.UI.FetchError.noResponse;
                          });
      
    8. Schließen Sie die itemsFromIndex-Methode. Da Sie als Nächstes eine weitere Methode definieren, fügen Sie nach dem Schließen von itemsFromIndex ein Komma hinzu.

                  },
      
  5. Implementieren Sie die getCount-Methode.

    1. Die getCount-Methode akzeptiert keine Parameter und gibt ein Promise-Objekt für die Anzahl der Elemente in den Ergebnissen des IListDataAdapter-Objekts zurück.

                  // Gets the number of items in the result list. 
                  // The count can be updated in itemsFromIndex.
                  getCount: function () {
                      var that = this;
      
    2. Erstellen Sie die Anforderungszeichenfolge. Da Bing keine explizite Methode hat, um nach der Anzahl zu fragen, fordern wir einen Datensatz an und verwenden ihn, um die Anzahl zu erhalten.

      
                      // Create up a request for 1 item so we can get the count
                      var requestStr = "http://api.bing.net/json.aspx?";
      
                      // Common request fields (required)
                      requestStr += "AppId=" + that._devkey
                      + "&Query=" + that._query
                      + "&Sources=Image";
      
                      // Common request fields (optional)
                      requestStr += "&Version=2.0"
                      + "&Market=en-us"
                      + "&Adult=Strict"
                      + "&Filters=Aspect:Wide";
      
                      // Image-specific request fields (optional)
                      requestStr += "&Image.Count=1"
                      + "&Image.Offset=0"
                      + "&JsonType=raw";
      
    3. Verwenden Sie WinJS.xhr, um die Anforderung zu senden. Verarbeiten Sie die Ergebnisse, und geben Sie die Anzahl zurück.

                      // Make an XMLHttpRequest to the server and use it to get the count.
                      return WinJS.xhr({ url: requestStr }).then(
      
                          // The callback for a successful operation.
                          function (request) {
                              var data = JSON.parse(request.responseText);
      
                              // Bing may return a large count of items, 
                              /// but you can only fetch the first 1000.
                              return Math.min(data.SearchResponse.Image.Total, that._maxCount);
                          },
                          function (request) {
                              return WinJS.Promise.wrapError(new WinJS.ErrorFromName(WinJS.UI.FetchError.doesNotExist));
                          });
                  }
      
  6. Das ist das letzte Instanzmember, schließen Sie also das Objekt, das Sie erstellt haben, damit es die Member enthält. Es gibt andere IListDataAdapter-Methoden, die Sie implementieren können, aber Sie benötigen diese nicht, um eine schreibgeschützte Datenquelle zu erstellen.

                // setNotificationHandler: not implemented
                // itemsFromStart: not implemented
                // itemsFromEnd: not implemented
                // itemsFromKey: not implemented
                // itemsFromDescription: not implemented
            }
    
  7. Schließen Sie den Aufruf von WinJS.Class.define.

            );
    

    Sie haben eine Klasse mit dem Namen bingImageSarchDataAdapter erstellt, die die IListDataAdapter-Schnittstelle implementiert. Als Nächstes erstellen Sie eine IListDataSource-Schnittstelle.

Schritt 3: Erstellen einer IListDataSource-Schnittstelle

Eine IListDataSource-Schnittstelle verbindet ein Steuerelement (beispielsweise ListView) mit einer IListDataAdapter-Schnittstelle. Die IListDataSource-Schnittstelle ändert die IListDataAdapter-Schnittstelle. Diese ist es, die tatsächlich die Daten ändert und abruft. In diesem Schritt implementieren Sie eine IListDataSource-Schnittstelle.

WinJS enthält eine Implementierung der IListDataSource-Schnittstelle für Sie: das VirtualizedDataSource-Objekt. Mithilfe dieses Objekts können Sie die IListDataSource-Schnittstelle implementieren. Sie werden gleich sehen, dass damit nicht viel Arbeit verbunden ist.

  1. Verwenden Sie die WinJS.Class.derive-Funktion, um eine Klasse zu erstellen, die von VirtualizedDataSource erbt. Definieren Sie für den zweiten Parameter der Funktion einen Konstruktor, der eine Bing-App-ID und eine Abfragezeichenfolge akzeptiert. Lassen Sie den Konstruktor den Basisklassenkonstruktor aufrufen, und übergeben Sie diesem ein neues bingImageSarchDataAdapter-Objekt (das Objekt, das Sie im vorherigen Schritt definiert haben).

        var bingImageSearchDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (devkey, query) {
            this._baseDataSourceConstructor(new bingImageSearchDataAdapter(devkey, query));
        });
    
  2. Verwenden Sie die WinJS.Namespace.define-Funktion, um einen Namespace zu definieren und den öffentlichen Zugriff auf die Klasse zu ermöglichen. Die WinJS.Namespace.define-Funktion akzeptiert zwei Parameter: den Namen des zu erstellenden Namespace und ein Objekt, das mindestens ein Eigenschaft/Wert-Paar enthält. Jede Eigenschaft ist der öffentliche Name des Members, und jeder Wert ist eine zugrunde liegende Variable, Eigenschaft oder Funktion in Ihrem privaten Code, die Sie verfügbar machen möchten.

        WinJS.Namespace.define("DataExamples", { bingImageSearchDataSource: bingImageSearchDataSource });  
    
  3. Sie haben nun eine IListDataAdapter-Schnittstelle und eine IListDataSource-Schnittstelle implementiert. Sie sind mit bingImageSearchDataSource.js fertig und können daher die äußere anonyme Funktion schließen.

    })();
    

    Um die benutzerdefinierte Datenquelle zu verwenden, erstellen Sie eine neue Instanz der bingImageSearchDataSource-Klasse. Übergeben Sie dem Konstruktor die Bing-App-ID für die App und eine Suchabfrage:

    var myDataSrc = new DataExamples.bingImageSearchDataSource(devKey, searchTerm);
    

    Jetzt können Sie bingImageSearchDataSource mit Steuerelementen verwenden, die eine IListDataSource-Schnittstelle akzeptieren, beispielsweise mit dem ListView-Steuerelement.

Vollständiges Beispiel

Hier ist der vollständige Code für "bingImageSearchDataSource.js". Das vollständige Beispiel finden Sie unter Arbeiten mit Datenquellen.

// Bing image search data source example
//
// This code implements a datasource that will fetch images from Bing's image search feature
// Because the Bing service requires a developer key, and each app needs its own key, you must
// register as a developer and obtain an App ID to use as a key. 
// For more info about how to obtain a key and use the Bing API, see
// https://bing.com/developers and https://msdn.microsoft.com/en-us/library/dd251056.aspx


(function () {

    // Define the IListDataAdapter.
    var bingImageSearchDataAdapter = WinJS.Class.define(
        function (devkey, query) {

            // Constructor
            this._minPageSize = 10;  // based on the default of 10
            this._maxPageSize = 50;  // max request size for bing images
            this._maxCount = 1000;   // limit on the bing API
            this._devkey = devkey;
            this._query = query;
        },

        // IListDataDapter methods
        // These methods define the contract between the IListDataSource and the IListDataAdapter.
        // These methods will be called by vIListDataSource to fetch items, 
        // get the number of items, and so on.
        {
            // This example only implements the itemsFromIndex and count methods

            // The itemsFromIndex method is called by the IListDataSource 
            // to retrieve items. 
            // It will request a specific item and hints for a number of items before and after the
            // requested item. 
            // The implementation should return the requested item. You can choose how many
            // additional items to send back. It can be more or less than those requested.
            //
            //   This funtion must return an object that implements IFetchResult. 
            itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                var that = this;
                if (requestIndex >= that._maxCount) {
                    return Promise.wrapError(new WinJS.ErrorFromName(UI.FetchError.doesNotExist));
                }

                var fetchSize, fetchIndex;

                // See which side of the requestIndex is the overlap.
                if (countBefore > countAfter) {
                    // Limit the overlap
                    countAfter = Math.min(countAfter, 10);

                    // Bound the request size based on the minimum and maximum sizes.
                    var fetchBefore = Math.max(
                        Math.min(countBefore, that._maxPageSize - (countAfter + 1)),
                        that._minPageSize - (countAfter + 1)
                        );
                    fetchSize = fetchBefore + countAfter + 1;
                    fetchIndex = requestIndex - fetchBefore;
                } else {
                    countBefore = Math.min(countBefore, 10);
                    var fetchAfter = Math.max(Math.min(countAfter, that._maxPageSize - (countBefore + 1)), that._minPageSize - (countBefore + 1));
                    fetchSize = countBefore + fetchAfter + 1;
                    fetchIndex = requestIndex - countBefore;
                }

                // Create the request string. 
                var requestStr = "http://api.bing.net/json.aspx?"
                + "AppId=" + that._devkey
                + "&Query=" + that._query
                + "&Sources=Image"
                + "&Version=2.0"
                + "&Market=en-us"
                + "&Adult=Strict"
                + "&Filters=Aspect:Wide"
                + "&Image.Count=" + fetchSize
                + "&Image.Offset=" + fetchIndex
                + "&JsonType=raw";

                // Return the promise from making an XMLHttpRequest to the server.
                return WinJS.xhr({ url: requestStr }).then(

                    // The callback for a successful operation. 
                    function (request) {
                        var results = [], count;

                        // Use the JSON parser on the results (it's safer than using eval).
                        var obj = JSON.parse(request.responseText);

                        // Verify that the service returned images.
                        if (obj.SearchResponse.Image !== undefined) {
                            var items = obj.SearchResponse.Image.Results;

                            // Create an array of IItem objects:
                            // results =[{ key: key1, data : { field1: value, field2: value, ... }}, { key: key2, data : {...}}, ...];
                            for (var i = 0, itemsLength = items.length; i < itemsLength; i++) {
                                var dataItem = items[i];
                                results.push({
                                    key: (fetchIndex + i).toString(),
                                    data: {
                                        title: dataItem.Title,
                                        thumbnail: dataItem.Thumbnail.Url,
                                        width: dataItem.Width,
                                        height: dataItem.Height,
                                        linkurl: dataItem.Url
                                    }
                                });
                            }

                            // Get the count.
                            count = obj.SearchResponse.Image.Total;

                            return {
                                items: results, // The array of items.
                                offset: requestIndex - fetchIndex, // The index of the requested item in the items array.
                                totalCount: Math.min(count, that._maxCount), // The total number of records. Bing will only return 1000, so we cap the value.
                            };
                        } else {
                            return WinJS.UI.FetchError.doesNotExist;
                        }
                    },

                    // Called if the WinJS.xhr funtion returned an error. 
                    function (request) {
                        return WinJS.UI.FetchError.noResponse;
                    });
            },


            // Gets the number of items in the result list. 
            // The count can be updated in itemsFromIndex.
            getCount: function () {
                var that = this;

                // Create up a request for 1 item so we can get the count
                var requestStr = "http://api.bing.net/json.aspx?";

                // Common request fields (required)
                requestStr += "AppId=" + that._devkey
                + "&Query=" + that._query
                + "&Sources=Image";

                // Common request fields (optional)
                requestStr += "&Version=2.0"
                + "&Market=en-us"
                + "&Adult=Strict"
                + "&Filters=Aspect:Wide";

                // Image-specific request fields (optional)
                requestStr += "&Image.Count=1"
                + "&Image.Offset=0"
                + "&JsonType=raw";

                // Make an XMLHttpRequest to the server and use it to get the count.
                return WinJS.xhr({ url: requestStr }).then(

                    // The callback for a successful operation.
                    function (request) {
                        var data = JSON.parse(request.responseText);

                        // Bing may return a large count of items, 
                        /// but you can only fetch the first 1000.
                        return Math.min(data.SearchResponse.Image.Total, that._maxCount);
                    },
                    function (request) {
                        return WinJS.Promise.wrapError(new WinJS.ErrorFromName(WinJS.UI.FetchError.doesNotExist));
                    });
            }

  

            // setNotificationHandler: not implemented
            // itemsFromStart: not implemented
            // itemsFromEnd: not implemented
            // itemsFromKey: not implemented
            // itemsFromDescription: not implemented
        }
        );

    var bingImageSearchDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (devkey, query) {
        this._baseDataSourceConstructor(new bingImageSearchDataAdapter(devkey, query));
    });

    WinJS.Namespace.define("DataExamples", { bingImageSearchDataSource: bingImageSearchDataSource }); 

})();