2015-04-05 1 views
0

Je tente actuellement de faire une application moderne de Windows HTML/JavaScript 8 dans lequel je veux accéder à un fichier XML local qui se trouve dans le répertoire d'installation. Après avoir lu de nombreuses idées et extraits de code sur le Web, j'ai trouvé une méthode asynchrone compliquée d'accès au fichier, qui fonctionne. Cependant, est-ce la meilleure façon de faire quelque chose d'aussi simple que d'accéder à un fichier XML local? De plus, je voudrais être en mesure d'avoir une fonction charger le fichier xml et enregistrez l'objet XMLDocument comme une variable « globale », de sorte que sur les presses de boutons et autres déclencheurs, l'objet XMLDocument est accessible et analysé. C'est là tous les problèmes commencent, car une méthode est async, et les variables ne sont pas définies, etc ....Windows 8 Javascript application XML Object

(function() { 
"use strict"; 

WinJS.UI.Pages.define("/pages/reader/reader.html", { 
    // This function is called whenever a user navigates to this page. It 
    // populates the page elements with the app's data. 
    ready: function (element, options) { 
     // TODO: Initialize the page here. 
     var button = document.getElementById("changeText"); 
     button.addEventListener("click", this.buttonClickHandler, false); 
     var dropdown = document.getElementById("volumeDropdown"); 
     dropdown.addEventListener("change", this.volumeChangeHandler, false); 
     var loadSettings = new Windows.Data.Xml.Dom.XmlLoadSettings; 
     loadSettings.prohibitDtd = false; 
     loadSettings.resolveExternals = false; 

     //previous attempt, also didn't work: 
     //this.xmlDoc = null; 
     //this.loadXMLdoc(this, this.testXML); 

     //also not working: 
     this.getXmlAsync().then(function (doc) { 
      var xmlDoc = doc; 
     }); 

     //this never works also, xmlDoc always undefined, or an error: 
     //console.log(xmlDoc);    
    }, 
    buttonClickHandler: function (eventInfo) { 
     // doesn't work, xmlDoc undefined or error: 
     console.log(xmlDoc); 
    }, 
    volumeChangeHandler: function (eventInfo) { 
     var e = document.getElementById("volumeDropdown"); 
     // of course doesn't work, since I can't save the XMLDocument object into a variable (works otherwise): 
     var nodelist2 = xmlDoc.selectNodes('//volume[@name="volumeName"]/chapter/@n'.replace('volumeName', list[0])); 
     var volumeLength = nodelist2.length; 
     for (var index = 0; index < volumeLength; index++) { 
      var option = document.createElement("option"); 
      option.text = index + 1; 
      option.value = index + 1; 
      var volumeDropdown = document.getElementById("chapterDropdown"); 
      volumeDropdown.appendChild(option); 
     } 
    }, 
    getXmlAsync: function() { 
     return Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("books").then(function (externalDtdFolder) { 
      externalDtdFolder.getFileAsync("book.xml").done(function (file) { 
       return Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file); 
      }) 
     }) 
    }, 
    loadXMLdoc: function (obj, callback) { 
     var loadSettings = new Windows.Data.Xml.Dom.XmlLoadSettings; 
     loadSettings.prohibitDtd = false; 
     loadSettings.resolveExternals = false; 
     Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("books").then(function (externalDtdFolder) { 
      externalDtdFolder.getFileAsync("book.xml").done(function (file) { 
       Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file, loadSettings).then(function (doc) { 

        var nodelist = doc.selectNodes("//volume/@name"); 
        var list = []; 
        for (var index = 0; index < nodelist.length; index++) { 
         list.push(nodelist[index].innerText); 
        }; 
        for (var index = 0; index < list.length; index++) { 
         var option = document.createElement("option"); 
         option.text = list[index] + "new!"; 
         option.value = list[index]; 
         var volumeDropdown = document.getElementById("volumeDropdown"); 
         volumeDropdown.appendChild(option); 
        }; 
        var nodelist2 = doc.selectNodes('//volume[@name="volumeName"]/chapter/@n'.replace('volumeName', list[0])); 
        var volumeLength = nodelist2.length; 
        for (var index = 0; index < volumeLength; index++) { 
         var option = document.createElement("option"); 
         option.text = index + 1; 
         option.value = index + 1; 
         var volumeDropdown = document.getElementById("chapterDropdown"); 
         volumeDropdown.appendChild(option); 
        }; 


        obj.xmlDoc = doc; 
        callback(obj); 

       }) 
      }) 
     }); 
    }, 
    initializeXML: function (doc, obj) { 
     console.log("WE ARE IN INITIALIZEXML NOW") 
     obj.xmlDoc = doc; 
    }, 
    testXML: function (obj) { 
     console.log(obj.xmlDoc); 
    }, 


}); 

})(); En résumé, avec toutes ces méthodes compliquées qui échouent, comment dois-je faire quelque chose d'aussi simple que de charger un fichier XML, puis de le rendre disponible comme objet pouvant être utilisé par d'autres fonctions, etc.?

