问题
full jsFiddle Example
From what I found out on my last question about $.tmpl, passing an array of strings into a template containing an {{each}}
will result in the template going over each string in the array like an array of characters because tmpl
implicitly loops over arrays already. Unfortunately, that means, if I am already in a given template and I recursively call that template again on an array of strings (sub-objects work fine), I skip a level of recursion and try to template each string in the array instead of the templating the array itself.
If I am recursively templating an object by {{each}} (reflection, basically), how do I keep that implicit array loop from happening?
HTML
<div class="results"></div>
<script id="reflectTemplate" type="text/x-jquery-tmpl">
<ul>
{{each(i, prop) $data}}
{{if $data.hasOwnProperty(i)}}
<li>
${i}:
{{if $item.shouldDigDeeper(prop)}}
{{tmpl(prop, { shouldDigDeeper: $item.shouldDigDeeper, formatDisplay: $item.formatDisplay }) "#reflectTemplate"}}
{{else}}
${$item.formatDisplay(prop)}
{{/if}}
</li>
{{/if}}
{{/each}}
</ul>
</script>
JavaScript
var data = {
test2: "abc",
test3: [ "abc", "123", "def", "456" ]
},
templateFunctions = {
shouldDigDeeper: function(itemToCheck) {
return null !== itemToCheck && "object" === typeof(itemToCheck);
},
formatDisplay: function(propertyValue) {
var result = propertyValue;
if (null === result) {
result = "null";
}
else if ("string" === typeof (propertyValue)) {
result = "\"" + result + "\"";
}
return result;
}
};
$("#reflectTemplate").tmpl(data, templateFunctions).appendTo($(".results"));
Actual Output
<ul>
<li>test2: "abc" </li>
<li>test3:
<ul>
<li>0: "a" </li>
<li>1: "b" </li>
<li>2: "c" </li>
</ul>
...
<ul>
<li>0: "4" </li>
<li>1: "5" </li>
<li>2: "6" </li>
</ul>
</li>
</ul>
Desired Output
<ul>
<li>test2: "abc" </li>
<li>test3:
<ul>
<li>0: "abc" </li>
...
<li>1: "456" </li>
</ul>
</li>
</ul>
回答1:
In case @mblase75 is right (read: it isn't possible) and no one else comes up with anything else, here is a workaround I came up with.
jsFiddle example
Since tmpl
pre-jumps a level deeper into arrays, I just came up with a system of templates that bounce off each other as an array comes up. One template handles only array items, while allowing nested objects/arrays within it (it seems). The duplication is a bit rough, but it appears to do the job for even crazy nested arrays.
HTML
<div class="results"></div>
<script id="arrayDisplayTemplate" type="text/x-jquery-tmpl">
<li>
{{if null !== $data && "object" === typeof ($data)}}
{{if $data instanceof Array}}
[
<ul>
{{tmpl($data, { formatDisplay: $item.formatDisplay }) "#arrayItemTemplate"}}
</ul>
]
{{else}}
{{tmpl($data, { formatDisplay: $item.formatDisplay }) "#reflectTemplate"}}
{{/if}}
{{else}}
${$item.formatDisplay($data)}
{{/if}}
</li>
</script>
<script id="reflectTemplate" type="text/x-jquery-tmpl">
<ul>
{{each(i, prop) $data}}
{{if $data.hasOwnProperty(i)}}
<li>
${i}:
{{if null !== prop && "object" === typeof (prop)}}
{{if prop instanceof Array}}
[
<ul>
{{tmpl(prop, { formatDisplay: $item.formatDisplay }) "#arrayDisplayTemplate"}}
</ul>
]
{{else}}
{{tmpl(prop, { formatDisplay: $item.formatDisplay }) "#reflectTemplate"}}
{{/if}}
{{else}}
${$item.formatDisplay(prop)}
{{/if}}
</li>
{{/if}}
{{/each}}
</ul>
</script>
JavaScript
var data = {
test1: 123,
test2: { w: [ "some", "string", "array" ], x: 1, y: 2, z: "abc" },
test3: [ "abc", "123", "def", "456" ],
test4: null
},
templateFunctions = {
formatDisplay: function(propertyValue) {
var propertyType = typeof (propertyValue),
result = propertyValue;
if (null === result) {
result = "null";
}
else if ("string" === propertyType) {
result = "\"" + result + "\"";
}
return result;
}
};
$("#reflectTemplate").tmpl(data, templateFunctions).appendTo($(".results"));
来源:https://stackoverflow.com/questions/7436189/how-do-i-prevent-jquery-template-from-digging-too-deeply-on-a-string-array-in-a