nth-level Dynamic Query Builder Using Angular 7

nth-level Dynamic Query Builder Using Angular 7

Dynamic Query builder is a more innovative way to handle multiple conditions. We can say a set of rules using a simple but astonishing UI where the user can easily create dynamic rulesets (Combination of various conditions). Usually, it is used in backend portals where users can set dynamic rules and get an output in structured JSON format. Then you can parse the outcome in your desired form like SQL or No-SQL.

Let’s see the dynamic builder’s usage and how you can place fields, operators, and results dynamically. Follow the steps as shown below:

Step 1: Install Query Builder Package from the command prompt

    npm install angular2-query-builder   

Step 2: Import Query Builder module for use package in component

File Name: app.module.ts

query-builder

Step 3: Create a query builder (Set rule and rule set with custom fields)

Here based on the field selection, the builder dynamically sets a query operator.

e.g. 1: if fields need to check multiple values then operator can be “IN or NOT IN”

e.g. 2: if fields need to check numeric value then operator can be “<, >, <=, >=, <>, =”

File Name: app.component.html

<query-builder [data]="uiExpression" [config]="config"> 
  	<ng-container 
    	*queryButtonGroup="let ruleset; let addRule=addRule; let addRuleSet=addRuleSet; let removeRuleSet=removeRuleSet"> 
    	<button mat-button (click)="addRule()">+ Rule</button> 
    	<button mat-button (click)="addRuleSet()">+ Ruleset</button> 
    	<button mat-button (click)="removeRuleSet()">- Ruleset</button> 
  	</ng-container> 
  	<ng-container *queryRemoveButton="let rule; let removeRule=removeRule"> 
    	<button mat-icon-button color="accent" (click)="removeRule(rule)"> 
      	<mat-icon>remove</mat-icon> 
    	</button> 
  	</ng-container> 
  	<ng-container *querySwitchGroup="let ruleset"> 
    	<mat-radio-group *ngIf="ruleset" [(ngModel)]="ruleset.condition"> 
      	<mat-radio-button value="and">And</mat-radio-button> 
      	<mat-radio-button value="or">Or</mat-radio-button> 
    	</mat-radio-group> 
  	</ng-container>        
  	<ng-container *queryInput="let rule; type: 'number'"> 
    	<mat-form-field> 
      	<input matInput type="number" [(ngModel)]="rule.value"> 
    	</mat-form-field> 
  	</ng-container> 
  	<ng-container *queryField="let rule; let fields=fields; let onChange=onChange; let changeField=changeField"> 
    	<mat-form-field class="full-width-field"> 
      	<mat-select [(ngModel)]="rule.field" (ngModelChange)="onChange($event, rule)">  
        	<mat-option *ngFor="let field of fields" [value]="field.value"> <b>{{field.sequence}}. </b> {{field.name}} 
        	</mat-option> 
      	</mat-select> 
    	</mat-form-field> 
  	</ng-container> 
  	<ng-container *queryOperator="let rule; let operators=operators; let onChange=onChange"> 
      	<mat-form-field class="text-center"> 
        	<mat-select [(ngModel)]="rule.operator" (ngModelChange)="onChange(rule)"> 
          	<mat-option *ngFor="let value of operators" [value]="value.value">{{value.name}} 
</mat-option> 
        	</mat-select> 
      	</mat-form-field> 
    	</ng-container>  
	</query-builder> 

File Name: app.component.ts

import { QueryBuilderConfig } from 'angular2-query-builder';  
export class AppComponent {} 

Step 4: Operator sets to display it in query builder  

checboxRadioOperators = [ 
	{ 'name': 'in', 'value': 'in' }, 
	{ 'name': 'not in', 'value': 'not in' }, 
  ]; 
  numberOperators = [ 
	{ 'name': '=', 'value': '=' }, 
	{ 'name': '!=', 'value': '!=' }, 
	{ 'name': '>', 'value': '>' }, 
	{ 'name': '>=', 'value': '>=' }, 
	{ 'name': '<', 'value': '<' }, 
	{ 'name': '<=', 'value': '<=' }, 
  ];    

Step 5: Call the API and fetch dynamic fields to set it in query builder

getQuestionList(searchObj) { 
    searchObj.filter.status = 1; 
    searchObj.sortOrder = 'asc'; 
	this.questionmappingService.getMapQuestionsList(this.analyzertagsId).subscribe(response => { 
      this.data = response; 
  	this.id = this.data .items[0]._id; 
      this.analyzerTagsObj = this.data.items[0].analyzerTagsObj[0]; 
      this.questionArray = this.data.items[0]; 
      this.selectedQuestion = this.data.items[0].questionsObj[ this.sequence - 1]; 
   	if (!this.selectedQuestion) { 
        this.router.navigate(['master/analyzertags-list']); 
   	} else { 
        this.data.items[0].questionsObj.forEach((element) => { 
      	if (element.sequence < this.sequence) { 
        	if (element.answer_input === 'radio' || element.answer_input === 'checkbox') { 
              this.operators = this.checboxRadioOperators; 
        	} 
        	if (element.answer_input === 'number') { 
              this.operators = this.numberOperators; 
        	} 
            this.fieldsS[element._id] = { 
        	name: element.question_text, 
        	sequence: element.sequence, 
        	type: element.answer_input.toLowerCase(), 
        	operators: this.operators, 
        	options: element.answer_options[0].answer 
      	}; 
          this.config.fields = this.fieldsS; 
      	} 
  	}); 
  	if (this.data.items[0].questionsObj.length > 0) { 
    	if (!this.data.items[0].questionsObj[this.sequence - 1].conditionObj) { 
      	if (this.data.items[0].answer_input === 'radio' || this.data.items[0].answer_input === 'checkbox') { 
            this.operators = this.checboxRadioOperators; 
      	} 
      	if (this.data.items[0].answer_input === 'number') { 
            this.operators = this.numberOperators; 
      	}        
/** Set default questions when query builder loads */    
this.uiExpression = { 
        	condition: 'and', 
        	rules: [ 
          	{ 
            	field: this.data.items[0].questionsObj[0]._id, 
            	type: this.data.items[0].questionsObj[0].answer_input.toLowerCase(), 
            	operator: this.operators[0].value, 
            	options: this.data.items[0].questionsObj[0].answer_options[0].answer[0], 
          	} 
        	] 
      	}; 
    	} else { 
          this.uiExpression = this.data.items[0].questionsObj[this.sequence - 1].conditionObj; 
    	} 
  	} 
   	} 
	}); 
  } 

The code snippets above have been implemented to create dynamic questions and their answers or any other dependency. Using this tool builds queries based on the input of questions. 

Ex:  If the answer to the age question is… > 50, then, If the answer to X question is IN (1, 2, 7), then. You can follow the steps above to integrate dynamic fields and operators in the query builder based on your requirements. To know more about Dynamic Query Builder, get in touch with a DEV IT expert here.