Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

mvvm - Mapping deeply hierarchical objects to custom classes using knockout mapping plugin

Using the knockout mapping plugin ( http://knockoutjs.com/documentation/plugins-mapping.html ) can you map a deeply hierachical object?

If I have an object with multiple levels:

var data = {
    name: 'Graham',
    children: [
        {
            name: 'Son of Graham',
            children: [
                {
                    name: 'Son of Son of Graham',
                    children: [
                        {
                            ... and on and on....
                        }
                    ]

                }
            ]
        }
    ]
}

How do I map it to my custom classes in javascript:

var mapping = {
    !! your genius solution goes here !!

    !! need to create a myCustomPerson object for Graham which has a child myCustomerPerson object 
    !! containing "Son of Graham" and that child object contains a child myCustomerPerson 
    !! object containing "Son of Son of Graham" and on and on....

}

var grahamModel = ko.mapping.fromJS(data, mapping);

function myCustomPerson(name, children)
{
     this.Name = ko.observable(name);
     this.Children = ko.observableArray(children);
}

Can the mapping plugin recursively map this data into an hierachy of my custom objects?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Something like this (Live copy on js fiddle):

CSS:

.left {
    float: left;
}

.clear {
    clear: both;
}?

HTML:

<p>Current:&nbsp;
    <a href="#" data-bind="visible: (stack.length > 0), text: selectedNode().name, click: selectParentNode"></a>
    <span data-bind="visible: (stack.length <= 0), text: selectedNode().name"></span>
</p>
<p class="left">Children:&nbsp;</p>
<ul class="left" data-bind="template: {name: 'childList', foreach: selectedNode().children}"></ul>

<script type="text/html" id="childList">
    <li data-bind="click: function(){nodeViewModel.selectChildNode($data)}">
        <a href="#">A${name}</a>
    </li>
</script>

<br /><br />
<ul class="clear" data-bind="template: {name: 'backBtn'}"></ul>

<script type="text/html" id="backBtn">
    <a href="#" data-bind="visible: $data.selectedNode().back, click: function() { nodeViewModel.selectBackNode($data.selectedNode().back) }">Back</a>
</script>?

JavaScript:

var node = function(config, parent) {
    this.parent = parent;
    var _this = this;

    var mappingOptions = {
        children: {
            create: function(args) {
                return new node(args.data, _this);
            }
        }
    };

    ko.mapping.fromJS(config, mappingOptions, this);
};

var myModel = {
    node: {
        name: "Root",
        children: [
            {
            name: "Child 1",
            back: 1,
            children: [
                {
                name: "Child 1_1",
                back: 1,
                children: [
                    {
                    name: "Child 1_1_1",
                    back: 4,
                    children: [
                        ]},
                {
                    name: "Child 1_1_2",
                    back: 2,
                    children: [
                        ]},
                {
                    name: "Child 1_1_3",
                    back: 1,
                    children: [
                        ]}
                    ]}
            ]},
        {
            name: "Child 2",
            back: 1,
            children: [
                {
                name: "Child 2_1",
                back: 1,
                children: [
                    ]},
            {
                name: "Child 2_2",
                back: 1,
                children: [
                    ]}
            ]}
        ]
    }
};

var viewModel = {

    nodeData: new node(myModel.node, undefined),

    selectedNode: ko.observable(myModel.node),

    stack: [],

    selectBackNode: function(numBack) {

        if (this.stack.length >= numBack) {
            for (var i = 0; i < numBack - 1; i++) {
                this.stack.pop();
            }
        }
        else {
            for (var i = 0; i < this.stack.length; i++) {
                this.stack.pop();
            }
        }

        this.selectNode( this.stack.pop() );
    },

    selectParentNode: function() {
        if (this.stack.length > 0) {
            this.selectNode( this.stack.pop() );
        }
    },

    selectChildNode: function(node) {
        this.stack.push(this.selectedNode());
        this.selectNode(node);
    },

    selectNode: function(node) {
        this.selectedNode(node);
    }

};

window.nodeViewModel = viewModel;
ko.applyBindings(viewModel);?

This sample just maps an infinitely nested set of JSON data, and I can say from actually using this exact code in application that is works great.

Some of the extra functions like

selectBackNode and selectParentNode

allow you to move back up the tree.

While navigating the example the parent label becomes a link to allow for going up one level, and some of the leaf nodes have a back button that allows them to move back up the tree by a given number of levels.

--EDIT--

If your leaf nodes don't have a children array you might get a problem where additional data is introduced that doesn't exist in the model.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...