Отображение глубоко иерархических объектов в пользовательские классы с использованием подключаемого модуля сопоставления нокаутов

Используя подключаемый модуль сопоставления нокаутов ( http://knockoutjs.com/documentation/plugins-mapping.html ), вы можете сопоставить глубоко иерархический объект?

Если у меня есть объект с несколькими уровнями:

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

                }
            ]
        }
    ]
}

Как мне сопоставить его с моими пользовательскими классами в 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);
}

Может ли плагин сопоставления рекурсивно отобразить эти данные в иерархию моих настраиваемых объектов ?

24
задан Mark Robinson 21 September 2011 в 11:41
поделиться

3 ответа

Примерно так ( Живая копия на 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);​

Этот пример просто отображает бесконечно вложенный набор данных JSON, и я могу сказать, что на самом деле этот точный код используется в приложении, которое прекрасно работает.

Некоторые дополнительные функции, такие как

selectBackNode и selectParentNode

, позволяют вам вернуться обратно вверх по дереву.

Во время навигации по примеру родительская метка становится ссылкой, позволяющей подняться на один уровень вверх, а некоторые конечные узлы имеют кнопку «Назад», которая позволяет им перемещаться вверх по дереву на заданное количество уровней.

- РЕДАКТИРОВАТЬ -

Если у ваших конечных узлов нет дочернего массива, вы можете столкнуться с проблемой, когда вводятся дополнительные данные, которых нет в модели.

17
ответ дан 29 November 2019 в 00:16
поделиться

Если вам не нужны вложенные mappingOptions (создание объекта карты ko для каждого уровня узла), вы можете воспользоваться тем фактом, что параметры отображения ko для create дают вам доступ к родительскому объекту. Примерно так:

function Folder(parent,data) {
    var self = this;
    self.parent = parent;
    ko.mapping.fromJS(data, self.map, self);
}

Folder.prototype.map = {
    'folders': {
        create: function(options) {
            var folder = new Folder(options.parent,options.data);
            return folder;
        }
    }
}

var data = { name:"root", folders: [ {name:"child", folders: [] } ] };
var root = new Folder(null, data);

Таким образом, у вас есть только 1 копия карты в прототипе вашего класса (или это может быть любая функция). Если вы хотите, чтобы Folder.parent также был наблюдаемым, вы можете сделать data.parent = parent; внутри функции карты и не передавать в качестве параметра конструктору папки, или сделать это внутри конструктора папки вместо self.parent = parent;

1
ответ дан 29 November 2019 в 00:16
поделиться

По своему опыту я бы сказал, что у него не должно быть никаких проблем.

Я хотел бы использовать следующую строку -

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

Затем установить точку останова на следующей строке при просмотре сгенерированного объекта в вашем отладчике (лучше всего работает chrome или FF + Firebug). Таким образом, вы узнаете, сгенерирует ли ko.mapping модель представления, соответствующую вашим потребностям.

Обычно он генерирует объект, в котором только конечные точки (переменные со значениями) являются ko.observables. Любое другое время данных, которое вы можете использовать для навигации по данным, например ... children: [..., отображается как обычные объекты javaScript.

4
ответ дан 29 November 2019 в 00:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: