Mapowanie głęboko hierarchicznych obiektów do klas niestandardowych za pomocą wtyczki mapowania knockout

Korzystanie z wtyczki mapowania knockout ( http://knockoutjs.com/documentation/plugins-mapping.html ) Czy potrafisz odwzorować obiekt głęboko hierachiczny?

Jeśli mam obiekt z wieloma poziomami:

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

                }
            ]
        }
    ]
}

Jak mapować to do moich niestandardowych klas w 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);
}

Czy wtyczka mapująca może rekurencyjnie mapować te dane na hierachy moich niestandardowych obiektów?

Author: Mark Robinson, 2011-09-21

4 answers

Coś takiego (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);​

Ta próbka po prostu odwzorowuje nieskończenie zagnieżdżony zestaw danych JSON, i mogę powiedzieć, że faktycznie używając tego dokładnego kodu w aplikacji, która działa świetnie.

Niektóre z dodatkowych funkcji jak

SelectBackNode i selectParentNode

/ Align = "left" /

Podczas poruszania się po przykładzie Etykieta rodzica staje się łączem pozwalającym na Przejście W górę o jeden poziom, a niektóre z węzłów liści mają przycisk Wstecz, który pozwala im poruszać się z powrotem w górę drzewa o określoną liczbę poziomów.

--EDIT --

Jeśli twoje węzły liściowe nie mają tablicy dzieci, może wystąpić problem z wprowadzaniem dodatkowych danych, które nie istnieją w modelu.

 17
Author: HJ05,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-28 21:24:10

Z mojego doświadczenia, powiedziałbym, że nie powinno mieć żadnych problemów.

Użyłbym następującej linijki -

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

Następnie ustaw punkt przerwania w następnej linii na spójrz na wygenerowany obiekt w debuggerze (chrome lub FF+Firebug działa najlepiej). W ten sposób dowiesz się, czy ko.mapowanie wygeneruje model widoku odpowiadający Twoim potrzebom.

Normalnie generuje obiekt, w którym tylko punkty końcowe (zmienne z wartościami) są ko.obserwatorzy. Każdy z pozostałych danych razy, że możesz użyć do nawigacji po danych, jak {[1] } są wyświetlane jako zwykłe obiekty javaScript.

 4
Author: photo_tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 17:21:13

Jeśli nie chcesz zagnieżdżać mappingOptions (tworzenia obiektu mapy ko dla każdego poziomu węzła), możesz skorzystać z faktu, że opcje mapowania ko dla create dają Ci dostęp do obiektu nadrzędnego. Coś takiego:

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);

W ten sposób Masz tylko 1 kopię mapy, w prototypie klasy (lub może być dowolną funkcją). Jeśli chcesz Folder.rodzica, aby być obserwowalnym, możesz zrobić data.parent = parent; wewnątrz funkcji mapy i nie przekazać jako parametr do konstruktora folderów, lub zrób to wewnątrz konstruktora folderów zamiast self.parent = parent;

 1
Author: eselk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-09 20:27:42

Użyłem podejścia w tej odpowiedzi , Aby utworzyć hierarchię pól wyboru, w której węzły z dziećmi są zwijane, a gdy zaznaczasz / odznaczasz rodzica, jego potomkowie są sprawdzani / odznaczani.

Model Widoku

var Category = function(data, parent) {
    var self = this;
    self.name = data.name;
    self.id = data.id;
    self.parent = parent;
    self.categoryChecked = ko.observable(false);
    ko.mapping.fromJS(data, self.map, self);
};

// This will add a "map" to our category view model
Category.prototype.map = {
    'sub_categories' : {
        create: function(options){
            var category = new Category(options.data, options.parent);
            category.parent.categoryChecked.subscribe(function(value){
                category.categoryChecked(value);
            });
            return category;
        }
    }
};  

HTML (view)

    <div data-role="panel" id="left-panel" data-position="left" data-position-fixed="false" data-theme="b">
            <div data-role="collapsible-set" data-bind="template: {name: 'category_collapsible', foreach: sub_categories}" data-mini="true" id="categories" data-iscroll> </div>
        </div><!-- END left panel -->

        <script type="text/html" id="category_collapsible">
            <div class="category_collapsible" data-mini="true" data-content-theme="b" data-inset="true" data-iconpos="right">
                <h3>     
                    <input data-role="none" data-them="b" data-bind='checked: categoryChecked, jqmChecked: true, attr: {id: "category_checkbox_"+id}' class="chk_category" type="checkbox" />
                    <label data-bind='attr: {for: "category_checkbox_"+id}'><span data-bind="text: name"> </span></label>
                </h3>
                <ul data-role="listview" data-bind="template: {name: 'category_list', foreach: sub_categories}">

                </ul>
            </div>
        </script><!-- END category_collapsible template -->

        <script type="text/html" id="category_list">
            <!-- ko if: sub_categories().length==0 -->
                <li data-theme="c">
                    <input data-role="none" data-theme="c" data-bind='checked: categoryChecked, jqmChecked: true, attr: {id: "category_checkbox_"+id}' class="chk_category" type="checkbox"/>
                    <label data-corners="false" data-bind='attr: {for: "category_checkbox_"+id}'>
                        <span data-bind="text: name"> </span>
                    </label>        
                </li>
            <!-- /ko -->
            <!-- ko if: sub_categories().length>0 -->
                <li data-theme="c" data-bind="template: {name: 'category_collapsible', data: $data}"></li>
            <!-- /ko -->
        </script>
 1
Author: hisa_py,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:16:58