Windows 8 Javascript 应用程序 XML 对象
Windows 8 Javascript app XML Object
我目前正在尝试制作 HTML/JavaScript Windows 8 现代应用程序,我想在其中访问安装目录中的本地 XML 文件。
在网上阅读了许多想法和代码片段后,我想出了一个复杂的异步访问文件的方法,这个方法很有效。但是,这是执行像访问本地 XML 文件这样简单的事情的 best/correct 方法吗?
此外,我希望能够有一个函数加载 xml 文件,并将 XMLDocument 对象保存为 "global" 变量,以便在按下按钮和其他触发器时,可以访问和解析 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 和 Windows 8 Modern Apps/WinAPIs 的新手。
以前的经验都在 Python 和 Java 中(这样做是微不足道的!)。
这里发生的一些事情应该可以帮助您。
首先,PageControl 有三种不同的加载事件,对应于您页面中的方法 class。 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" 在处理程序中声明了一个局部变量,但它在处理程序 return 后立即被丢弃。您需要做的是分配 this.xmlDoc = doc,但诀窍是确保 "this" 是您希望它成为的对象,而不是全局上下文,这是匿名函数的默认值.人们普遍使用的模式如下:
var that = this;
this.getXmlAsync().then(function (doc) {
that.xmlDoc = doc;
});
当然,只有在匿名处理程序被调用之后,xmlDoc 成员才会有效。也就是说,如果您在上面代码的末尾放置一个 console.log,在 }) 之后,处理程序将尚未从异步线程中调用,因此 xmlDoc 将无效。如果你在 that.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 是如何链接而不是嵌套的。这通常是一个更好的结构,并且允许这样的函数 return 承诺将由链中的最后一个 return 值实现。然后可以将其与较早的分配 that.xmlDoc 的代码一起使用,并且您可以避免传入 obj 和回调(承诺旨在避免此类回调)。
总的来说,如果您的应用程序中有您要导航到的任何其他页面,您真的希望加载此 XML 文件并为该应用程序创建一次 XmlDocument,而不是使用特定的页。否则,您每次导航到该页面时都会重新加载该文件。出于这个原因,您可以选择在应用程序启动时加载,而不是页面加载,并使用 WinJS.Namespace.define 创建一个 namespace 变量来存储 xmlDoc。因为该代码会在初始屏幕可见时在启动时加载,所以当第一页出现时,一切都应该准备就绪。需要考虑的事情。
无论如何,鉴于您是这个 space 的新手,我建议您下载我的免费电子书 Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition,其中第 3 章包含有关应用程序启动、页面控件的所有详细信息和 promises(当然是在对第 1 章和第 2 章进行更广泛的介绍之后)。
我目前正在尝试制作 HTML/JavaScript Windows 8 现代应用程序,我想在其中访问安装目录中的本地 XML 文件。 在网上阅读了许多想法和代码片段后,我想出了一个复杂的异步访问文件的方法,这个方法很有效。但是,这是执行像访问本地 XML 文件这样简单的事情的 best/correct 方法吗? 此外,我希望能够有一个函数加载 xml 文件,并将 XMLDocument 对象保存为 "global" 变量,以便在按下按钮和其他触发器时,可以访问和解析 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 和 Windows 8 Modern Apps/WinAPIs 的新手。 以前的经验都在 Python 和 Java 中(这样做是微不足道的!)。
这里发生的一些事情应该可以帮助您。
首先,PageControl 有三种不同的加载事件,对应于您页面中的方法 class。 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" 在处理程序中声明了一个局部变量,但它在处理程序 return 后立即被丢弃。您需要做的是分配 this.xmlDoc = doc,但诀窍是确保 "this" 是您希望它成为的对象,而不是全局上下文,这是匿名函数的默认值.人们普遍使用的模式如下:
var that = this;
this.getXmlAsync().then(function (doc) {
that.xmlDoc = doc;
});
当然,只有在匿名处理程序被调用之后,xmlDoc 成员才会有效。也就是说,如果您在上面代码的末尾放置一个 console.log,在 }) 之后,处理程序将尚未从异步线程中调用,因此 xmlDoc 将无效。如果你在 that.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 是如何链接而不是嵌套的。这通常是一个更好的结构,并且允许这样的函数 return 承诺将由链中的最后一个 return 值实现。然后可以将其与较早的分配 that.xmlDoc 的代码一起使用,并且您可以避免传入 obj 和回调(承诺旨在避免此类回调)。
总的来说,如果您的应用程序中有您要导航到的任何其他页面,您真的希望加载此 XML 文件并为该应用程序创建一次 XmlDocument,而不是使用特定的页。否则,您每次导航到该页面时都会重新加载该文件。出于这个原因,您可以选择在应用程序启动时加载,而不是页面加载,并使用 WinJS.Namespace.define 创建一个 namespace 变量来存储 xmlDoc。因为该代码会在初始屏幕可见时在启动时加载,所以当第一页出现时,一切都应该准备就绪。需要考虑的事情。
无论如何,鉴于您是这个 space 的新手,我建议您下载我的免费电子书 Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition,其中第 3 章包含有关应用程序启动、页面控件的所有详细信息和 promises(当然是在对第 1 章和第 2 章进行更广泛的介绍之后)。