Работает ли у вас что-то вроде , это ?
public readonly countries = ["France", "Germany"];
public readonly cities = {
"France": ["Paris", "Lyon", "Marseille"],
"Germany": ["Berlin", "Frankfurt", "Hamburg"]
};
public selection = {
country: '',
city: ''
};
Pick country
<select [(ngModel)]="selection.country">
<option *ngFor="let c of countries" [ngValue]="c">{{c}}</option>
</select>
<br/>
<br/>
<ng-container *ngIf="selection.country.length > 0">
Pick city
<select [(ngModel)]="selection.city">
<option *ngFor="let c of cities[selection.country]" [ngValue]="c">{{c}}</option>
</select>
</ng-container>
Начинается с начального выбора верхнего уровня, а затем показывает следующий вопрос в зависимости от предыдущего ответа.
ОБНОВЛЕНИЕ 1: Я адаптировал ответ на основе опубликованной вами диаграммы. Новое решение не использует жестко закодированные пути, а определяет структуру «дерева решений» и использует ее. Я объясню более подробно позже, но суть в том, что вам нужно изменить только содержимое одной переменной, и приложение все равно будет работать. Новый stackblitz .
Идея заключается в сохранении каждого узла в вашем дереве в виде объекта, подобного следующему:
export class TreeNode {
public readonly id: nodeId;
public readonly description: string;
public decision: boolean | null;
public readonly yesId: nodeId | null;
public readonly noId: nodeId | null;
constructor(
id: nodeId,
description: string,
yesId: nodeId | null,
noId: nodeId | null
) {
this.id = id;
this.description = description;
this.decision = null; // <-- must be null on creation. wait for decision from user.
this.yesId = yesId;
this.noId = noId;
}
}
id
- это строка, если быть уникальным среди всех узлов. Подробнее об этом позже. description
- тело вопроса / ответа. decision
- результат вопроса Да / Нет. yesId
is id
узла, к которому мы должны перейти, когда решение - да. noId
- это id
узла, к которому мы должны перейти, когда решение - нет. Если узел является листом, то yesId
и noId
должны быть равны нулю.
Я сохранил все возможные узлы из вашего чертежа как «словарь» под названием nodeList
, поэтому мы можем получить доступ к узлу, используя синтаксис nodeList[id]
. Теперь это выглядит так:
/**
* Possible values for a node id. This is optional, but highly encouraged.
*/
export type nodeId =
| "chestPain"
| "twaveInversion"
| "rateOver100"
| "hypotensive"
| "nstemi"
| "unstableAngina"
| "sedate"
| "qrsOver012"
| "vtGetExpertHelp"
| "qrsRegular"
| "svtVagal"
| "pwavesPresent"
| "atrialFibrilation"
| "aflutter"
| "rate100temp";
/**
* Dictionary of nodes using their id as a key.
*/
export const nodeList = {
chestPain: new TreeNode(
"chestPain",
"Chest Pain?",
"twaveInversion",
"rateOver100"
),
twaveInversion: new TreeNode(
"twaveInversion",
"Twave Inversion?",
"nstemi",
"unstableAngina"
),
unstableAngina: new TreeNode("unstableAngina", "Unstable Angina", null, null),
nstemi: new TreeNode("nstemi", "NSTEMI", null, null),
rateOver100: new TreeNode(
"rateOver100",
"Rate > 100?",
"hypotensive",
"rate100temp"
),
hypotensive: new TreeNode(
"hypotensive",
"Hypotensive?",
"sedate",
"qrsOver012"
),
sedate: new TreeNode("sedate", "Sedate.", null, null),
qrsOver012: new TreeNode(
"qrsOver012",
"QRS > 0.12s ?",
"vtGetExpertHelp",
"qrsRegular"
),
vtGetExpertHelp: new TreeNode(
"vtGetExpertHelp",
"VT Get expert help.",
null,
null
),
qrsRegular: new TreeNode(
"qrsRegular",
"QRS regular?",
"svtVagal",
"pwavesPresent"
),
svtVagal: new TreeNode("svtVagal", "SVT Vagal", null, null),
pwavesPresent: new TreeNode(
"pwavesPresent",
"Pwaves present?",
"aflutter",
"atrialFibrilation"
),
aflutter: new TreeNode("aflutter", "Aflutter", null, null),
atrialFibrilation: new TreeNode(
"atrialFibrilation",
"Afrial fibrilation",
null,
null
),
rate100temp: new TreeNode(
"rate100temp",
"Rate < 100 answer (no node given)",
null,
null
)
};
И, наконец, DecisionTreeFormComponent
:
export class DecisionTreeFormComponent implements OnInit {
public decisionTree: IDecisionTree;
public currentNode: TreeNode;
public treeJSONhidden: boolean = true;
public nodeJSONhidden: boolean = true;
constructor() {}
ngOnInit() {
this.reset();
}
public reset() {
// Init base node and tree here.
this.decisionTree = [];
this.currentNode = Object.assign({}, nodeList.chestPain);
}
public yes() {
this.currentNode.decision = true;
this.pushNode();
this.currentNode = Object.assign({}, nodeList[this.currentNode.yesId]);
if( this.isFinal(this.currentNode)) {
this.pushNode();
}
}
public no() {
this.currentNode.decision = false;
this.pushNode();
this.currentNode = Object.assign({}, nodeList[this.currentNode.noId]);
if( this.isFinal(this.currentNode)) {
this.pushNode();
}
}
public isFinal = (node: TreeNode) => node.yesId == null && node.noId == null;
private pushNode():void {
this.decisionTree.push({
node: this.currentNode,
index: this.decisionTree.length
});
}
}
В методе reset()
я просто инициализирую currentNode
к вопросу о боли в груди а остальное обрабатывается по выбору пользователя. Я сохраняю путь в виде массива для отображения окончательного результата при достижении конечного узла. Посмотрите образец HTML, который я подготовил в стеке, но в данном случае презентация не имеет значения. Вы можете настроить его в соответствии со своими потребностями.
Я думаю, что это правильное решение, где вам нужно всего лишь изменить nodeList
, и форма будет работать на основе значений, которые там найдены.
Ограничения: В настоящее время это работает только для вопросов типа ДА / НЕТ. Если вы ожидаете множественный выбор, вы можете попытаться «рефакторинг» вопросов, чтобы они были множеством истинных / ложных вопросов. В противном случае мы можем обсудить это дальше.