Windows 8 Javascript应用程序XML对象

Windows 8 Javascript app XML Object

本文关键字:XML 对象 应用程序 Javascript Windows      更新时间:2023-09-26

我目前正在尝试制作一个HTML/JavaScriptWindows8现代应用程序,我想在其中访问安装目录中的本地XML文件。在阅读了网上的许多想法和代码片段后,我想出了一种访问文件的复杂异步方法,这种方法很有效。但是,这是访问本地XML文件这样简单的操作的最佳/正确方法吗?此外,我希望能够让一个函数加载xml文件,并将XMLDocument对象保存为"全局"变量,以便在按下按钮和其他触发器时,可以访问和解析XMLDocument对象。这就是所有问题的开始,因为一个方法是异步的,然后变量是未定义的,等等…

(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和Windows8现代应用程序/WinAPI非常陌生。以前在Python和Java方面的经验(在那里做这件事很琐碎!)。

这里有一些事情应该会对您有所帮助。

首先,PageControl有三个不同的加载事件,与页面类中的方法相对应。ready方法(这是VS项目模板中唯一包含的方法)仅在过程结束时被调用,因此在异步文件加载过程中有些滞后。在init方法中执行这项工作更合适,该方法在页面上创建任何元素之前调用。(processed方法是在WinJS.UI.processAll完成之后,但在页面添加到DOM之前调用的。ready是在所有内容都在DOM中之后调用的。)

其次,您的getXMLAsync方法看起来不错,但您完成的处理程序正在声明另一个xmlDoc变量,然后将其丢弃:

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

"var-xmlDoc"在处理程序中声明了一个局部变量,但一旦处理程序返回,它就会被丢弃。你需要做的是分配this.xml doc=doc,但诀窍是确保"this"是你想要的对象,而不是全局上下文,这是匿名函数的默认值。人们通常使用的模式如下:

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

当然,只有在调用了匿名处理程序之后,xmlDoc成员才会有效。也就是说,如果您将console.log放在上面代码的末尾,在}之后);,处理程序还没有从异步线程调用,所以xmlDoc将不会有效。如果您在.xmlDoc=doc之后立即将其放入处理程序中,那么它应该是有效的。

这一切都只是为了习惯异步的工作方式。:)

现在,为了稍微简化一下,有一个静态方法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方案。

还要注意promise是如何链接而不是嵌套的。这通常是一个更好的结构,它允许像这样的函数返回一个承诺,该承诺将用链中的最后一个返回值来实现。然后,这可以与分配.xmlDoc的早期代码一起使用,并且可以避免传递obj和回调(promise旨在避免此类回调)。

总的来说,如果您的应用程序中有任何其他页面要导航到,那么您确实希望加载此XML文件并为应用程序创建一次XmlDocument,而不是使用特定页面。否则,每次导航到页面时都会重新加载该文件。因此,您可以选择在应用程序启动时加载,而不是页面加载,并使用WinJS.Namespace.define创建一个存储xmlDoc的命名空间变量。因为该代码将在启动时加载,而启动屏幕是可见的,所以当第一个页面出现时,一切都应该准备好。值得思考的事情。

无论如何,如果你是这个领域的新手,我建议你下载我的免费电子书《用HTML、CSS和JavaScript编程Windows应用商店》,第二版,其中第3章包含了关于应用程序启动、页面控制和承诺的所有细节(当然是在第1章和第2章更广泛的介绍之后)。