我们有一个包含许多问题的表单,其中一些表单仅基于与JSON中发送的其他问题相关的动态逻辑显示。
每个问题的逻辑都类似于,但可能比以下更复杂:
如果显示以下内容:(quest1 == value1 OR quest1 == value2) AND (quest2 == value3 OR quest3 == value4)
。
简化的JSON示例:
[
"AND",
{
"quest1": "value1,value2"
},
[
"OR",
{
"quest2": "value3"
},
{
"quest3": "value4"
}
]
]
当父问题值发生变化时,似乎确定子问题可见性的一种方法是将给定问题的JSON解析为如上所述的JS友好语法,包括实际当前值并使用eval()
。还有更好的选择吗?
谢谢!
答案 0 :(得分:1)
我不会讲一讲为什么不使用eval。永远不要使用它。
也许您可以代表这样的问题的显示逻辑。这实际上取决于你的逻辑有多复杂以及什么是有用的表示。
var display_logic = {"operator":"and","conditions":
[
{"operator":"or","conditions":
[
{"operator":"equals","input_id":"question1","value":"val1"},
{"operator":"equals","input_id":"question2","value":"val2"}
]
},{"operator":"equals","input_id":"question3","value":"val4"}
]
};
编辑:我实际上更喜欢你提供的格式,所以我会改用它。下面的代码使用递归计算函数来评估结构为true还是false。然后,您可以根据需要轻松扩展逻辑运算符。
var visibility_logic = [
"AND",
{
"quest1": "value1,value2"
},
[
"OR",
{
"quest2": "value3"
},
{
"quest3": "value4"
}
]
];
//Interface to however you are accessing the answers
var qa_interface_func = function(question) {
//E.g.
//return $("#" + question).val();
//For debugging, I will assume the answer is always "value3"
return "value3";
};
//Recursive function which returns true or false based on the visibility logic and the current answers
function evaluateVisibility(item, qa_interface_func) {
if (typeof item === "object") {
if (Array.isArray(item)) {
//Item is an array
//Assume index 0 is the operator and the rest are conditions
if (item.length < 3) {
console.err("Bad structure", item);
return false;
}
var operator = item[0];
if (operator === "AND") {
//All conditions need to be true to evaluate to true
for(var index=1; index<item.length; index++) {
if (!evaluateVisibility(item[index], qa_interface_func)) {
return false;
}
}
return true;
} else if (operator === "OR") {
//1 condition needs to be true to evaluate to true
for(var index=1; index<item.length; index++) {
if (evaluateVisibility(item[index], qa_interface_func)) {
return true;
}
}
return false;
} else {
console.err("Unknown operator", operator);
return false;
}
} else {
//Item is an object
//All mappings must be true to evaluate to true
for (var key in item) {
var accepted_values = item[key].split(",");
var current_value = qa_interface_func(key);
if (accepted_values.indexOf(current_value) === -1) {
return false;
}
}
return true;
}
} else {
console.err("Must be an object or an array", item);
}
}
if (evaluateVisibility(visibility_logic, qa_interface_func)) {
console.log("Yes, show this question");
} else {
console.log("No, hide this question");
}
上面的代码打印出&#34; No&#34;,但你可以说它&#34;是&#34;如果你摆弄逻辑阵列。
答案 1 :(得分:1)
我已经建立了这个确切的场景。我将问题和答案组装为树服务器端,然后通过Ajax调用将模型加载到页面中。每个Question对象都有问题文本和一个Answer对象数组,以及一个属性(最初为null)来保存任何选定的答案。每个Answer对象包含答案值和基于该答案的指向下一个Question的指针。我使用自引用的Knockout模板,在select中显示问题和答案列表。选择答案时,会触发显示下一个问题的if逻辑和可能的答案 - 如果:question.chosenAnswer()。
这里的关键是构建一个树并使用您正在使用的任何库或框架遍历它。
我现在正在打电话,当我有一个真正的键盘时会添加一些代码。
答案结构:{ answerId, answerValue, nextQuestion }
其中answerId是唯一的数字/ ID,用于标识树中唯一的路径以获得该答案。
问题树:
{
question: "Are there lots of questions?", // string
chosenAnswer: null, // Answer
answers: [
{
answerId: 1,
answerValue: "Yes",
nextQuestion: {
question: "How many answers can there be?",
chosenAnswer: null,
answers: [
{
answerId: 2,
answerValue: "1",
nextQuestion: null // leaf
},
{
answerId: 4,
answerValue: "2",
nextQuestion: {
question: "How do you decide?",
chosenAnswer: null,
answers: [
{
answerId: 5,
answerValue: "Guess",
nextQuestion: null // leaf
},
{
answerId: 6,
answerValue: "Think",
nextQuestion: null // leaf
},
]
}
},
]
}
},
{
answerId: 7,
answerValue: "No"
nextQuestion: null // leaf
},
]
}
你可以通过AJAX调用加载它,如果你使用jQuery for AJAX,你可以在.done(响应)中获得解析的JSON。如果没有,您可以使用JSON.parse()。不需要评估。我基于这种结构构建了我的knockout.js视图模型。
然后,只需使用您的框架(挖空,角度等)来根据所选答案管理增量显示。如何执行此操作取决于您的工具。
根据当前问题的答案数组填充我的选择下拉菜单。我在selectedAnswer属性上订阅(或观察),以便在选择叶子答案时(nextQuestion为null或未定义),我触发一个事件,该事件包括选择作为事件数据一部分的Answer对象。因此,我可以记录或回应最终选择的答案。
例如,这是我在knockout.js中使用的自引用模板:
<script id="qaTmpl" type="text/html">
<div class="col-md-12 clearfix leaders">
<div>
<label class="question pull-left">{{question}}</label>
<span>
<select class="pull-right" data-bind="disable: $root.editBuffer().valueId(),
options: answers, optionsText: 'answer', optionValue: 'answer', value: chosenAnswer, optionsCaption:'Choose...'"></select>
</span>
</div>
</div>
<!-- ko if: $data && chosenAnswer&& chosenAnswer() && chosenAnswer().nextQuestion -->
<!-- ko template: {name: 'qaTmpl', data: chosenAnswer().nextQuestion} --><!-- /ko -->
<!-- /ko -->
</script>
我希望你明白这个主意。我希望我可以更具体,但很多实现细节都特定于您的框架和模板引擎。
答案 2 :(得分:0)
最好将数据和代码分开,在这种情况下,这意味着值“val1”,“val2”等可以通过json引入,但比较应该在js文件中编码。这可以通过灵活的方式来解决您的字段的动态特性。
相信我,使用已保存的代码片段来验证表单会成为维护的噩梦。信不信由我在编程生涯的早期实际上已经完成了它,这很糟糕。
使用正确的JSON解析器而不是eval。如果您无法将所需内容编码为JSON解析器将读取的内容,那么您不希望它出现在JSON中!
您是否有任何理由不能以这种方式读取您正在验证的数据?