gpt4 book ai didi

javascript - 递归构建器嵌套问题

转载 作者:行者123 更新时间:2023-11-30 06:32:32 24 4
gpt4 key购买 nike

我正在尝试将 JSON/JavaScript 对象解析为 HTML...大部分工作正常,但我无法弄清楚我的递归构建方法 (App.Layout.Form.DocumentDirector)...

它会在不应该的时候继续嵌套(追加)元素。请参阅 http://jsfiddle.net/blogshop/DSxft/6/ 中的示例和代码

有人能帮我弄清楚吗?构建方法位于第 408 行(在 fiddle 中——向下滚动 DocumentDirector 一点点以在此处看到它)。谢谢!


构建方法

build: function (obj, level) {
var that = this,
builder = this.getBuilder(),
iterator = Object.create(App.Utilities.RecursiveIterator(obj)),
level = level || 0,
prev = this._prevLevel || 0,
next = level,
key,
current,
attributes,
pair,
children,
hasType = false,
maxNesting = false;

do {
// Get the current node and build it
key = iterator.key();
current = iterator.current();

console.log(iterator.key() + ": " + iterator.current().toSource());

if (current.hasOwnProperty('type') && current.type !== '') {

if (current.hasOwnProperty('type') && current.type !== '') {
//console.log(iterator.current().type);

attributes = {};

$.each(current, function(key, value) {
if (that._validAttributes.indexOf(key) !== -1) {
attributes[key] = value;
}
});
//console.log(attributes);
}

//console.log("Prev: " + prev);
console.log("Level: " + level);

if (level == prev && level == 0) {
builder.append(current.type, attributes);
} else if (level == prev && level > 0) {
builder.add(current.type, attributes);
} else if (level < prev && level > 0) {
builder.parent().append(current.type, attributes);
} else if (level > prev && level > 0) {
builder.append(current.type, attributes);
} else {
// Do nothing
}

if (current.hasOwnProperty('label')) {
var label = builder.addBefore(builder.getCurrent(), 'label');
builder.text(current.label, label);
}
}

// Are there child nodes? If so, recurse...
if (iterator.hasChildren()) {
children = iterator.getChildren();

if (this.isCollection(key, current)) {
//console.log("I am a container");
}


//console.log("I have children");
console.log("-----------------------------");

hasType = (current.hasOwnProperty('type') && current.type !== '') ? current.type : false;

if (hasType) {
maxNesting = (this.isVoid(current.type)) ? true : false;
}

if (maxNesting === false) {
next = level;

if (hasType) {
this._prevLevel = level;
next = level + 1;
}

this.build(children, next);
}

} else {
if (current.hasOwnProperty('type') && current.type !== '') {
//console.log("I am childless");
console.log("-----------------------------");
}
}
iterator.next();
} while (iterator.hasNext());

if (current && current.hasOwnProperty('type') && current.type !== '') {

if (iterator.hasNext() === false) {
//console.log("<----- MOVING UP " + parseInt(level - prev) + " LEVELS ----->");
//console.log("Diff: " + parseInt(level - prev));
//console.log("Level: " + level);
//console.log("Prev: " + prev);

builder.parent();
this._prevLevel = level - prev;
}
}
},

附加信息

