重构Angular 17查询构建器:移除嵌套调用并使用NgTemplate
用NgTemplate重构Angular查询构建器消除组件嵌套重复
核心思路
把query-builder组件的模板内容抽离成可复用的ng-template,通过ngTemplateOutlet实现递归渲染,替代原来的组件嵌套调用,彻底消除重复的组件实例创建逻辑。
修改后的代码
1. 重构query-builder.component.html
将原组件模板内容封装到ng-template中,递归时引用该模板而非组件自身:
<ng-template #groupTemplate let-group> <div class="alert alert-warning alert-group"> <div class="input-group"> <select [(ngModel)]="group.operator" class="form-control input-sm"> <option *ngFor="let o of operators" [value]="o.name">{{ o.name }}</option> </select> <button style="margin-left: 5px" (click)="addCondition(group)" class="btn btn-sm btn-success"> <span class="glyphicon glyphicon-plus-sign"></span> Add Condition </button> <button style="margin-left: 5px" (click)="addGroup(group)" class="btn btn-sm btn-success"> <span class="glyphicon glyphicon-plus-sign"></span> Add Group </button> <button style="margin-left: 5px" (click)="removeGroup(group)" class="btn btn-sm btn-danger"> <span class="glyphicon glyphicon-minus-sign"></span> Remove Group </button> </div> <div class="group-conditions"> <div *ngFor="let rule of group.rules" class="condition"> <div [ngSwitch]="rule.hasOwnProperty('group')"> <div *ngSwitchCase="true"> <!-- 用模板引用替代组件嵌套 --> <ng-container [ngTemplateOutlet]="groupTemplate" [ngTemplateOutletContext]="{ $implicit: rule.group }"></ng-container> </div> <div *ngSwitchDefault> <div class="input-group"> <select [(ngModel)]="rule.field" class="form-control input-sm"> <option *ngFor="let t of fields" [value]="t.name">{{ t.name }}</option> </select> <select style="margin-left: 5px" [(ngModel)]="rule.condition" class="form-control input-sm"> <option *ngFor="let c of conditions" [value]="c.name">{{ c.name }}</option> </select> <input style="margin-left: 5px" type="text" [(ngModel)]="rule.data" class="form-control input-sm" /> <button style="margin-left: 5px" (click)="removeCondition(group, rule)" class="btn btn-sm btn-danger"> <span class="glyphicon glyphicon-minus-sign">Delete</span> </button> </div> </div> </div> </div> </div> </div> </ng-template> <!-- 渲染根组模板 --> <ng-container [ngTemplateOutlet]="groupTemplate" [ngTemplateOutletContext]="{ $implicit: group }"></ng-container>
2. 调整query-builder.component.ts方法
为适配模板上下文传递,修改操作方法并完善删除逻辑:
import { Component, Input, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core'; import { Group, QueryService, Rule } from '../shared/query.service'; @Component({ selector: 'query-builder', templateUrl: './query-builder.component.html', styleUrls: ['./query-builder.component.css'] }) export class QueryBuilderComponent { @Input() group: Group = { operator: 'AND', rules: [], parent: null }; @ViewChild('groupTemplate') groupTemplate!: TemplateRef<any>; operators = [ { name: 'AND' }, { name: 'OR' } ]; fields = [ { name: 'Firstname' }, { name: 'Lastname' }, { name: 'Birthdate' }, { name: 'City' }, { name: 'Country' } ]; conditions = [ { name: '=' }, { name: '<>' }, { name: '<' }, { name: '<=' }, { name: '>' }, { name: '>=' } ]; @Output() queryChanged = new EventEmitter<Group>(); constructor(private queryService: QueryService) {} addCondition(currentGroup: Group) { currentGroup.rules.push({ field: '', condition: '', data: '' }); this.queryChanged.emit(this.group); } addGroup(currentGroup: Group) { // 为新组设置父组引用 const newGroup: Group = { operator: 'AND', rules: [], parent: currentGroup }; currentGroup.rules.push({ group: newGroup }); this.queryChanged.emit(this.group); } removeGroup(currentGroup: Group) { const parent = currentGroup.parent; if (parent) { // 从父组规则中移除当前组 parent.rules = parent.rules.filter(rule => rule.group !== currentGroup); this.queryChanged.emit(this.group); } } removeCondition(currentGroup: Group, rule: Rule) { // 从当前组规则中移除指定条件 currentGroup.rules = currentGroup.rules.filter(r => r !== rule); this.queryChanged.emit(this.group); } }
3. app.component相关文件无需修改
保持原有调用逻辑即可,注意补充queryChanged事件绑定:
<div class="container"> <h1>Angular Query Builder</h1> <div class="alert alert-info"> <strong>Example Output</strong><br> <span [innerHTML]="output"></span> </div> <query-builder [group]="filter.group" (queryChanged)="onQueryChanged($event)"></query-builder> </div>
重构优势
- 消除组件递归嵌套导致的重复代码与冗余实例
- 模板复用更高效,减少DOM节点创建开销
- 逻辑集中在单个组件类中,维护性更强
内容的提问来源于stack exchange,提问作者Smit
相关产品推荐
相关产品推荐

