Comment créer une source de données personnalisée (HTML)

[ Cet article est destiné aux développeurs de Windows 8.x et Windows Phone 8.x qui créent des applications Windows Runtime. Si vous développez une application pour Windows 10, voir la Documentation ]

La bibliothèque Windows pour JavaScript fournit plusieurs objets source de données prêts à l’emploi qui permettent de remplir un objet ListView ou FlipView avec différents types de données : l’objet WinJS.Binding.List, qui permet d’accéder aux tableaux et aux données JSON, et l’objet StorageDataSource, qui est utilisé pour accéder aux informations relatives au système de fichiers.

Vous n’êtes pas limité à ces sources de données. Vous pouvez également créer votre propre source de données personnalisée qui accède à un autre type de données, tel qu’un fichier XML ou un service Web. Cette rubrique vous montre comment implémenter une source de données personnalisée qui accède à un service Web. La source de données utilise une demande XHR pour accéder au service de recherche d’images Bing, puis elle affiche les résultats dans un objet ListView.

(Le service Bing requiert que chaque application possède sa propre clé d’identification. Vous devez donc vous procurer cette clé afin de pouvoir utiliser ce code. Pour plus d’informations sur l’obtention d’une clé d’identification d’application, voir le Centre de développement Bing.)

Pour créer une source de données personnalisée, vous avez besoin d’objets qui implémentent les interfaces IListDataAdapter et IListDataSource. WinJS fournit un objet VirtualizedDataSource qui implémente l’objet IListDataSource. Pour ce faire, vous devez hériter de cet objet et passer un élément IListDataAdapter au constructeur de base. Vous devez créer votre propre objet qui implémente l’interface de l’objet IListDataAdapter.

IListDataAdapter interagit directement avec la source de données pour récupérer ou mettre à jour les éléments. IListDataSource se connecte à un contrôle et manipule IListDataAdapter.

Prérequis

Instructions

Étape 1: créer un fichier JavaScript pour votre source de données personnalisée

  1. Utilisez Microsoft Visual Studio afin d’ajouter un fichier JavaScript dans votre projet. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le dossier js du projet, puis sélectionnez Ajouter > Nouvel élément. La boîte de dialogue Ajouter un nouvel élément s’ouvre.
  2. Sélectionnez Fichier JavaScript. Nommez-le "bingImageSearchDataSource.js". Cliquez sur Ajouter pour créer le fichier. Visual Studio crée un fichier JavaScript vide nommé bingImageSearchDataSource.js.

Étape 2: créer un objet IListDataAdapter

L’étape suivante consiste à créer un objet qui implémente l’interface IListDataAdapter. L’interface IListDataAdapter récupère les données d’une source de données afin de les fournir à un objet IListDataSource.

