将分层JSON映射到TypeScript-KnockoutJS类型的Object

DIG*_*IDY 7 knockout-mapping-plugin knockout-2.0 knockout-mvc typescript

让我们先提前感谢:)

好的,所以我试图使用knockout.mapping插件从匹配的JSON数据加载/映射分层的TypeScript/KnockoutJS类型的类,层次结构可以是N度.

我知道我可以执行以下操作来从JSON数据映射/加载顶级类.

var qry = ko.mapping.fromJS(jsData, {}, new Query());
Run Code Online (Sandbox Code Playgroud)

但是我无法弄清楚如何将复杂的,N度,分层JSON数据映射/加载到一组TypeScript/KnockoutJS类并构建父/子关系.

我读过无数的艺术品,但是除了简单的父/子示例之外,它们在层次关系方面都不尽如人意,而且我无法使用knockout.mapping插件找到它们.

以下是我希望映射/加载的TypeScript类的减少定义.我是一个c ++/c#开发人员,所以这种性质的JavaScript对我来说是一个新手.

TypeScript对象

module ViewModel
{
    export class QueryModuleViewModel {
        public QueryObj: KnockoutObservable<Query>;

        constructor() {
            this.QueryObj = ko.observable<Query>();
        }

        public Initialize() {
            $.getJSON("/api/query/2", null,
                d => {
                    var qry = ko.mapping.fromJS(d, {}, new Query());
                    this.QueryObj(qry);
                });
        }
    }

    export class Query
    {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public RootTargetID: KnockoutObservable<number>;
        public RootTarget: KnockoutObservable<QueryTarget>;

        constructor()
        {
            this.ID = ko.observable<number>(0);
            this.Name = ko.observable<string>();
            this.RootTargetID = ko.observable<number>();
            this.RootTarget = ko.observable<QueryTarget>();
        }
    }

    export class QueryTarget
    {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public ParentID: KnockoutObservable<number>;
        public Children: KnockoutObservableArray<QueryTarget>;
        public Parent: KnockoutObservable<QueryTarget>;
        public Selects: KnockoutObservableArray<QuerySelect>;
        public FilterID: KnockoutObservable<number>;
        public Filter: KnockoutObservable<FilterClause>;

        constructor()
        {
            this.ID = ko.observable<number>(0);
            this.Name = ko.observable<string>();
            this.ParentID = ko.observable<number>(0);
            this.Children = ko.observableArray<QueryTarget>();
            this.Parent = ko.observable<QueryTarget>();
            this.Selects = ko.observableArray<QuerySelect>();
            this.FilterID = ko.observable<number>(0);
            this.Filter = ko.observable<FilterClause>();
        }
    }

    export class QuerySelect
    {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public Aggregation: KnockoutObservable<string>;
        public TargetID: KnockoutObservable<number>;
        public Target: KnockoutObservable<QueryTarget>;

        constructor()
        {
            this.ID = ko.observable<number>();
            this.Name = ko.observable<string>();
            this.Aggregation = ko.observable<string>();
            this.TargetID = ko.observable<number>();
            this.Target = ko.observable<QueryTarget>();
        }
    }

    export class FilterClause
    {
        public FilterClauseID: KnockoutObservable<number>;
        public Type: KnockoutObservable<string>;
        public Left: KnockoutObservable<string>;
        public Right: KnockoutObservable<string>;
        public ParentID: KnockoutObservable<number>;
        public Parent: KnockoutObservable<FilterClause>;
        public Children: KnockoutObservableArray<FilterClause>;
        public QueryTargets: KnockoutObservableArray<QueryTarget>;

