2015-04-05 1 views
0

В настоящее время я пытаюсь создать современное приложение HTML/JavaScript Windows 8, в котором я хочу получить доступ к локальному файлу XML, находящемуся в каталоге установки. После прочтения многих идей и фрагментов кода по всему Интернету я придумал запутанный асинхронный метод доступа к файлу, который работает. Однако, это лучший/правильный способ сделать что-то так же просто, как доступ к локальному XML-файлу? Кроме того, я хотел бы иметь возможность загружать файл xml и сохранять объект XMLDocument как «глобальную» переменную, так что при нажатии кнопки и других триггерах объект XMLDocument может быть доступен и проанализирован. Это где начать все проблемы, так как один метод асинхронной, а затем переменные не определены, и т.д ....Windows 8 Javascript app 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); 
    }, 


}); 

})();

В заключение, если все эти сложные методы не выполняются, как мне следует делать что-то простое, как загрузка XML-файла, а затем его доступность как объекта, который может использоваться другими функциями и т. Д.?

Благодарим за помощь!

PS: Я очень новичок в JavaScript и Windows 8 Modern Apps/WinAPI. Предыдущий опыт работы на Python и Java (где это тривиально!).

ответ

1

Здесь есть несколько вещей, которые должны помочь вам.

Во-первых, существует три разных события загрузки для параметра PageControl, соответствующего методам в вашем классе страниц. Метод ready (который является единственным, в который включен шаблон проекта VS), вызывается только в конце процесса и, таким образом, несколько задерживается в процессе загрузки асинхронного файла. Уместнее сделать эту работу в рамках метода init, который вызывается до того, как на странице были созданы какие-либо элементы. (The обрабатывается метод вызывается после того, как WinJS.UI.processAll завершена, но до того, как страница была добавлена ​​в DOM. готовые вызывается после все в DOM.)

Во-вторых, ваш метод выглядит getXMLAsync хорошо, но ваш обработчик завершения объявляет другую переменную xmlDoc, а затем выбросить:

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

«вар xmlDoc» объявляет локальную переменную в обработчике, но он отброшен, как только обработчик возвращается. Что вам нужно сделать, это назначить this.xmlDoc = doc, но трюк - это то, что «это» - это объект, который вы хотите, а не глобальный контекст, который по умолчанию используется для анонимной функции. Узор, что люди, как правило, используют следующим образом:

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

Конечно, это только после того, что анонимный обработчик вызывается, что член xmlDoc будет действительным. То есть, если вы поместите console.log в конец вышеприведенного кода, после}) ;, обработчик еще не будет вызван из потока async, поэтому xmlDoc не будет действительным. Если вы поместите его внутри обработчика сразу после этого. XmlDoc = doc, тогда он должен быть действительным.

Это все о том, как привыкнуть к работе async.:)

Теперь, чтобы упростить дело для вас, существует статический метод StorageFile.getFileFromApplicationUriAsync, который вы можете использовать для прямого доступа к файлу внутри пакета с помощью одного вызова, а не для навигации по папкам. С помощью этого вы можете загрузить создание XmlDocument следующим образом:

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

Обратите внимание, что необходимы три ///; ms-appx: /// - это схема URI, которая относится к содержимому пакета приложения.

Также обратите внимание на то, как обещания связаны цепью вместо вложенных. Это, как правило, лучшая структура, и та, которая позволяет такую ​​функцию возвращать обещание, которое будет выполнено с последним возвратным значением в цепочке. Затем это можно использовать с более ранним битом кода, который присваивает this.xmlDoc, и вы избегаете передачи в obj и обратном вызове (обещания направлены на то, чтобы избежать таких обратных вызовов).

В целом, если у вас есть другие страницы в приложении, к которым вы будете перемещаться, вы действительно захотите загрузить этот XML-файл и создать XmlDocument один раз для приложения, а не на определенную страницу. В противном случае вы будете перезагружать файл каждый раз, когда вы переходите на страницу. По этой причине вы можете выбрать загрузку при запуске приложения, а не загрузку страницы, и использовать WinJS.Namespace.define для создания переменной пространства имен, в которой вы храните xmlDoc. Поскольку этот код загружался при запуске, пока экран заставки видимый, все должно быть готово при первой странице. Что-то думать о.

В любом случае, учитывая, что вы новичок в этом пространстве, я предлагаю вам скачать бесплатную электронную книгу, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, где в Главе 3 есть все сведения о запуске приложений, элементах управления страницами и обещаниях (после более широких представлений о Главы 1 и 2, конечно).

+0

Большое спасибо за подробный ответ! С этой информацией и другими, у меня было приложение работает в течение последних месяцев с перезагрузкой XML-документа на каждой странице навигации, и теперь я пытаюсь это исправить! – cloudcrypt

+0

Я поставил свою новую проблему в вопрос: http://stackoverflow.com/questions/31353887/windows-8-javascript-app-activation-launch-deferral – cloudcrypt