DocumentDirector: function (builder) {
var director = Object.create({
_builder: {},
_validCollection: ['sections', 'forms', 'fieldsets', 'rows', 'fields'],
_validAttributes: ['id', 'name'],
_voidElements: ['base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'img', 'input', 'link', 'meta', 'param', 'source'],
_inputElements: ['text', 'select', 'radio', 'checkbox', 'textarea', 'datepicker', 'yesno'],
_prevLevel: 0,

init: function (builder) {
builder = builder || '';
if (builder) this.setBuilder(builder);

return this;
},
setBuilder: function (builder) {
this._builder = builder;

return this;
},
getBuilder: function () {
return this._builder;
},
isCollection: function (key, node) {
// Collections MUST have a key, as they don't have a type
key = key || '';
if (key === '') return false;

return (node.constructor === Array && this._validCollection.indexOf(key) !== -1) ? true : false;
},
isVoid: function (type) {
var isVoidElement = false;

if (this._voidElements.indexOf(type.toString()) !== -1) {
isVoidElement = true;
}

if (this._inputElements.indexOf(type.toString()) !== -1) {
isVoidElement = true;
}

return isVoidElement;
},
build: function (obj, level) {
var that = this,
builder = this.getBuilder(),
iterator = Object.create(App.Utilities.RecursiveIterator(obj)),
level = level || 0,
prev = this._prevLevel || 0,
next = level,
key,
current,
attributes,
pair,
children,
hasType = false,
maxNesting = false;

do {
// Get the current node and build it
key = iterator.key();
current = iterator.current();

console.log(iterator.key() + ": " + iterator.current().toSource());

if (current.hasOwnProperty('type') && current.type !== '') {

if (current.hasOwnProperty('type') && current.type !== '') {
//console.log(iterator.current().type);

attributes = {};

$.each(current, function(key, value) {
if (that._validAttributes.indexOf(key) !== -1) {
attributes[key] = value;
}
});
//console.log(attributes);
}

//console.log("Prev: " + prev);
console.log("Level: " + level);

if (level == prev && level == 0) {
builder.append(current.type, attributes);
} else if (level == prev && level > 0) {
builder.add(current.type, attributes);
} else if (level < prev && level > 0) {
builder.parent().append(current.type, attributes);
} else if (level > prev && level > 0) {
builder.append(current.type, attributes);
} else {
// Do nothing
}

if (current.hasOwnProperty('label')) {
var label = builder.addBefore(builder.getCurrent(), 'label');
builder.text(current.label, label);
}
}

// Are there child nodes? If so, recurse...
if (iterator.hasChildren()) {
children = iterator.getChildren();

if (this.isCollection(key, current)) {
//console.log("I am a container");
}


//console.log("I have children");
console.log("-----------------------------");

hasType = (current.hasOwnProperty('type') && current.type !== '') ? current.type : false;

if (hasType) {
maxNesting = (this.isVoid(current.type)) ? true : false;
}

if (maxNesting === false) {
next = level;

if (hasType) {
this._prevLevel = level;
next = level + 1;
}

this.build(children, next);
}

} else {
if (current.hasOwnProperty('type') && current.type !== '') {
//console.log("I am childless");
console.log("-----------------------------");
}
}
iterator.next();
} while (iterator.hasNext());

if (current && current.hasOwnProperty('type') && current.type !== '') {

if (iterator.hasNext() === false) {
//console.log("<----- MOVING UP " + parseInt(level - prev) + " LEVELS ----->");
//console.log("Diff: " + parseInt(level - prev));
//console.log("Level: " + level);
//console.log("Prev: " + prev);

builder.parent();
this._prevLevel = level - prev;
}
}
},
getDocument: function () {
return this.getBuilder().getDocument();
}
});

return director.init(builder);
}

DOMBuilder(注入(inject)到 DocumentDirector——处理 DOM 操作):

DOMBuilder: function () {
var domBuilder = Object.create({
_document: {},
_rootNode: {},
_currentNode: {},

init: function (viewModel, domBuilder) {
var doc, rootNode;

this._document = doc = document.createDocumentFragment();
this._rootNode = rootNode = this.appendNode(doc, 'section');
this._currentNode = rootNode;

return this;
},

/* Generic methods
------------------ */

/**
* Returns the document
*
* @return DOM Node: The DOM document fragment
*/
getDocument: function () {
return this._document;
},
/**
* Returns the root node
*
* @return DOM Node: The root node
*/
getRoot: function () {
return this._rootNode;
},
/**
* Returns the current node
*
* @return DOM Node: The current node
*/
getCurrent: function () {
return this._currentNode;
},
/**
* Sets the current node
*
* @return DOM Node: The current node
*/
setCurrent: function (node) {
this._currentNode = node;

return this;
},
/**
* Returns the parent of the current node
*
* @return DOM Node: The parent node
*/
getParent: function () {
return this._currentNode.parentNode;
},
/**
* Creates and appends a node inside a specified parent
*
* @ref DOM Node: The insertion target for the new node
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
*
* @return DOM Node: The newly created node
*/
appendNode: function (ref, type, attributes) {
var node = document.createElement(type);
ref.appendChild(node);
//this._currentNode = node;

return node;
},
/**
* Creates a node and inserts it before the specified element
*
* @ref DOM Node: A reference node for inserting the new node
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
*
* @return DOM Node: The newly created node
*/
addBefore: function (ref, type, attributes) {
var node = document.createElement(type);
ref.parentNode.insertBefore(node, ref);
//this._currentNode = node;

return node;
},
/**
* Creates a node and inserts it after the specified element
*
* @parent DOM Node: A reference node for inserting the new node
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
*
* @return DOM Node: The newly created node
*/
addAfter: function (ref, type, attributes) {
var node = document.createElement(type);
ref.parentNode.insertBefore(node, ref.nextSibling);
//this._currentNode = node;

return node;
},

/* Chainable methods
---------------------- */

/**
* Creates and appends a node inside a specified parent
*
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
* @ref DOM Node: (Optional) The insertion target for the new node
*
* @return DOMBuilder: this
*/
append: function (type, attributes, ref) {
var parent, node;

ref = ref || this._currentNode;
node = document.createElement(type);
ref.appendChild(node);
this._currentNode = node;

if (attributes) {
// TODO: Use map instead
$.each(attributes, function (key, value) {
node.setAttribute(key, value);
});
}

return this;
},
/**
* Creates a node and inserts it after the specified element
*
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
* @ref DOM Node: A reference node for inserting the new node
*
* @return DOMBuilder: this
*/
add: function (type, attributes, ref) {
var ref, node;

ref = ref || this._currentNode;
node = document.createElement(type);
//console.log(ref);
ref.parentNode.insertBefore(node, ref.nextSibling);
this._currentNode = node;

if (attributes) {
// TODO: Use map instead
$.each(attributes, function (key, value) {
node.setAttribute(key, value);
});
}

return this;
},
/**
* Creates a node and inserts it before the specified element
*
* @type String: A valid HTML5 element type
* @attributes Object: And object containing key-value pairs of attributes and values
* @ref DOM Node: A reference node for inserting the new node
*
* @return DOMBuilder: this
*/
before: function (type, attributes, ref) {
var ref, node;

ref = ref || this._currentNode;
node = document.createElement(type);
ref.parentNode.insertBefore(node, ref);
this._currentNode = node;

if (attributes) {
// TODO: Use map instead
$.each(attributes, function (key, value) {
node.setAttribute(key, value);
});
}

return this;
},
/**
* Sets the internal current node reference to the parent of the current node
*
* @return DOMBuilder: this
*/
parent: function () {
var ref, node;

ref = ref || this._currentNode;
this._currentNode = this._currentNode.parentNode;

return this;
},
/**
* Sets the text for a specified node
*
* @return DOMBuilder: this
*/
text: function (value, ref) {
var node = document.createTextNode(value);
ref.appendChild(node);

return this;
}
});

return domBuilder.init();
}

Bootstrap :

var formLayout = [
{
type: 'section',
id: 'header',
},
{
type: 'div',
id: 'center-pane',
forms: [
{
type: 'section',
id: 'claim-history',
label: 'Add Claim History',
templates: [
{
fieldsets: [
{
type: 'fieldset',
id: 'claim-history-details',
label: 'Details',
rows: [
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_LossDate',
name: 'LossDate',
label: 'Date of Loss',
type: 'datepicker', // Types: any standard HTML5 form element or Kendo UI widget
className: 'small',
data: {
role: 'datepicker', // Redundant proxy for type parameter
bind: {
value: 'datePickerValue' // Bind Kendo UI Datepicker
}
}
}
]
},
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_TypeOfLoss',
name: 'TypeOfLoss',
label: 'Type of Loss',
type: 'select', // This could be a combobox, really
className: ''
}
]
},
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_AtFaultPercentage',
name: 'AtFaultPercentage',
label: 'At Fault %',
type: 'text',
className: 'tiny'
},
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_CauseOfLoss',
name: 'CauseOfLoss',
label: 'Cause of Loss',
type: 'select',
className: ''
}
]
},
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: '',
name: '',
label: 'Charges Laid',
type: 'yesno',
className: '',
data: {
bind: {
source: 'yesno',
value: 'datePickerValue' // Bind Kendo UI Datepicker
}
}
},
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_ChargesLaid',
name: 'ChargesLaid',
label: 'Details',
type: 'textarea',
className: 'tiny'
}
]
}
],
},
{
type: 'fieldset',
id: 'claim-history-amounts',
label: 'Amounts',
rows: [
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_SecA',
name: 'SecA',
label: 'Sec A',
type: 'yesno',
className: 'tiny'
},
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_SecATotal',
name: 'SecATotal',
label: 'Sec A Total',
type: 'text',
className: 'small'
}
]
},
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_SecC',
name: 'SecC',
label: 'Sec C',
type: 'text',
className: 'tiny'
},
]
},
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_IgnoreReason',
name: 'IgnoreReason',
label: 'Ignore Reason',
type: 'select',
className: ''
}
]
}
],
},
{
type: 'fieldset',
id: 'claim-history-vehicle-driver',
label: 'Vehicle and Driver',
rows: [
{
type: 'div',
fields: [
{
// Any standard html5 attributes can be defined
id: 'MainContent_DetailsContent_ClaimVehicle',
name: 'ClaimVehicle',
label: 'Claim Vehicle',
type: 'select',
className: ''
}
]
}
],
} // END fieldset
] // END fieldsets
} // END template
] // END templates
} // END form
] // END forms
},
{
type: 'div',
id: 'left-pane'
},
{
type: 'div',
id: 'right-pane'
}
]; // END sections
var formBuilder = Object.create(App.Layout.Form.DOMBuilder());
var formDirector = Object.create(App.Layout.Form.DocumentDirector(formBuilder));
formDirector.build(formLayout);
document.getElementById("test").appendChild(formDirector.getDocument());