L’interface IListDataAdapter prend en charge l’accès en lecture et en écriture, ainsi que les notifications de changement. Toutefois, vous n’avez pas besoin d’implémenter toute l’interface : vous pouvez créer une interface simple en lecture seule, IListDataAdapter, en implémentant uniquement les méthodes itemsFromIndex et getCount.

  1. Ouvrez bingImageSearchDataSource.js, le fichier JavaScript que vous avez créé lors de l’étape précédente.

  2. Créez une fonction anonyme et activez le mode strict.

    Comme indiqué dans Codage d’applications de base, il est judicieux d’encapsuler votre code JavaScript en l’incluant dans une fonction anonyme, qui plus est en mode strict.

    (function () {
        "use strict"; 
    
  3. Utilisez la fonction WinJS.Class.define pour créer votre implémentation de l’interface IListDataAdapter. Le constructeur de classe est le premier paramètre que la fonction WinJS.Class.define prend.

    Cette interface IListDataAdapter se connecte ensuite au service de recherche Bing. La requête de recherche lancée dans l’API Bing a besoin de certaines données. Nous allons stocker ces données, entre autres, dans l’interface IListDataAdapter en tant que membres de classe :

    • _minPageSize : nombre minimal d’éléments par page.
    • _maxPageSize : nombre maximal d’éléments par page.
    • _maxCount : nombre maximal d’éléments renvoyés.
    • _devKey : clé d’identification de l’application. L’API Bing nécessite une clé d’identification pour identifier l’application.
    • _query : chaîne de recherche.

    Créez un constructeur qui prend une clé d’identification d’application pour l’API Bing et une chaîne de requête, et qui fournit des valeurs pour les autres membres.

    
        // 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. Le paramètre suivant attendu par la fonction WinJS.Class.define est un objet contenant les membres d’instance de la classe. Cet objet vous permet d’implémenter les méthodes itemsFromIndex et getCount.

    Créez l’accolade ouvrante pour cet objet.

            // IListDataDapter methods
            // These methods define the contract between the IListDataSource and the IListDataAdapter.
            {
    
    1. Implémentez la méthode itemsFromIndex. La méthode itemsFromIndex se connecte à la source de données, puis renvoie les données demandées dans un objet IFetchResult. La méthode itemsFromIndex prend les trois paramètres suivants : l’index de l’élément à récupérer, le nombre d’éléments situés avant l’élément à récupérer et le nombre d’éléments situés après l’élément à récupérer.

                  itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                      var that = this;
      
    2. Vérifiez que l’index de l’élément demandé (requestIndex) est inférieur au nombre maximal d’éléments à récupérer. Sinon, une erreur se produit.

                      if (requestIndex >= that._maxCount) {
                          return Promise.wrapError(new WinJS.ErrorFromName(UI.FetchError.doesNotExist));
                      }
      
    3. Utilisez les paramètres requestIndex, countBefore et countAfter pour calculer l’index du premier élément et la taille de la requête. Les paramètres countBefore et countAfter fournissent des recommandations sur la quantité de données à récupérer ; vous n’êtes pas obligé de récupérer la totalité des éléments demandés. Dans cet exemple, le service Bing limite la taille de la requête à 50 éléments maximum. Nous allons donc faire de même.

      Une requête demande généralement un ou deux éléments avant et après l’élément demandé, et un nombre supérieur pour l’autre partie. Nous tiendrons compte de ces considérations lorsque nous ferons notre requête au serveur.

                      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. Créez la chaîne de requête.

                      // 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. Envoyez la requête à l’aide de la fonction WinJS.xhr. La fonction WinJS.xhr renvoie un objet Promise contenant le résultat. Vous pouvez traiter le résultat en appelant la méthode then de l’objet Promise.

                      return WinJS.xhr({ url: requestStr }).then(
      
    6. Créez un rappel pour une exécution réussie de la fonction WinJS.xhr. Cette fonction traite les résultats et renvoie ces derniers dans un élément IFetchResult. L’élément IFetchResult comporte les trois propriétés suivantes :

      • items : tableau d’objets IItem représentant les résultats de la requête.
      • offset : index de l’élément demandé dans le tableau d’éléments.
      • totalCount : nombre total d’éléments dans le tableau d’éléments.

      Chaque élément IItem doit avoir une propriété key et une propriété data contenant l’identificateur et les données de l’élément, respectivement :

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

      Voici à quoi doit ressembler le tableau d’objets IItem :

      [{ 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. Créez un rappel pour une exécution non réussie de la fonction WinJS.xhr .

                          // Called if the WinJS.xhr funtion returned an error. 
                          function (request) {
                              return WinJS.UI.FetchError.noResponse;
                          });
      
    8. Fermez la méthode itemsFromIndex. Étant donné que vous allez définir une autre méthode, ajoutez une virgule après avoir fermé itemsFromIndex.

                  },
      
  5. Implémentez la méthode getCount.

    1. La méthode getCount ne prend aucun paramètre et retourne un objet Promise avec le nombre d’éléments contenus dans les résultats de l’objet IListDataAdapter .

                  // Gets the number of items in the result list. 
                  // The count can be updated in itemsFromIndex.
                  getCount: function () {
                      var that = this;
      
    2. Créez la chaîne de requête. Le service Bing ne demande pas explicitement le nombre. Pour obtenir ce dernier, nous allons demander et utiliser un enregistrement.

      
                      // 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. Envoyez la requête à l’aide de la fonction WinJS.xhr. Traitez les résultats et renvoyez le nombre.

                      // 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. Il s’agit du dernier membre d’instance. Vous pouvez donc fermer l’objet que vous avez créé pour les membres. Vous pourriez implémenter d’autres méthodes IListDataAdapter, mais vous n’en avez pas besoin pour créer une source de données en lecture seule.

                // setNotificationHandler: not implemented
                // itemsFromStart: not implemented
                // itemsFromEnd: not implemented
                // itemsFromKey: not implemented
                // itemsFromDescription: not implemented
            }
    
  7. Fermez l’appel à WinJS.Class.define.

            );
    

    Vous avez créé la classe bingImageSarchDataAdapter qui implémente l’interface IListDataAdapter. Vous allez maintenant créer un objet IListDataSource.

Étape 3: créer un objet IListDataSource

L’objet IListDataSource connecte un contrôle (ListView, par exemple) à un objet IListDataAdapter. L’objet IListDataSource manipule l’objet IListDataAdapter, qui est chargé de manipuler et de récupérer les données. Dans cette étape, vous implémentez un objet IListDataSource.

WinJS fournit une seule implémentation de l’interface IListDataSource : l’objet VirtualizedDataSource. Vous pouvez utiliser cet objet pour faciliter l’implémentation de votre objet IListDataSource. Comme vous allez le constater ci-après, cela demande peu de travail d’écriture de code.

  1. Utilisez la fonction WinJS.Class.derive pour créer une classe qui hérite de l’objet VirtualizedDataSource. Pour le deuxième paramètre de la fonction, définissez un constructeur qui prend une clé d’identification d’application pour l’API Bing et une chaîne de requête. Utilisez ce constructeur pour appeler le constructeur de classe de base, puis transmettez-le au nouvel objet bingImageSarchDataAdapter que vous avez défini lors de l’étape précédente.

        var bingImageSearchDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (devkey, query) {
            this._baseDataSourceConstructor(new bingImageSearchDataAdapter(devkey, query));
        });
    
  2. Utilisez la fonction WinJS.Namespace.define pour définir un espace de noms et rendre la classe accessible publiquement. La fonction WinJS.Namespace.define prend les deux paramètres suivants : le nom de l’espace de noms à créer et un objet contenant une ou plusieurs paires propriété/valeur. La propriété indique le nom public du membre, tandis que la valeur désigne la variable, propriété ou fonction sous-jacente dans votre code privé que vous souhaitez exposer.

        WinJS.Namespace.define("DataExamples", { bingImageSearchDataSource: bingImageSearchDataSource });  
    
  3. Vous avez implémenté un objet IListDataAdapter et un objet IListDataSource. Vous avez terminé de modifier le fichier bingImageSearchDataSource.js, vous pouvez donc fermer la fonction anonyme externe.

    })();
    

    Pour utiliser votre source de données personnalisée, créez une nouvelle instance de la classe bingImageSearchDataSource. Transmettez au constructeur la clé d’identification d’application pour l’API Bing et une chaîne de requête :

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

    Vous pouvez maintenant utiliser bingImageSearchDataSource avec des contrôles qui prennent un objet IListDataSource, tel que le contrôle ListView.

Terminer l’exemple

Voici le code complet du fichier bingImageSearchDataSource.js. Pour obtenir un exemple complet, voir Utilisation des sources de données.

// 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 }); 

})();