File

demo/src/app/app.component.ts

Metadata

selector app-root
styles /deep/ html { font: 14px sans-serif; margin: 30px; } .mat-icon-button { outline: none; } .mat-arrow-icon { outline: none; line-height: 32px; } .mat-form-field { padding-left: 5px; padding-right: 5px; } .text-input { padding: 4px 8px; border-radius: 4px; border: 1px solid #ccc; } .text-area { width: 300px; height: 100px; } .output { width: 100%; height: 300px; }
template <h2>Vanilla</h2>
<br>
<query-builder [formControl]='queryCtrl' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
  <ng-container *queryInput="let rule; type: 'textarea'; let getDisabledState=getDisabledState">
    <textarea class="text-input text-area" [(ngModel)]="rule.value" [disabled]=getDisabledState()
      placeholder="Custom Textarea"></textarea>
  </ng-container>
</query-builder>
<br>
<div>
  <div class="row">
    <p class="col-6">Control Valid (Vanilla): {{ queryCtrl.valid }}</p>
    <div class="col-6">
      <label><input type="checkbox" (change)=switchModes($event)>Entity Mode</label>
    </div>
  </div>
  <div class="row">
    <p class="col-6">Control Touched (Vanilla): {{ queryCtrl.touched }}</p>
    <div class="col-6">
      <label><input type="checkbox" (change)=changeDisabled($event)>Disabled</label>
    </div>
  </div>
  <div class="row">
    <div class="col-6">
      <label><input type="checkbox" [(ngModel)]='allowRuleset'>Allow Ruleset</label>
    </div>
    <div class="col-6">
      <label><input type="checkbox" [(ngModel)]='allowCollapse'>Allow Collapse</label>
    </div>
    <div class="col-6">
      <label><input type="checkbox" [(ngModel)]='persistValueOnFieldChange'>Persist Values on Field Change</label>
    </div>
  </div>

  <textarea class="output">{{query | json}}</textarea>
</div>
<br>
<h2>Custom Material</h2>
<br>
<mat-card>
<query-builder [(ngModel)]='query' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
  <ng-container *queryButtonGroup="let ruleset; let addRule=addRule; let addRuleSet=addRuleSet; let removeRuleSet=removeRuleSet">
    <button type="button" mat-icon-button color="primary" (click)="addRule()">
      <mat-icon>add</mat-icon></button>
    <button type="button" mat-icon-button color="primary" *ngIf="addRuleSet" (click)="addRuleSet()">
      <mat-icon>add_circle_outline</mat-icon></button>
    <button type="button" mat-icon-button color="accent" *ngIf="removeRuleSet" (click)="removeRuleSet()">
      <mat-icon>remove_circle_outline</mat-icon></button>
  </ng-container>
  <ng-container *queryArrowIcon>
    <mat-icon ngClass="mat-arrow-icon">chevron_right</mat-icon>
  </ng-container>
  <ng-container *queryRemoveButton="let rule; let removeRule=removeRule">
    <button type="button" mat-icon-button color="accent" (click)="removeRule(rule)">
      <mat-icon>remove</mat-icon>
    </button>
  </ng-container>
  <ng-container *querySwitchGroup="let ruleset; let onChange=onChange">
    <mat-radio-group *ngIf="ruleset" [(ngModel)]="ruleset.condition" (ngModelChange)="onChange($event)">
      <mat-radio-button [style.padding.px]="10" value="and">And</mat-radio-button>
      <mat-radio-button [style.padding.px]="10" value="or">Or</mat-radio-button>
    </mat-radio-group>
  </ng-container>
  <ng-container *queryEntity="let rule; let entities=entities; let onChange=onChange">
    <mat-form-field>
      <mat-select [(ngModel)]="rule.entity" (ngModelChange)="onChange($event, rule)">
        <mat-option *ngFor="let entity of entities" [value]="entity.value">
        {{entity.name}}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </ng-container>
  <ng-container *queryField="let rule; let fields=fields; let onChange=onChange; let getFields = getFields">
    <mat-form-field>
      <mat-select [(ngModel)]="rule.field" (ngModelChange)="onChange($event, rule)">
        <mat-option *ngFor="let field of getFields(rule.entity)" [value]="field.value">
          {{ 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 [style.width.px]="90">
      <mat-select [(ngModel)]="rule.operator" (ngModelChange)="onChange(rule)">
        <mat-option *ngFor="let value of operators" [value]="value">
          {{ value }}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; type: 'boolean'; let onChange=onChange">
    <mat-checkbox [(ngModel)]="rule.value" (ngModelChange)="onChange()"></mat-checkbox>
  </ng-container>
  <ng-container *queryInput="let rule; let field=field; let options=options; type: 'category'; let onChange=onChange">
    <mat-form-field>
      <mat-select [(ngModel)]="rule.value" (ngModelChange)="onChange()">
        <mat-option *ngFor="let opt of options" [value]="opt.value">
          {{ opt.name }}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; type: 'date'; let onChange=onChange">
    <mat-form-field>
      <input matInput [matDatepicker]="picker" [(ngModel)]="rule.value" (ngModelChange)="onChange()">
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <mat-datepicker #picker></mat-datepicker>
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; let options=options; type: 'multiselect'; let onChange=onChange">
    <mat-form-field [style.width.px]="300">
      <mat-select [(ngModel)]="rule.value" multiple (ngModelChange)="onChange()">
        <mat-option *ngFor="let opt of options" [value]="opt.value">
          {{ opt.name }}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; let field=field; type: 'number'; let onChange=onChange">
    <mat-form-field [style.width.px]="50">
      <input matInput [(ngModel)]="rule.value" type="number" (ngModelChange)="onChange()">
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; let field=field; type: 'string'; let onChange=onChange">
    <mat-form-field>
      <input matInput [(ngModel)]="rule.value" (ngModelChange)="onChange()">
    </mat-form-field>
  </ng-container>
  <ng-container *queryInput="let rule; let field=field; type: 'textarea'; let onChange=onChange">
    <mat-form-field>
      <textarea matInput [(ngModel)]="rule.value" (ngModelChange)="onChange()">
      </textarea>
    </mat-form-field>
  </ng-container>
</query-builder>
</mat-card>
<br>
<h2>Bootstrap</h2>
<br>
<query-builder [(ngModel)]='query' [classNames]='bootstrapClassNames' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
  <div class="col-auto" *queryInput="let rule; type: 'textarea'">
    <textarea class="form-control" [(ngModel)]="rule.value"
      placeholder="Custom Textarea"></textarea>
  </div>
</query-builder>

Constructor

constructor(formBuilder: FormBuilder)

Methods

switchModes
switchModes(event: Event)
Returns: void
changeDisabled
changeDisabled(event: Event)
Returns: void

Properties

Public allowCollapse
allowCollapse: boolean
Public allowRuleset
allowRuleset: boolean
Default value: true
Public bootstrapClassNames
bootstrapClassNames: QueryBuilderClassNames
Public config
config: QueryBuilderConfig
Public currentConfig
currentConfig: QueryBuilderConfig
Public entityConfig
entityConfig: QueryBuilderConfig
Public persistValueOnFieldChange
persistValueOnFieldChange: boolean
Default value: false
Public query
query: { condition: string; rules: ({ field: string; operator: string; entity: string; } | { field: stri...
Public queryCtrl
queryCtrl: FormControl
import { FormBuilder, FormControl } from '@angular/forms';
import { Component } from '@angular/core';
import { QueryBuilderClassNames, QueryBuilderConfig } from 'angular2-query-builder';

@Component({
  selector: 'app-root',
  template: `
  <h2>Vanilla</h2>
  <br>
  <query-builder [formControl]='queryCtrl' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
    <ng-container *queryInput="let rule; type: 'textarea'; let getDisabledState=getDisabledState">
      <textarea class="text-input text-area" [(ngModel)]="rule.value" [disabled]=getDisabledState()
        placeholder="Custom Textarea"></textarea>
    </ng-container>
  </query-builder>
  <br>
  <div>
    <div class="row">
      <p class="col-6">Control Valid (Vanilla): {{ queryCtrl.valid }}</p>
      <div class="col-6">
        <label><input type="checkbox" (change)=switchModes($event)>Entity Mode</label>
      </div>
    </div>
    <div class="row">
      <p class="col-6">Control Touched (Vanilla): {{ queryCtrl.touched }}</p>
      <div class="col-6">
        <label><input type="checkbox" (change)=changeDisabled($event)>Disabled</label>
      </div>
    </div>
    <div class="row">
      <div class="col-6">
        <label><input type="checkbox" [(ngModel)]='allowRuleset'>Allow Ruleset</label>
      </div>
      <div class="col-6">
        <label><input type="checkbox" [(ngModel)]='allowCollapse'>Allow Collapse</label>
      </div>
      <div class="col-6">
        <label><input type="checkbox" [(ngModel)]='persistValueOnFieldChange'>Persist Values on Field Change</label>
      </div>
    </div>

    <textarea class="output">{{query | json}}</textarea>
  </div>
  <br>
  <h2>Custom Material</h2>
  <br>
  <mat-card>
  <query-builder [(ngModel)]='query' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
    <ng-container *queryButtonGroup="let ruleset; let addRule=addRule; let addRuleSet=addRuleSet; let removeRuleSet=removeRuleSet">
      <button type="button" mat-icon-button color="primary" (click)="addRule()">
        <mat-icon>add</mat-icon></button>
      <button type="button" mat-icon-button color="primary" *ngIf="addRuleSet" (click)="addRuleSet()">
        <mat-icon>add_circle_outline</mat-icon></button>
      <button type="button" mat-icon-button color="accent" *ngIf="removeRuleSet" (click)="removeRuleSet()">
        <mat-icon>remove_circle_outline</mat-icon></button>
    </ng-container>
    <ng-container *queryArrowIcon>
      <mat-icon ngClass="mat-arrow-icon">chevron_right</mat-icon>
    </ng-container>
    <ng-container *queryRemoveButton="let rule; let removeRule=removeRule">
      <button type="button" mat-icon-button color="accent" (click)="removeRule(rule)">
        <mat-icon>remove</mat-icon>
      </button>
    </ng-container>
    <ng-container *querySwitchGroup="let ruleset; let onChange=onChange">
      <mat-radio-group *ngIf="ruleset" [(ngModel)]="ruleset.condition" (ngModelChange)="onChange($event)">
        <mat-radio-button [style.padding.px]="10" value="and">And</mat-radio-button>
        <mat-radio-button [style.padding.px]="10" value="or">Or</mat-radio-button>
      </mat-radio-group>
    </ng-container>
    <ng-container *queryEntity="let rule; let entities=entities; let onChange=onChange">
      <mat-form-field>
        <mat-select [(ngModel)]="rule.entity" (ngModelChange)="onChange($event, rule)">
          <mat-option *ngFor="let entity of entities" [value]="entity.value">
          {{entity.name}}
          </mat-option>
        </mat-select>
      </mat-form-field>
    </ng-container>
    <ng-container *queryField="let rule; let fields=fields; let onChange=onChange; let getFields = getFields">
      <mat-form-field>
        <mat-select [(ngModel)]="rule.field" (ngModelChange)="onChange($event, rule)">
          <mat-option *ngFor="let field of getFields(rule.entity)" [value]="field.value">
            {{ 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 [style.width.px]="90">
        <mat-select [(ngModel)]="rule.operator" (ngModelChange)="onChange(rule)">
          <mat-option *ngFor="let value of operators" [value]="value">
            {{ value }}
          </mat-option>
        </mat-select>
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; type: 'boolean'; let onChange=onChange">
      <mat-checkbox [(ngModel)]="rule.value" (ngModelChange)="onChange()"></mat-checkbox>
    </ng-container>
    <ng-container *queryInput="let rule; let field=field; let options=options; type: 'category'; let onChange=onChange">
      <mat-form-field>
        <mat-select [(ngModel)]="rule.value" (ngModelChange)="onChange()">
          <mat-option *ngFor="let opt of options" [value]="opt.value">
            {{ opt.name }}
          </mat-option>
        </mat-select>
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; type: 'date'; let onChange=onChange">
      <mat-form-field>
        <input matInput [matDatepicker]="picker" [(ngModel)]="rule.value" (ngModelChange)="onChange()">
        <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
        <mat-datepicker #picker></mat-datepicker>
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; let options=options; type: 'multiselect'; let onChange=onChange">
      <mat-form-field [style.width.px]="300">
        <mat-select [(ngModel)]="rule.value" multiple (ngModelChange)="onChange()">
          <mat-option *ngFor="let opt of options" [value]="opt.value">
            {{ opt.name }}
          </mat-option>
        </mat-select>
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; let field=field; type: 'number'; let onChange=onChange">
      <mat-form-field [style.width.px]="50">
        <input matInput [(ngModel)]="rule.value" type="number" (ngModelChange)="onChange()">
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; let field=field; type: 'string'; let onChange=onChange">
      <mat-form-field>
        <input matInput [(ngModel)]="rule.value" (ngModelChange)="onChange()">
      </mat-form-field>
    </ng-container>
    <ng-container *queryInput="let rule; let field=field; type: 'textarea'; let onChange=onChange">
      <mat-form-field>
        <textarea matInput [(ngModel)]="rule.value" (ngModelChange)="onChange()">
        </textarea>
      </mat-form-field>
    </ng-container>
  </query-builder>
  </mat-card>
  <br>
  <h2>Bootstrap</h2>
  <br>
  <query-builder [(ngModel)]='query' [classNames]='bootstrapClassNames' [config]='currentConfig' [allowRuleset]='allowRuleset' [allowCollapse]='allowCollapse' [persistValueOnFieldChange]='persistValueOnFieldChange'>
    <div class="col-auto" *queryInput="let rule; type: 'textarea'">
      <textarea class="form-control" [(ngModel)]="rule.value"
        placeholder="Custom Textarea"></textarea>
    </div>
  </query-builder>
  `,
  styles: [`
  /deep/ html {
    font: 14px sans-serif;
    margin: 30px;
  }

  .mat-icon-button {
    outline: none;
  }

  .mat-arrow-icon {
    outline: none;
    line-height: 32px;
  }

  .mat-form-field {
    padding-left: 5px;
    padding-right: 5px;
  }

  .text-input {
    padding: 4px 8px;
    border-radius: 4px;
    border: 1px solid #ccc;
  }

  .text-area {
    width: 300px;
    height: 100px;
  }

  .output {
    width: 100%;
    height: 300px;
  }
  `]
})
export class AppComponent {
  public queryCtrl: FormControl;

  public bootstrapClassNames: QueryBuilderClassNames = {
    removeIcon: 'fa fa-minus',
    addIcon: 'fa fa-plus',
    arrowIcon: 'fa fa-chevron-right px-2',
    button: 'btn',
    buttonGroup: 'btn-group',
    rightAlign: 'order-12 ml-auto',
    switchRow: 'd-flex px-2',
    switchGroup: 'd-flex align-items-center',
    switchRadio: 'custom-control-input',
    switchLabel: 'custom-control-label',
    switchControl: 'custom-control custom-radio custom-control-inline',
    row: 'row p-2 m-1',
    rule: 'border',
    ruleSet: 'border',
    invalidRuleSet: 'alert alert-danger',
    emptyWarning: 'text-danger mx-auto',
    operatorControl: 'form-control',
    operatorControlSize: 'col-auto pr-0',
    fieldControl: 'form-control',
    fieldControlSize: 'col-auto pr-0',
    entityControl: 'form-control',
    entityControlSize: 'col-auto pr-0',
    inputControl: 'form-control',
    inputControlSize: 'col-auto'
  };

  public query = {
    condition: 'and',
    rules: [
      {field: 'age', operator: '<=', entity: 'physical'},
      {field: 'birthday', operator: '=', value: new Date(), entity: 'nonphysical'},
      {
        condition: 'or',
        rules: [
          {field: 'gender', operator: '=', entity: 'physical'},
          {field: 'occupation', operator: 'in', entity: 'nonphysical'},
          {field: 'school', operator: 'is null', entity: 'nonphysical'},
          {field: 'notes', operator: '=', entity: 'nonphysical'}
        ]
      }
    ]
  };

  public entityConfig: QueryBuilderConfig = {
    entities: {
      physical: {name: 'Physical Attributes'},
      nonphysical: {name: 'Nonphysical Attributes'}
    },
    fields: {
      age: {name: 'Age', type: 'number', entity: 'physical'},
      gender: {
        name: 'Gender',
        entity: 'physical',
        type: 'category',
        options: [
          {name: 'Male', value: 'm'},
          {name: 'Female', value: 'f'}
        ]
      },
      name: {name: 'Name', type: 'string', entity: 'nonphysical'},
      notes: {name: 'Notes', type: 'textarea', operators: ['=', '!='], entity: 'nonphysical'},
      educated: {name: 'College Degree?', type: 'boolean', entity: 'nonphysical'},
      birthday: {name: 'Birthday', type: 'date', operators: ['=', '<=', '>'],
        defaultValue: (() => new Date()), entity: 'nonphysical'
      },
      school: {name: 'School', type: 'string', nullable: true, entity: 'nonphysical'},
      occupation: {
        name: 'Occupation',
        entity: 'nonphysical',
        type: 'category',
        options: [
          {name: 'Student', value: 'student'},
          {name: 'Teacher', value: 'teacher'},
          {name: 'Unemployed', value: 'unemployed'},
          {name: 'Scientist', value: 'scientist'}
        ]
      }
    }
  };

  public config: QueryBuilderConfig = {
    fields: {
      age: {name: 'Age', type: 'number'},
      gender: {
        name: 'Gender',
        type: 'category',
        options: [
          {name: 'Male', value: 'm'},
          {name: 'Female', value: 'f'}
        ]
      },
      name: {name: 'Name', type: 'string'},
      notes: {name: 'Notes', type: 'textarea', operators: ['=', '!=']},
      educated: {name: 'College Degree?', type: 'boolean'},
      birthday: {name: 'Birthday', type: 'date', operators: ['=', '<=', '>'],
        defaultValue: (() => new Date())
      },
      school: {name: 'School', type: 'string', nullable: true},
      occupation: {
        name: 'Occupation',
        type: 'category',
        options: [
          {name: 'Student', value: 'student'},
          {name: 'Teacher', value: 'teacher'},
          {name: 'Unemployed', value: 'unemployed'},
          {name: 'Scientist', value: 'scientist'}
        ]
      }
    }
  };

  public currentConfig: QueryBuilderConfig;
  public allowRuleset: boolean = true;
  public allowCollapse: boolean;
  public persistValueOnFieldChange: boolean = false;

  constructor(
    private formBuilder: FormBuilder
  ) {
    this.queryCtrl = this.formBuilder.control(this.query);
    this.currentConfig = this.config;
  }

  switchModes(event: Event) {
    this.currentConfig = (<HTMLInputElement>event.target).checked ? this.entityConfig : this.config;
  }

  changeDisabled(event: Event) {
    (<HTMLInputElement>event.target).checked ? this.queryCtrl.disable() : this.queryCtrl.enable();
  }
}

results matching ""

    No results matching ""