最佳答案

解决了——对于每个连续的递归,我应该追加第一个子节点(传入对象的),然后添加剩余的节点。添加了一个 isFirst 变量来进行跟踪。

如果有人有兴趣的话,工作 fiddle ...... http://jsfiddle.net/blogshop/DSxft/

build: function (obj, level) {
var that = this,
builder = this.getBuilder(),
iterator = Object.create(App.Utilities.RecursiveIterator(obj)),
level,
key,
current,
attributes,
pair,
children,
isFirst = true,
hasType = false,
maxNesting = false;

do {
// Get the current node and build it
key = iterator.key();
current = iterator.current();

hasType = (current.hasOwnProperty('type') && current.type !== '') ? current.type : false;

if (hasType) {
maxNesting = (this.isVoid(current.type)) ? true : false;

attributes = {};

$.each(current, function(key, value) {
if (that._validAttributes.indexOf(key) !== -1) {
attributes[key] = value;
}
});

if (isFirst == true) {
builder.append(current.type, attributes);
isFirst = false;
} else {
builder.add(current.type, attributes)
}

// Prepend label to field
if (current.hasOwnProperty('label')) {
var label = builder.addBefore(builder.getCurrent(), 'label');
builder.text(current.label, label);
}
}

// Are there child nodes? If so, recurse...
if (iterator.hasChildren()) {
children = iterator.getChildren();

if (maxNesting === false) {
// Recurse
level = (hasType) ? level + 1 : level;
this.build(children, level);
}
}

// Move to the next node
iterator.next();
} while (iterator.hasNext());

if (current && current.hasOwnProperty('type') && current.type !== '') {
if (iterator.hasNext() === false) builder.parent();
}
},

关于javascript - 递归构建器嵌套问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16680369/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com