Q.Promise和KO.mapping不将对象数组转换为正确类型

时间:2016-06-01 15:47:37

标签: javascript arrays knockout.js typescript q

我正在尝试过滤一个可观察的数组,但由于我认为是ko.utils.arrayFilter方法将所有模型的字段名称更改为小写,因此遇到问题。我应该注意到这个项目正在使用Typescript。

这是我的模特:

    export class MyListModel {
    constructor(jsObject?: {}) {
        if (jsObject) {
            ko.mapping.fromJS(jsObject, {}, this);
        }
    }
    Text = ko.observable<string>();
    Value = ko.observable<string>();
}

在我的viewModel中,我有以下字段:

  inches = ko.observableArray<Models.MyListModel>([]);

在程序的另一部分中,我调用filterInches()方法根据某些条件过滤数组。 value参数是下拉列表中当前选定的值。

    filterInches(value) {

        if (value == 6) {
            var filtered = ko.utils.arrayFilter(this.inches(),
                function (item) {

                    if (parseInt(item.Text()) <= 8)
                        return true;
                });

            this.filteredInches(filtered);
        } else {
            this.filteredInches(this.inches());
        }
    }

在编译时没有抛出任何错误,但是当我在浏览器中运行应用程序时,我收到错误说&#34; item.Text不是函数&#34;。当我逐步浏览Chrome中的代码时,看起来项目会转换为带有文本和值字段的匿名对象。这些字段现在是小写的,我认为这会导致我遇到的问题。可能导致这种行为的原因是什么?

修改 我正在处理此代码中不同但相关的部分,我想我已经开始明白为什么它不起作用了。我相信它与Q Promise库有关,但是我对这个库不太了解,弄清楚它为什么不工作(是的,我阅读了文档)。我认为编写此代码的开发人员并没有意识到它没有做他们认为正在做的事情。

为验证出现问题,我尝试过的第一件事就是修改模型的属性名称:

    export class MyListModel {
constructor(jsObject?: {}) {
    if (jsObject) {
        ko.mapping.fromJS(jsObject, {}, this);
    }
}
  Cat = ko.observable<string>();
  Chicken = ko.observable<string>();
}

现在,如果我们回到修改后的filterInches()方法,我们将看到item.Cat在编译时工作,但是当我在Chrome中逐步执行代码时,item对象实际上没有名为Cat的属性(它&#39;未定义)。它的属性仍然是文本和值:

    filterInches(value) {

    if (value == 6) {
        var filtered = ko.utils.arrayFilter(this.inches(),
            function (item) {

                if (parseInt(item.Cat()) <= 8)
                    return true;
            });

        this.filteredInches(filtered);
    } else {
        this.filteredInches(this.inches());
    }
}

这告诉我,我们从json检索的对象没有被映射到MyListModel对象。我相信MyListModel类本身很好。

我认为问题源于首先得到英寸的代码:

    refreshInches() {
        this.DataService.getInches().done(entities => {
            this.inches(entities);
        });
    }

然后getInches()方法如下:

    getInches(): Q.Promise<Array<Models.MyListModel>> {
        return Q($.getJSON(this._baseUrl + 'GetInches'));
    }

我认为此代码的最初意图是从端点异步获取英寸数据并将json数据转换为MyListModel对象。正如我之前所说,我对Q.Promise不够熟悉,不知道getInches()方法有什么问题。虽然它只是从JSON数据中返回一组匿名对象,但我很清楚。

作为参考,从端点返回的json对象如下所示:

[{"text":"0","value":"0"},{"text":"1","value":"1"},...]

任何人都知道如何改进getInches()方法以执行它应该做的事情?

1 个答案:

答案 0 :(得分:2)

根据您的代码,getInches应该返回Q.Promise<Array<Models.MyListModel>>,即对MyListModel个对象数组的承诺。

但是,目前的形式是:

getInches(): Q.Promise<Array<Models.MyListModel>> {
    return Q($.getJSON(this._baseUrl + 'GetInches'));
}

它没有那样做。上面的内容返回<whatever the server gives you>的承诺,在本例中显然是一个普通对象数组。

要更改它以使其与假定的类型匹配,我们可以

getInches(): Q.Promise<Array<Models.MyListModel>> {
    var jqXhr = $.getJSON(this._baseUrl + 'GetInches');

    return Q(jqXhr).then(data => {
        return data.map(item => {
            return new Models.MyListModel(item);
        });
    });

    // or, if you must
    return Q(jqXhr).then(data => data.map(item => new Models.MyListModel(item)));
}

现在我们可以将新创建​​的MyListModel实例存储在一个observable中:

refreshInches() {
    this.DataService.getInches().done(this.inches);
}

注意this.inches是一个可观察的,可观察的是函数,它允许我们直接将它用作回调。

用作promise处理程序的函数将接收承诺的值作为其第一个参数。一个observable将存储你调用它的第一个参数的值,所以这非常适合。

此外,您的filteredInches方法过于复杂。而不是使它成为您需要调用的函数,而是使其成为依赖于viewmodel中某些可观察对象的计算。这样就永远不会与其他人不一致。

this.filteredInches = ko.pureComputed(() => {
    var value = this.value();

    return ko.utils.arrayFilter(this.inches(), item => {
        value != 6 || +item.Text() <= 8;
    });
});

查看您的viewmodel创建,您可能希望在某处具有数字可观察(或计算),以便您不需要记住在计算中键入转换Text属性。 / p>

相关问题