        constructor()
        {
            this.FilterClauseID = ko.observable<number>();
            this.Type = ko.observable<string>();
            this.Left = ko.observable<string>();
            this.Right = ko.observable<string>();
            this.ParentID = ko.observable<number>();
            this.Parent = ko.observable<FilterClause>();
            this.Children = ko.observableArray<FilterClause>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

JSON看起来像这样:

{
    "ID": 2,
    "Name": "Northwind 2",
    "RootTargetID": 2,
    "RootTarget": {
        "ID": 2,
        "Name": "Customers",
        "ParentID": null,
        "FilterID": 2,
        "Queries": [],
        "Children": [],
        "Parent": null,
        "Selects": [
            {
                "ID": 3,
                "Name": "CompanyName",
                "Aggregation": "None",
                "TargetID": 2,
                "Target": null
            },
            {
                "ID": 4,
                "Name": "ContactName",
                "Aggregation": "None",
                "TargetID": 2,
                "Target": null
            }
        ],
        "Filter": {
            "FilterClauseID": 2,
            "Type": "AND",
            "Left": null,
            "Right": null,
            "ParentID": null,
            "QueryTargets": [],
            "Parent": null,
            "Children": [
                {
                    "FilterClauseID": 3,
                    "Type": "NE",
                    "Left": "Country",
                    "Right": "Germany",
                    "ParentID": 2,
                    "QueryTargets": [],
                    "Parent": null,
                    "Children": []
                },
                {
                    "FilterClauseID": 4,
                    "Type": "NE",
                    "Left": "Country",
                    "Right": "Mexico",
                    "ParentID": 2,
                    "QueryTargets": [],
                    "Parent": null,
                    "Children": []
                }
            ]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

DIG*_*IDY 6

好吧,经过大量的头发拉动和数字测试后,我现在更进一步了.

下面是我正在尝试实现的几乎可行的示例,唯一的问题是它似乎没有正确映射,即使单步执行代码似乎表明它正确加载.只有当我将它与我的绑定一起使用时,它才会在RootTaget.Filter.Type上抛出一个null未引用的绑定,该绑定应该填充一个值.

我仍在试图找出原因,但我会欢迎有关可能出现的错误的建议.:)

现在固定和工作

半工作打字稿

///<reference path="Scripts/typings/jquery/jquery.d.ts"/>
///<reference path="Scripts/typings/knockout/knockout.d.ts"/>
///<reference path="Scripts/typings/knockout.mapping/knockout.mapping.d.ts"/>

module ViewModel
{
    export class Query {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public RootTargetID: KnockoutObservable<number>;
        public RootTarget: KnockoutObservable<QueryTarget>;

        constructor(json: any) {
            this.ID = ko.observable<number>(0);
            this.Name = ko.observable<string>();
            this.RootTargetID = ko.observable<number>();
            this.RootTarget = ko.observable<QueryTarget>();

            var mapping = {
                'RootTarget': {
                    create: function (args) {
                        return new QueryTarget(args.data, null);
                    }
                }
            };

            ko.mapping.fromJS(json, mapping, this);

        }
    }

    export class QueryTarget {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public ParentID: KnockoutObservable<number>;
        public Children: KnockoutObservableArray<QueryTarget>;
        public Parent: KnockoutObservable<QueryTarget>;
        public Selects: KnockoutObservableArray<QuerySelect>;
        public FilterID: KnockoutObservable<number>;
        public Filter: KnockoutObservable<FilterClause>;

        constructor(json: any, parent: QueryTarget) {
            this.ID = ko.observable<number>(0);
            this.Name = ko.observable<string>();
            this.ParentID = ko.observable<number>(0);
            this.Children = ko.observableArray<QueryTarget>();
            this.Parent = ko.observable<QueryTarget>(parent);
            this.Selects = ko.observableArray<QuerySelect>();
            this.FilterID = ko.observable<number>(0);
            this.Filter = ko.observable<FilterClause>();

            var mapping = {
                'Children': {
                    create: function (args) {
                        return new QueryTarget(args.data, this);
                    }
                },
                'Selects': {
                    create: function (args) {
                        return new QuerySelect(args.data, this);
                    }
                },
                'Filter': {
                    create: function (args) {
                        return new FilterClause(args.data, null);
                    }
                }
            };

            ko.mapping.fromJS(json, mapping, this);
        }
    }

    export class QuerySelect {
        public ID: KnockoutObservable<number>;
        public Name: KnockoutObservable<string>;
        public Aggregation: KnockoutObservable<string>;
        public TargetID: KnockoutObservable<number>;
        public Target: KnockoutObservable<QueryTarget>;

        constructor(json: any, parent: QueryTarget) {
            this.ID = ko.observable<number>();
            this.Name = ko.observable<string>();
            this.Aggregation = ko.observable<string>();
            this.TargetID = ko.observable<number>();
            this.Target = ko.observable<QueryTarget>(parent);

            ko.mapping.fromJS(json, {}, this);
        }
    }

    export class FilterClause {
        public FilterClauseID: KnockoutObservable<number>;
        public Type: KnockoutObservable<string>;
        public Left: KnockoutObservable<string>;
        public Right: KnockoutObservable<string>;
        public ParentID: KnockoutObservable<number>;
        public Parent: KnockoutObservable<FilterClause>;
        public Children: KnockoutObservableArray<FilterClause>;

        constructor(json: any, parent: FilterClause) {
            this.FilterClauseID = ko.observable<number>();
            this.Type = ko.observable<string>();
            this.Left = ko.observable<string>();
            this.Right = ko.observable<string>();
            this.ParentID = ko.observable<number>();
            this.Parent = ko.observable<FilterClause>(parent);
            this.Children = ko.observableArray<FilterClause>();

            var mapping = {
                'Children': {
                    create: function (args) {
                        return new FilterClause(args.data, this);
                    }
                }
            };

            ko.mapping.fromJS(json, mapping, this);
        }
    }

    export class QueryModuleViewModel
    {
        public QueryObj: Query;

        constructor() {

            var json = {
                "ID": 2,
                "Name": "Northwind 2",
                "RootTargetID": 2,
                "RootTarget": {
                    "ID": 2,
                    "Name": "Customers",
                    "ParentID": null,
                    "FilterID": 2,
                    "Queries": [],
                    "Children": [],
                    "Parent": null,
                    "Selects": [
                        {
                            "ID": 3,
                            "Name": "CompanyName",
                            "Aggregation": "None",
                            "TargetID": 2,
                            "Target": null
                        },
                        {
                            "ID": 4,
                            "Name": "ContactName",
                            "Aggregation": "None",
                            "TargetID": 2,
                            "Target": null
                        }
                    ],
                    "Filter": {
                        "FilterClauseID": 2,
                        "Type": "AND",
                        "Left": null,
                        "Right": null,
                        "ParentID": null,
                        "QueryTargets": [],
                        "Parent": null,
                        "Children": [
                            {
                                "FilterClauseID": 3,
                                "Type": "NE",
                                "Left": "Country",
                                "Right": "Germany",
                                "ParentID": 2,
                                "QueryTargets": [],
                                "Parent": null,
                                "Children": []
                            },
                            {
                                "FilterClauseID": 4,
                                "Type": "NE",
                                "Left": "Country",
                                "Right": "Mexico",
                                "ParentID": 2,
                                "QueryTargets": [],
                                "Parent": null,
                                "Children": []
                            }
                        ]
                    }
                }
            }

            //$.getJSON("/api/query/2", null,
            //    d => {
            //        this.QueryObj = new Query(d);
            //    })

            this.QueryObj = new Query(json);
        }
    }
}

window.onload = () => {
    ko.applyBindings(new ViewModel.QueryModuleViewModel());
};
Run Code Online (Sandbox Code Playgroud)

html绑定测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>TypeScript Knockout Mapping Query Test</title>
    <link rel="stylesheet" href="app.css" type="text/css" />

    <script src="Scripts/jquery-2.0.2.js" type="text/javascript"></script>
    <script src="Scripts/knockout-2.2.1.debug.js" type="text/javascript"></script>
    <script src="Scripts/knockout.mapping-latest.debug.js" type="text/javascript"></script>
    <script src="query.js"></script>
    <!--<script src="my_js_query_test_all.js"></script>-->

</head>
<body>
    <h1>TypeScript Knockout Mapping Query Test</h1>
    <div data-bind="with: QueryObj">
        <span data-bind="blah: console.log($context)"></span>

        <p>Query Name: <input data-bind="value: Name" /></p>

        <hr />
        <p>Quick test of RootTarget and Filter data</p>
        <p>RootTarget.ID: <input data-bind="value: RootTarget().ID" /></p>
        <p>RootTarget.Name: <input data-bind="value: RootTarget().Name" /></p>

        <p>TYPE: <input data-bind="value: RootTarget().Filter().Type" /></p>

        <hr />
        <p>RootTarget.FilterClause Hierarcy</p>
        <div data-bind="with: RootTarget().Filter">
            <div data-bind="template: { name: 'QueryListClauseTemplate' }"></div>
        </div>

        <hr />
        <p>RootTarget.Selects</p>
        <div data-bind="foreach: { data: RootTarget().Selects }">
            <div data-bind="template: { name: 'QueryListSelectsTemplate' }"></div>
        </div>

    </div>

    <script type="text/template" id="QueryListClauseTemplate">

        <a title="FilterClause.Type" href="#" data-bind="text: Type" />

        <div data-bind="foreach: { data: Children }">
            <div data-bind="template: { name: 'QueryListClauseTemplate' }"></div>
        </div>
    </script>

    <script type="text/template" id="QueryListSelectsTemplate">
        <a title="Select.Name" href="#" data-bind="text: Name" />
    </script>

</body>
</html>
Run Code Online (Sandbox Code Playgroud)