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?
4 answers
Coś takiego (Live copy on js fiddle):
CSS:
.left {
float: left;
}
.clear {
clear: both;
}
HTML:
<p>Current:
<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: </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.
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.
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;
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>
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