Merci pour votre aide!

PS: Je suis très nouveau pour JavaScript et Windows 8 Apps moderne/WinAPIs. Expérience précédente tout en Python et Java (où faire ceci est trivial!).

Répondre

1

Il y a deux choses qui se passent ici qui devrait vous aider.

D'abord, il y a trois événements de chargement différents pour un PageControl, correspondant aux méthodes dans votre classe de page. La méthode ready ready (qui est la seule que le modèle de projet VS inclut) est appelée uniquement à la fin du processus et est donc un peu en retard dans le processus de chargement d'un fichier asynchrone. Il est plus approprié de faire ce travail dans la méthode init, qui est appelée avant que tous les éléments aient été créés sur la page. (La méthode traitée est appelée après WinJS.UI.processAll est terminée, mais avant que la page a été ajoutée au DOM. prêt est appelé après tout est dans les DOM.)

Deuxièmement, votre apparence méthode getXMLAsync bien, mais votre gestionnaire complet est déclarer une autre variable xmlDoc puis jeter:

this.getXmlAsync().then(function (doc) { 
    var xmlDoc = doc; //local variable gets discarded 
}); 

le « var xmlDoc » déclare une variable locale dans le gestionnaire, mais il est mis au rebut dès le retour du gestionnaire. Ce que vous devez faire est d'assigner this.xmlDoc = doc, mais l'astuce est alors de s'assurer que "this" est l'objet que vous voulez qu'il soit plutôt que le contexte global, qui est la valeur par défaut pour une fonction anonyme. Le modèle que les gens utilisent généralement comme suit:

var that = this; 
this.getXmlAsync().then(function (doc) { 
    that.xmlDoc = doc; 
}); 

Bien sûr, il est seulement après que gestionnaire anonyme est appelé que le membre xmlDoc sera valide. Autrement dit, si vous mettez un fichier console.log à la fin du code ci-dessus, après le}) ;, le gestionnaire n'aura pas encore été appelé depuis le thread asynchrone, donc xmlDoc ne sera pas valide. Si vous le placez dans le gestionnaire immédiatement après that.xmlDoc = doc, alors il devrait être valide.

Tout cela est à peu près pour s'y habituer comment fonctionne async.:)

Maintenant, pour vous simplifier un peu, il existe la méthode statique StorageFile.getFileFromApplicationUriAsync que vous pouvez utiliser pour accéder directement au fichier dans un package avec un seul appel, plutôt que de naviguer dans les dossiers. Avec ceci vous pouvez charger créer le XmlDocument comme suit:

getXmlAsync: function() { 
    return StorageFile.getFileFromApplicationUriAsync("ms-appx:///books/book.xml").then((function (file) { 
     return Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file); 
    }).then(function (xmlDoc) { 
     return xmlDoc; 
    }); 
} 

Notez que les trois /// sont nécessaires; ms-appx: /// est un schéma d'URI qui va au contenu du paquet de l'application.

Notez également que les promesses sont chaînées et non imbriquées. C'est généralement une meilleure structure, et celle qui permet à une telle fonction de renvoyer une promesse qui sera satisfaite avec la dernière valeur de retour dans la chaîne. Cela peut ensuite être utilisé avec le bit de code précédent qui affecte that.xmlDoc, et vous évitez de passer dans obj et un rappel (les promesses sont destinées à éviter de tels rappels). Dans l'ensemble, si vous avez d'autres pages dans votre application, vous voudrez vraiment charger ce fichier XML et créer le XmlDocument une fois pour l'application, pas avec la page spécifique. Sinon, vous rechargerez le fichier chaque fois que vous naviguez sur la page. Pour cette raison, vous pouvez choisir d'effectuer le chargement au démarrage de l'application, et non de charger la page, et d'utiliser WinJS.Namespace.define pour créer une variable d'espace de noms dans laquelle vous stockez xmlDoc. Parce que ce code chargerait au démarrage alors que l'écran de démarrage est visible, tout devrait être prêt quand la première page apparaît. Quelque chose à quoi penser. En tout cas, étant donné que vous êtes nouveau dans cet espace, je vous suggère de télécharger mon ebook gratuit, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, où le chapitre 3 contient tous les détails sur le démarrage de l'application, les contrôles de page et les promesses (après les introductions plus générales de Chapitres 1 et 2 bien sûr).

+0

Merci beaucoup pour votre réponse détaillée! Avec cette information et d'autres, j'ai eu l'application qui travaille au cours des derniers mois avec le rechargement du document xml sur chaque navigation de page et j'essaie maintenant de résoudre ce problème! – cloudcrypt

+0

J'ai mis mon nouveau numéro dans une question ici: http://stackoverflow.com/questions/31353887/windows-8-javascript-app-activation-launch-deferral – cloudcrypt