src/app/query/query.component.ts
AfterViewInit
AfterContentInit
OnDestroy
| encapsulation | ViewEncapsulation.None |
| providers |
'en-GB'
|
| selector | app-query |
| styleUrls | ./query.component.scss |
| templateUrl | ./query.component.html |
'en-GB'
AfterViewInit
AfterContentInit
OnDestroy
Properties |
|
Methods |
|
Inputs |
constructor(sasStoreService: SasStoreService, eventService: EventService, helperService: HelperService, cdf: ChangeDetectorRef)
|
|||||||||||||||
|
Defined in src/app/query/query.component.ts:157
|
|||||||||||||||
|
Parameters :
|
| caching | |
Type : boolean
|
|
Default value : true
|
|
|
Defined in src/app/query/query.component.ts:39
|
|
| viewboxId | |
Type : number | undefined
|
|
|
Defined in src/app/query/query.component.ts:40
|
|
| Public addClause | ||||||
addClause(clauseIndex: number)
|
||||||
|
Defined in src/app/query/query.component.ts:933
|
||||||
|
Parameters :
Returns :
void
|
| Public addGroupClause |
addGroupClause()
|
|
Defined in src/app/query/query.component.ts:967
|
|
Returns :
void
|
| Public clearValues |
clearValues(ind: any, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:866
|
|
Returns :
void
|
| Public clearValuesOperator |
clearValuesOperator(ind: any, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:870
|
|
Returns :
void
|
| Public dateChange | ||||||||||||||||||
dateChange(pickerValue: any, query: any, queryIndex: number, clauseIndex: number, queryValueIndex?: number)
|
||||||||||||||||||
|
Defined in src/app/query/query.component.ts:306
|
||||||||||||||||||
|
Parameters :
Returns :
void
|
| Public dateTimeChange | |||||||||||||||
dateTimeChange(query: any, queryIndex: number, clauseIndex: number, queryValueIndex?: number)
|
|||||||||||||||
|
Defined in src/app/query/query.component.ts:324
|
|||||||||||||||
|
Parameters :
Returns :
void
|
| Public dynamicWhereClauseCreator |
dynamicWhereClauseCreator(groupIndex: number, clauseIndex: number)
|
|
Defined in src/app/query/query.component.ts:396
|
|
Takes the clause object, removes current clause and makes new array (table) ready to be sent to SAS
Returns :
{}
|
| Public getFromGlobals |
getFromGlobals()
|
|
Defined in src/app/query/query.component.ts:271
|
|
Returns :
void
|
| Public getlogic | ||||||
getlogic(logic: any)
|
||||||
|
Defined in src/app/query/query.component.ts:755
|
||||||
|
Parameters :
Returns :
void
|
| getQueryDateTime |
getQueryDateTime(clauseIndex: number, queryIndex: number)
|
|
Defined in src/app/query/query.component.ts:176
|
|
Gets and sets temporary values selected with DATETIME or TIME picker Those values are used for picker to work with format it lieks Later before sending values to backend, values are parsed
Returns :
QueryDateTime
|
| Public Async getValues |
getValues(variable: any, ind: any, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:639
|
|
Returns :
any
|
| Public Async getValuesLoadMore | |||||||||||||||
getValuesLoadMore(event: OnLoadingMoreEvent, variable: any, ind: any, clauseIndex: any)
|
|||||||||||||||
|
Defined in src/app/query/query.component.ts:522
|
|||||||||||||||
|
Parameters :
Returns :
any
|
| Public hasInvalidCluase | ||||||
hasInvalidCluase(clauses: any)
|
||||||
|
Defined in src/app/query/query.component.ts:886
|
||||||
|
Checks if clause is invalid and sets bool property accordingly It is being used in html to mark invalid clauses red
Parameters :
Returns :
boolean
|
| Public hasInvalidGroupCluase |
hasInvalidGroupCluase()
|
|
Defined in src/app/query/query.component.ts:919
|
|
Checks if group clause is invalid and sets bool property accordingly It is being used in html to mark invalid group clauses red
Returns :
boolean
|
| Public isArr | ||||||
isArr(val: any)
|
||||||
|
Defined in src/app/query/query.component.ts:765
|
||||||
|
Parameters :
Returns :
boolean
|
| Public isInHtmlCollection | |||||||||
isInHtmlCollection(value: string, collection: HTMLCollectionOf
|
|||||||||
|
Defined in src/app/query/query.component.ts:992
|
|||||||||
|
Parameters :
Returns :
boolean
|
| ngAfterContentInit |
ngAfterContentInit()
|
|
Defined in src/app/query/query.component.ts:1104
|
|
Returns :
void
|
| Async ngAfterViewInit |
ngAfterViewInit()
|
|
Defined in src/app/query/query.component.ts:1033
|
|
Returns :
any
|
| ngOnDestroy |
ngOnDestroy()
|
|
Defined in src/app/query/query.component.ts:1115
|
|
Returns :
void
|
| onAutocompleteLoadingMore | |||||||||||||||
onAutocompleteLoadingMore(event: OnLoadingMoreEvent, variable: any, ind: any, clauseIndex: any)
|
|||||||||||||||
|
Defined in src/app/query/query.component.ts:1095
|
|||||||||||||||
|
Parameters :
Returns :
void
|
| Public removeClause |
removeClause(ind: number, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:1008
|
|
Returns :
void
|
| Public removeGroupClause | ||||||
removeGroupClause(clauseIndex: number)
|
||||||
|
Defined in src/app/query/query.component.ts:1003
|
||||||
|
Parameters :
Returns :
void
|
| Public resetFilter |
resetFilter()
|
|
Defined in src/app/query/query.component.ts:220
|
|
Resets all variables used for filtering
Returns :
void
|
| Public selectedLabelChange |
selectedLabelChange(label: string, query: any)
|
|
Defined in src/app/query/query.component.ts:773
|
|
Returns :
void
|
| Public setGroupLogic | ||||||||
setGroupLogic(groupLogic: any)
|
||||||||
|
Defined in src/app/query/query.component.ts:298
|
||||||||
|
Sets filtering multiple caluses group logic (and / or)
Parameters :
Returns :
void
|
| Public setINValues |
setINValues(clauseIndex: number, queryIndex: number)
|
|
Defined in src/app/query/query.component.ts:846
|
|
Returns :
void
|
| Public setLogic |
setLogic()
|
|
Defined in src/app/query/query.component.ts:760
|
|
Returns :
void
|
| Public setToGlobals |
setToGlobals()
|
|
Defined in src/app/query/query.component.ts:252
|
|
Returns :
void
|
| Public setVariableOperator |
setVariableOperator(ind: any, operator: any, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:800
|
|
Returns :
void
|
| Public setVariableValues |
setVariableValues(valueObj: any, ind: any, clauseIndex: any)
|
|
Defined in src/app/query/query.component.ts:788
|
|
Returns :
void
|
| Public timeChange | ||||||||||||||||||
timeChange(time: any, query: any, ind: number, clauseIndex: number, queryValueIndex?: number)
|
||||||||||||||||||
|
Defined in src/app/query/query.component.ts:366
|
||||||||||||||||||
|
Parameters :
Returns :
void
|
| usePickersChange |
usePickersChange()
|
|
Defined in src/app/query/query.component.ts:199
|
|
When toggling pickers feature we reset the temp picker values array
Returns :
void
|
| Public variableInputChange | |||||||||||||||
variableInputChange(queryVariable: any, index: number, clauseIndex: number, event: any)
|
|||||||||||||||
|
Defined in src/app/query/query.component.ts:779
|
|||||||||||||||
|
Parameters :
Returns :
void
|
| Public whereClauseFn | ||||||||
whereClauseFn(reset: boolean)
|
||||||||
|
Defined in src/app/query/query.component.ts:1014
|
||||||||
|
Parameters :
Returns :
void
|
| Private _removeQry |
Type : Subscription | undefined
|
|
Defined in src/app/query/query.component.ts:47
|
| Public charOperators |
Type : Array<any>
|
Default value : [
'=',
'<',
'>',
'<=',
'>=',
'CONTAINS',
'IN',
'NOT IN',
'NE'
// "LIKE",
// "BEGINS_WITH",
]
|
|
Defined in src/app/query/query.component.ts:68
|
| Public clauseAmt |
Type : Array<any>
|
Default value : ['']
|
|
Defined in src/app/query/query.component.ts:56
|
| Public clauses |
Type : any
|
Default value : {
queryObj: [this.queryObj],
clauseLogic: [],
groupLogic: this.groupLogic
}
|
|
Defined in src/app/query/query.component.ts:134
|
| Public cols |
Type : Array<any> | undefined
|
|
Defined in src/app/query/query.component.ts:48
|
| Private columnsSub |
Type : Subscription | undefined
|
|
Defined in src/app/query/query.component.ts:45
|
| Public currentClauseIndex |
Type : number
|
Default value : -1
|
|
Defined in src/app/query/query.component.ts:152
|
| Public currentQueryIndex |
Type : number
|
Default value : -1
|
|
Defined in src/app/query/query.component.ts:153
|
| Public dynamicWhereClause |
Type : boolean
|
Default value : true
|
|
Defined in src/app/query/query.component.ts:86
|
| Public groupLogic |
Type : string
|
Default value : 'AND'
|
|
Defined in src/app/query/query.component.ts:132
|
| Private initial_rows |
Type : number
|
Default value : 1000
|
|
Defined in src/app/query/query.component.ts:43
|
| Public innerWidth |
Type : any
|
|
Defined in src/app/query/query.component.ts:54
|
| Public inObj |
Type : Array<any>
|
Default value : [
{
checked: false,
value: null
}
]
|
|
Defined in src/app/query/query.component.ts:125
|
| Public libds |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:49
|
| Public logic |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:81
|
| Public logicOperators |
Type : Array<string>
|
Default value : ['AND', 'OR']
|
|
Defined in src/app/query/query.component.ts:143
|
| Public noBorder |
Type : any
|
|
Defined in src/app/query/query.component.ts:55
|
| Public notArr |
Type : boolean
|
Default value : true
|
|
Defined in src/app/query/query.component.ts:83
|
| Public notIn |
Type : boolean
|
Default value : false
|
|
Defined in src/app/query/query.component.ts:82
|
| Public numOperators |
Type : Array<any>
|
Default value : [
'=',
'<',
'>',
'<=',
'>=',
'BETWEEN',
'IN',
'NOT IN',
'NE'
]
|
|
Defined in src/app/query/query.component.ts:57
|
| Public operator |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:52
|
| Public pendingINValuesSet |
Type : literal type | null
|
Default value : null
|
|
Defined in src/app/query/query.component.ts:154
|
| Public queryDateTime |
Type : QueryDateTime[]
|
Default value : []
|
|
Defined in src/app/query/query.component.ts:150
|
|
Temporary values array used in pickers because they need particular format to work before sending values to backend, values are parsed |
| Private queryObj |
Type : { elements: any[]; clauseLogic?: string; invalidClause?: boolean }
|
Default value : {
elements: [
{
logic: null,
type: null,
ddtype: null,
variable: null,
operator: null,
value: null,
valueVariable: false,
startrow: 0,
rows: 0,
nobs: 0,
values: <
{
formatted: any
unformatted: any
}[]
>[],
operators: []
}
],
clauseLogic: '',
invalidClause: false
}
|
|
Defined in src/app/query/query.component.ts:89
|
| Private rows_increment |
Type : number
|
Default value : 100
|
|
Defined in src/app/query/query.component.ts:42
|
| Public selVar |
Type : any
|
Default value : {
index: null,
format: null,
type: null
}
|
|
Defined in src/app/query/query.component.ts:119
|
| Public usePickers |
Type : boolean
|
Default value : false
|
|
Defined in src/app/query/query.component.ts:87
|
| Public val |
Type : any
|
|
Defined in src/app/query/query.component.ts:141
|
| Public vals |
Type : Array<any> | undefined
|
|
Defined in src/app/query/query.component.ts:53
|
| Public value |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:51
|
| Private valuesSub |
Type : Subscription | undefined
|
|
Defined in src/app/query/query.component.ts:46
|
| Public variable |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:50
|
| Public wasBetween |
Type : boolean
|
Default value : false
|
|
Defined in src/app/query/query.component.ts:84
|
| Public whereClause |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:142
|
| Public whereClauseLoading |
Type : boolean
|
Default value : false
|
|
Defined in src/app/query/query.component.ts:85
|
| Public whereString |
Type : string | undefined
|
|
Defined in src/app/query/query.component.ts:140
|
import { Subscription } from 'rxjs'
import {
Component,
AfterViewInit,
AfterContentInit,
OnDestroy,
ChangeDetectorRef,
LOCALE_ID,
Input,
ViewEncapsulation
} from '@angular/core'
import { SasStoreService } from '../services/sas-store.service'
import { globals } from '../_globals'
import { EventService } from '../services/event.service'
import { registerLocaleData } from '@angular/common'
import localeEnGB from '@angular/common/locales/en-GB'
import { HelperService } from '../services/helper.service'
import { QueryClause } from '../models/TableData'
import { PublicGetcolvalsSASResponse } from '../models/sas/public-getcolvals.model'
import { QueryDateTime } from './models/QueryDateTime.model'
import { isSpecialMissing } from '@sasjs/utils/input/validators'
import { get } from 'lodash-es'
import { OnLoadingMoreEvent } from '../shared/autocomplete/autocomplete.component'
import { getFilterObjPath } from './utils/getFilterObjPath'
registerLocaleData(localeEnGB)
@Component({
selector: 'app-query',
templateUrl: './query.component.html',
styleUrls: ['./query.component.scss'],
providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }],
encapsulation: ViewEncapsulation.None,
standalone: false
})
export class QueryComponent
implements AfterViewInit, AfterContentInit, OnDestroy
{
@Input() caching: boolean = true
@Input() viewboxId: number | undefined
private rows_increment: number = 100
private initial_rows: number = 1000
private columnsSub: Subscription | undefined
private valuesSub: Subscription | undefined
private _removeQry: Subscription | undefined
public cols: Array<any> | undefined
public libds: string | undefined
public variable: string | undefined
public value: string | undefined
public operator: string | undefined
public vals: Array<any> | undefined
public innerWidth: any
public noBorder: any
public clauseAmt: Array<any> = ['']
public numOperators: Array<any> = [
'=',
'<',
'>',
'<=',
'>=',
'BETWEEN',
'IN',
'NOT IN',
'NE'
]
public charOperators: Array<any> = [
'=',
'<',
'>',
'<=',
'>=',
'CONTAINS',
'IN',
'NOT IN',
'NE'
// "LIKE",
// "BEGINS_WITH",
]
public logic: string | undefined
public notIn: boolean = false
public notArr: boolean = true
public wasBetween: boolean = false
public whereClauseLoading: boolean = false
public dynamicWhereClause: boolean = true
public usePickers: boolean = false
private queryObj: {
elements: any[]
clauseLogic?: string
invalidClause?: boolean
} = {
elements: [
{
logic: null,
type: null,
ddtype: null,
variable: null,
operator: null,
value: null,
valueVariable: false,
startrow: 0,
rows: 0,
nobs: 0,
values: <
{
formatted: any
unformatted: any
}[]
>[],
operators: []
}
],
clauseLogic: '',
invalidClause: false
}
public selVar: any = {
index: null,
format: null,
type: null
}
public inObj: Array<any> = [
{
checked: false,
value: null
}
]
public groupLogic: string = 'AND'
public clauses: any = {
queryObj: [this.queryObj],
clauseLogic: [],
groupLogic: this.groupLogic
}
public whereString: string | undefined
public val: any
public whereClause: string | undefined
public logicOperators: Array<string> = ['AND', 'OR']
/**
* Temporary values array used in pickers
* because they need particular format to work
* before sending values to backend, values are parsed
*/
public queryDateTime: QueryDateTime[] = []
public currentClauseIndex: number = -1
public currentQueryIndex: number = -1
public pendingINValuesSet: {
clauseIndex: number
queryIndex: number
} | null = null
constructor(
private sasStoreService: SasStoreService,
private eventService: EventService,
private helperService: HelperService,
private cdf: ChangeDetectorRef
) {
this.innerWidth = window.screen.width
if (this.innerWidth > 768) {
this.noBorder = 'border-left: 2px solid gray;'
}
}
/**
* Gets and sets temporary values selected with DATETIME or TIME picker
* Those values are used for picker to work with format it lieks
* Later before sending values to backend, values are parsed
*/
getQueryDateTime(clauseIndex: number, queryIndex: number): QueryDateTime {
let existingQueryDateTime = this.queryDateTime.find(
(x) => x.clauseIndex === clauseIndex && x.queryIndex === queryIndex
)
if (!existingQueryDateTime) {
const lastIndex =
this.queryDateTime.push({
clauseIndex,
queryIndex,
date: '',
time: ''
}) - 1
existingQueryDateTime = this.queryDateTime[lastIndex]
}
return existingQueryDateTime
}
/**
* When toggling pickers feature we reset the temp picker values array
*/
usePickersChange() {
this.queryDateTime = []
if (this.usePickers) {
this.clauses.queryObj.forEach((queryObj: any) => {
queryObj.elements.forEach((element: any) => {
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(
element.ddtype
)
if (isDateOrTime && element.valueVariable) {
element.value = ''
element.valueVariable = false
}
})
})
}
}
/**
* Resets all variables used for filtering
*/
public resetFilter() {
this.whereString = undefined
this.whereClause = undefined
this.queryObj = {
elements: [
{
logic: null,
type: null,
ddtype: null,
variable: null,
operator: null,
value: null,
values: [],
operators: []
}
]
}
this.clauses = {
queryObj: [this.queryObj],
clauseLogic: [],
groupLogic: this.groupLogic
}
this.whereClauseFn(true)
}
/**
* `Globals` are used to store filtering state (variables) as a caching feature
* until browser reloads
*/
public setToGlobals() {
if (!this.caching) return
const objPath = getFilterObjPath(globals.rootParam, this.viewboxId)
get(globals, objPath).filter.groupLogic = this.groupLogic
if (typeof this.whereClause === 'string') {
get(globals, objPath).filter.whereClause = this.whereClause
}
if (typeof this.libds === 'string') {
get(globals, objPath).filter.libds = this.libds
}
get(globals, objPath).filter.clauses = this.clauses
}
/**
* `Globals` are used to store filtering state (variables) as a caching feature
* until browser reloads
*/
public getFromGlobals() {
if (!this.caching) return
const objPath = getFilterObjPath(globals.rootParam, this.viewboxId)
if (get(globals, objPath).filter.cols.length > 0) {
this.cols = JSON.parse(JSON.stringify(get(globals, objPath).filter.cols))
}
if (get(globals, objPath).filter.vals.length > 0) {
this.vals = JSON.parse(JSON.stringify(get(globals, objPath).filter.vals))
}
if (get(globals, objPath).filter.groupLogic !== '') {
this.groupLogic = get(globals, objPath).filter.groupLogic
this.whereClause = get(globals, objPath).filter.whereClause
this.libds = get(globals, objPath).filter.libds
this.clauses = get(globals, objPath).filter.clauses
this.whereClauseFn()
}
}
/**
* Sets filtering multiple caluses group logic (and / or)
*
* @param groupLogic to set
*/
public setGroupLogic(groupLogic: any) {
this.groupLogic = groupLogic
this.clauses.groupLogic = groupLogic
this.whereClauseFn()
this.setToGlobals()
}
public dateChange(
pickerValue: any,
query: any,
queryIndex: number,
clauseIndex: number,
queryValueIndex?: number
) {
let valueInDays = this.helperService.convertJsDateToSasDate(pickerValue)
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
this.clauses.queryObj[clauseIndex].elements[queryIndex].value[
queryValueIndex
] = valueInDays
}
this.setVariableValues(valueInDays, queryIndex, clauseIndex)
}
public dateTimeChange(
query: any,
queryIndex: number,
clauseIndex: number,
queryValueIndex?: number
) {
const queryDateTime = this.getQueryDateTime(clauseIndex, queryIndex)
if (queryDateTime.date === '') {
if (query.value === '') return
queryDateTime.date = query.value.split(':')[0]
}
const dateHours = parseInt(queryDateTime.time.split(':')[0])
const dateMinutes = parseInt(queryDateTime.time.split(':')[1])
const dateSeconds = parseInt(queryDateTime.time.split(':')[2]) || 0
let dateObject = new Date(queryDateTime.date)
let dateTimeObject = new Date(
dateObject.getFullYear(),
dateObject.getMonth(),
dateObject.getDate(),
dateHours,
dateMinutes,
dateSeconds
)
let valueInSeconds = this.helperService.convertJsDateToSasDate(
dateTimeObject,
'seconds'
)
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
this.clauses.queryObj[clauseIndex].elements[queryIndex].value[
queryValueIndex
] = valueInSeconds
}
this.setVariableValues(valueInSeconds, queryIndex, clauseIndex)
}
public timeChange(
time: any,
query: any,
ind: number,
clauseIndex: number,
queryValueIndex?: number
) {
time = time.target.value
let hours = parseInt(time.split(':')[0])
let minutes = parseInt(time.split(':')[1])
let seconds = parseInt(time.split(':')[2]) || 0
let hourSeconds = hours * 60 * 60
let minuteSeconds = minutes * 60
let inputValue = hourSeconds + minuteSeconds + seconds
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
this.clauses.queryObj[clauseIndex].elements[ind].value[queryValueIndex] =
inputValue
}
this.setVariableValues(inputValue, ind, clauseIndex)
}
/**
* Takes the clause object, removes current clause and makes new array (table)
* ready to be sent to SAS
*/
public dynamicWhereClauseCreator(groupIndex: number, clauseIndex: number) {
let dynamicWhereClauseTable: QueryClause[] = []
if (
this.clauses.queryObj.length > 1 ||
this.clauses.queryObj[groupIndex].elements.length > 1
) {
for (
let groupInd = 0;
groupInd < this.clauses.queryObj.length;
groupInd++
) {
for (
let clauseInd = 0;
clauseInd < this.clauses.queryObj[groupInd].elements.length;
clauseInd++
) {
if (groupInd === groupIndex && clauseInd === clauseIndex) continue
if (
[null, undefined].includes(
this.clauses.queryObj[groupInd].elements[clauseInd].variable
) ||
[null, undefined].includes(
this.clauses.queryObj[groupInd].elements[clauseInd].operator
) ||
[null, undefined].includes(
this.clauses.queryObj[groupInd].elements[clauseInd].value
)
)
continue
let rawValue = ''
const element = this.clauses.queryObj[groupInd].elements[clauseInd]
const operator = element.operator
const variable = element.variable
/* a temporal value is sent as a SAS literal (eg '20APR2042'd), the
same way the applied filter carries it */
const temporalValue = (value: any) =>
element.valueVariable
? null
: this.helperService.sasTemporalLiteral(value, element.ddtype)
/* the dropdowns and the IN modal hold the *formatted* value (the
soft-select option value, and setINValues), so a picked value is
mapped back to the unformatted one the filter needs */
const unformattedValue = (value: any) =>
(element.values || []).find((v: any) => v.formatted === value)
?.unformatted ?? value
if (operator === 'BETWEEN') {
const from = unformattedValue(element.value[0])
const to = unformattedValue(element.value[1])
rawValue = `${temporalValue(from) ?? from} AND ${
temporalValue(to) ?? to
}`
} else if (operator === 'IN' || operator === 'NOT IN') {
element.value.forEach((item: any) => {
if (item.checked) {
/* map once, and use the unformatted value in every branch - the
literal is built from it, and so are the fallbacks, so a
formatted label can never reach RAW_VALUE */
const value = unformattedValue(item.val)
const literal = temporalValue(value)
if (literal) {
rawValue += (rawValue.length > 0 ? ',' : '(') + literal
} else if (
typeof value === 'string' &&
!isSpecialMissing(value)
) {
if (rawValue.length > 0) {
rawValue += `,'${value}'`
} else {
rawValue = `('${value}'`
}
} else {
if (rawValue.length > 0) {
rawValue += `,${value}`
} else {
rawValue = `(${value}`
}
}
}
})
if (rawValue.length > 0) {
rawValue += ')'
}
} else if (
this.clauses.queryObj[groupInd].elements[clauseInd].value === ''
) {
if (
this.clauses.queryObj[groupInd].elements[clauseInd].type ===
'char'
) {
rawValue = `' '`
} else {
rawValue = '.'
}
} else {
const literal = temporalValue(element.value)
if (literal) {
rawValue = literal
} else if (element.type === 'char') {
rawValue = `'${element.value}'`
}
}
dynamicWhereClauseTable.push({
GROUP_LOGIC: this.clauses.groupLogic,
SUBGROUP_LOGIC:
this.clauses.queryObj[groupInd].clauseLogic || 'AND',
SUBGROUP_ID: groupInd,
VARIABLE_NM: variable,
OPERATOR_NM: operator,
RAW_VALUE:
rawValue ||
this.clauses.queryObj[groupInd].elements[clauseInd].value
})
}
}
}
return dynamicWhereClauseTable
}
public async getValuesLoadMore(
event: OnLoadingMoreEvent,
variable: any,
ind: any,
clauseIndex: any
) {
const libds = this.libds || ''
try {
if (this.cols !== undefined) {
for (let index = 0; index < this.cols.length; index++) {
let element = this.cols[index]
if (element.NAME === variable) {
this.selVar.index = index
}
}
const DDTYPE = this.cols[this.selVar.index].DDTYPE
const { startrow: currentStartrow, rows: currentRows } =
this.clauses.queryObj[clauseIndex].elements[ind]
const ROWS_INCREMENT: number = this.rows_increment
const STARTROW = currentStartrow + currentRows
let dynamicWhereClauseTable: any[] = []
if (this.dynamicWhereClause) {
dynamicWhereClauseTable = this.dynamicWhereClauseCreator(
clauseIndex,
ind
)
}
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(DDTYPE)
this.sasStoreService
.getQueryValues(
variable,
libds,
dynamicWhereClauseTable,
STARTROW,
ROWS_INCREMENT
)
.then((res: PublicGetcolvalsSASResponse) => {
const values = res.vals
const { STARTROW, ROWS, NOBS } = res.meta[0]
this.clauses.queryObj[clauseIndex].elements[ind].startrow = STARTROW
this.clauses.queryObj[clauseIndex].elements[ind].rows = ROWS
this.clauses.queryObj[clauseIndex].elements[ind].nobs = NOBS
if (
!(
values.length === 1 &&
values[0].FORMATTED === '' &&
values[0].UNFORMATTED === ''
)
) {
this.clauses.queryObj[clauseIndex].elements[ind].values.push(
...values.map((item: any) => {
const numeric = res.$vals.vars.UNFORMATTED.type === 'num'
const specialMissing =
numeric && isSpecialMissing(item.UNFORMATTED)
let unformatted = specialMissing
? '.' + item.UNFORMATTED
: item.UNFORMATTED
// If it's not special missing and is numeric and null then it is normal missing `.`
if (numeric && unformatted === null) unformatted = '.'
return {
formatted: isDateOrTime
? item.FORMATTED.split('.')[0]
: item.FORMATTED, //WORKAROUND SHOULD BE FIXED PROPERLY (#413): We will remove the milliseconds inside the formatted value, if decimal exists and it is date, time or datetime ddtype
unformatted: unformatted
}
})
)
const currentValues =
this.clauses.queryObj[clauseIndex].elements[ind].values.length
const moreExists = NOBS - currentValues > 0
event.loadMoreFinished(moreExists)
} else {
event.loadMoreFinished(false)
}
this.whereClauseFn()
this.setToGlobals()
this.whereClauseLoading = false
/**
* If getValues request did not finish when operator was changed to IN, we need to set values after the request is finished.
* Othervise values are not beign populated
*/
if (this.pendingINValuesSet !== null) {
const { clauseIndex, queryIndex } = this.pendingINValuesSet
this.setINValues(clauseIndex, queryIndex)
this.pendingINValuesSet = null
}
this.cdf.detectChanges()
})
.catch((err: any) => {
this.whereClauseLoading = false
})
}
} catch (error) {
this.eventService.catchResponseError('public/getcolvals', error)
}
}
public async getValues(variable: any, ind: any, clauseIndex: any) {
this.whereClauseLoading = true
let libds: string = ''
if (this.libds !== undefined) {
libds = this.libds
}
this.clearValues(ind, clauseIndex)
this.clauses.queryObj[clauseIndex].elements[ind].values = []
this.clauses.queryObj[clauseIndex].elements[ind].value = ''
this.clauses.queryObj[clauseIndex].elements[ind].operator = '='
this.clauses.queryObj[clauseIndex].elements[ind].variable = variable
try {
if (this.cols !== undefined) {
for (let index = 0; index < this.cols.length; index++) {
let element = this.cols[index]
if (element.NAME === variable) {
this.selVar.index = index
}
}
const DDTYPE = this.cols[this.selVar.index].DDTYPE
this.clauses.queryObj[clauseIndex].elements[ind].ddtype = DDTYPE
this.clauses.queryObj[clauseIndex].elements[ind].type =
this.cols[this.selVar.index].TYPE
if (this.clauses.queryObj[clauseIndex].elements[ind].type === 'num') {
this.clauses.queryObj[clauseIndex].elements[ind].operators =
this.numOperators
} else {
this.clauses.queryObj[clauseIndex].elements[ind].operators =
this.charOperators
}
let dynamicWhereClauseTable: any[] = []
if (this.dynamicWhereClause) {
dynamicWhereClauseTable = this.dynamicWhereClauseCreator(
clauseIndex,
ind
)
}
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(DDTYPE)
this.sasStoreService
.getQueryValues(
variable,
libds,
dynamicWhereClauseTable,
1,
this.initial_rows
)
.then((res: PublicGetcolvalsSASResponse) => {
const values = res.vals
const { STARTROW, ROWS, NOBS } = res.meta[0]
this.clauses.queryObj[clauseIndex].elements[ind].startrow = STARTROW
this.clauses.queryObj[clauseIndex].elements[ind].rows = ROWS
this.clauses.queryObj[clauseIndex].elements[ind].nobs = NOBS
this.clauses.queryObj[clauseIndex].elements[ind].values = values
.map((item: any) => {
const numeric = res.$vals.vars.UNFORMATTED.type === 'num'
const specialMissing =
numeric && isSpecialMissing(item.UNFORMATTED)
let unformatted = specialMissing
? '.' + item.UNFORMATTED
: item.UNFORMATTED
// If it's not special missing and is numeric and null then it is normal missing `.`
if (numeric && unformatted === null) unformatted = '.'
return {
formatted: isDateOrTime
? item.FORMATTED.split('.')[0]
: item.FORMATTED, //WORKAROUND SHOULD BE FIXED PROPERLY (#413): We will remove the milliseconds inside the formatted value, if decimal exists and it is date, time or datetime ddtype
unformatted: unformatted
}
})
.slice(0, 2000)
this.whereClauseFn()
this.setToGlobals()
this.whereClauseLoading = false
/**
* If getValues request did not finish when operator was changed to IN, we need to set values after the request is finished.
* Othervise values are not beign populated
*/
if (this.pendingINValuesSet !== null) {
const { clauseIndex, queryIndex } = this.pendingINValuesSet
this.setINValues(clauseIndex, queryIndex)
this.pendingINValuesSet = null
}
this.cdf.detectChanges()
})
.catch((err: any) => {
this.whereClauseLoading = false
})
}
} catch (error) {
this.eventService.catchResponseError('public/getcolvals', error)
}
}
public getlogic(logic: any) {
this.logic = logic
this.whereClauseFn()
}
public setLogic() {
this.whereClauseFn()
this.setToGlobals()
}
public isArr(val: any) {
return (
val instanceof Array &&
val.length > 0 &&
typeof val[0].checked !== 'undefined'
)
}
public selectedLabelChange(label: string, query: any) {
query.valueVariable = label === 'Variable'
query.value = ''
this.whereClauseFn()
}
public variableInputChange(
queryVariable: any,
index: number,
clauseIndex: number,
event: any
) {
this.getValues(queryVariable, index, clauseIndex)
}
public setVariableValues(valueObj: any, ind: any, clauseIndex: any) {
// clauseIndex is current clause in group clauses, ind is current query in clause
let operator = this.clauses.queryObj[clauseIndex].elements[ind].operator
if (operator !== 'BETWEEN' && operator !== 'IN' && operator !== 'NOT IN') {
this.clauses.queryObj[clauseIndex].elements[ind].value =
valueObj.toString()
}
this.whereClauseFn()
this.setToGlobals()
}
public setVariableOperator(ind: any, operator: any, clauseIndex: any) {
let currentValue = this.clauses.queryObj[clauseIndex].elements[ind].value
if (
currentValue instanceof Array &&
operator !== 'IN' &&
operator !== 'NOT IN'
) {
if (
currentValue instanceof Array &&
currentValue.length === 2 &&
typeof currentValue[0] === 'string'
) {
currentValue = currentValue[0]
} else {
currentValue = ''
}
}
this.clearValuesOperator(ind, clauseIndex)
this.clauses.queryObj[clauseIndex].elements[ind].operator = operator
// tslint:disable-next-line:max-line-length
if (
operator === '=' ||
operator === '>' ||
operator === '<' ||
operator === '<=' ||
operator === '>=' ||
operator === 'NE'
) {
this.clauses.queryObj[clauseIndex].elements[ind].value = currentValue
}
if (operator === 'BETWEEN') {
this.clauses.queryObj[clauseIndex].elements[ind].value = []
this.clauses.queryObj[clauseIndex].elements[ind].value.push(currentValue)
this.clauses.queryObj[clauseIndex].elements[ind].value.push('')
}
if (operator === 'IN' || operator === 'NOT IN') {
this.setINValues(clauseIndex, ind)
}
this.whereClauseFn()
this.setToGlobals()
}
public setINValues(clauseIndex: number, queryIndex: number) {
const vals = []
const inValues =
this.clauses.queryObj[clauseIndex].elements[queryIndex].values
if (inValues.length < 1) {
this.pendingINValuesSet = {
clauseIndex,
queryIndex
}
}
for (let index = 0; index < inValues.length; index++) {
vals.push({ checked: false, val: inValues[index].formatted })
}
this.clauses.queryObj[clauseIndex].elements[queryIndex].value = vals
}
public clearValues(ind: any, clauseIndex: any) {
this.clauses.queryObj[clauseIndex].elements[ind].value = ''
}
public clearValuesOperator(ind: any, clauseIndex: any) {
let operator = this.clauses.queryObj[clauseIndex].elements[ind].operator
if (
operator === 'BETWEEN' ||
operator === 'IN' ||
operator === 'NOT IN' ||
operator === 'CONTAINS'
) {
this.clearValues(ind, clauseIndex)
}
}
/**
* Checks if clause is invalid and sets bool property accordingly
* It is being used in html to mark invalid clauses red
*/
public hasInvalidCluase(clauses: any): boolean {
for (let clause of clauses) {
clause['invalidClause'] = false
if (
clause.value === '' &&
!(clause.operator === 'NE' || clause.operator === 'CONTAINS')
) {
clause['invalidClause'] = true
return true
}
if (
clause.variable === null ||
clause.operator === null ||
clause.value === null
) {
clause['invalidClause'] = true
return true
}
}
clauses.invalidClause = false
return false
}
/**
* Checks if group clause is invalid and sets bool property accordingly
* It is being used in html to mark invalid group clauses red
*/
public hasInvalidGroupCluase(): boolean {
for (let i = 0; i < this.clauses.queryObj.length; i++) {
if (this.hasInvalidCluase(this.clauses.queryObj[i].elements)) {
this.clauses.queryObj[i].invalidClause = true
return true
} else {
this.clauses.queryObj[i].invalidClause = false
}
}
return false
}
public addClause(clauseIndex: number) {
if (this.hasInvalidCluase(this.clauses.queryObj[clauseIndex].elements)) {
this.eventService.showInfoModal(
'Error',
'Cannot add new clause, when one or more clauses are invalid or empty.'
)
return
}
this.clauseAmt.push('')
this.clauses.queryObj[clauseIndex].elements.push({
logic: null,
type: null,
variable: null,
operator: null,
value: null,
values: [],
operators: []
})
let clauseLogic = this.clauses.queryObj[clauseIndex].clauseLogic
if (typeof clauseLogic === 'undefined') {
this.clauses.queryObj[clauseIndex].clauseLogic = 'AND'
} else if (clauseLogic === 'OR') {
this.clauses.queryObj[clauseIndex].clauseLogic = 'OR'
} else {
this.clauses.queryObj[clauseIndex].clauseLogic = 'AND'
}
this.whereClauseFn()
this.setToGlobals()
}
public addGroupClause() {
if (this.hasInvalidGroupCluase()) {
this.eventService.showInfoModal(
'Error',
'Cannot add new clause, when one or more clauses are invalid or empty.'
)
return
}
this.clauses.queryObj.push({
elements: [
{
logic: null,
type: null,
variable: null,
operator: null,
value: null,
values: [],
operators: []
}
]
})
this.setToGlobals()
}
public isInHtmlCollection(
value: string,
collection: HTMLCollectionOf<HTMLOptionElement>
): boolean {
for (let i = 0; i < collection.length; i++) {
if (collection[i].value === value) return true
}
return false
}
public removeGroupClause(clauseIndex: number) {
this.clauses.queryObj.splice(clauseIndex, 1)
this.whereClauseFn()
}
public removeClause(ind: number, clauseIndex: any) {
this.clauses.queryObj[clauseIndex].elements.splice(ind, 1)
this.whereClauseFn()
this.setToGlobals()
}
public whereClauseFn(reset: boolean = false) {
const clauses = this.helperService.deepClone(this.clauses)
if (reset) clauses.queryObj[0].elements = []
if (this.libds !== undefined) {
let result = this.sasStoreService.whereClauseCreator(
clauses,
this.groupLogic,
this.libds
)
if (!reset) {
this.whereClause = result.whereClause
this.whereString = result.string
}
}
}
async ngAfterViewInit() {
this._removeQry = this.sasStoreService.removeQuery.subscribe((remove) => {
let res = remove
if (this.clauses.queryObj.length >= 1) {
this.clauses.queryObj.length = 1
if (this.clauses.queryObj[0].elements.length >= 1) {
this.clauses.queryObj[0].elements.length = 1
this.clauses.queryObj[0].elements = []
this.clauses.queryObj[0].elements.push({
logic: null,
type: null,
variable: null,
operator: null,
value: null,
values: [],
operators: []
})
this.whereClauseFn()
}
}
})
this.columnsSub = this.sasStoreService.columns.subscribe(
(response: any) => {
let cols = response.data.cols
const objPath = getFilterObjPath(globals.rootParam, this.viewboxId)
if (globals.rootParam === 'home' || globals.rootParam === 'editor') {
this.cols = cols
let some = cols[0].NAME
this.libds = response.libds
get(globals, objPath).filter.cols = JSON.parse(JSON.stringify(cols))
}
if (globals.rootParam === 'view') {
if (get(globals, objPath).filter.cols.length < 1) {
this.cols = cols
let some = cols[0].NAME
this.libds = response.libds
get(globals, objPath).filter.cols = JSON.parse(JSON.stringify(cols))
}
}
if (!this.libds) {
this.libds = response.libds
}
}
)
this.valuesSub = this.sasStoreService.values.subscribe((res: any) => {
const objPath = getFilterObjPath(globals.rootParam, this.viewboxId)
if (get(globals, objPath).filter.vals.length < 1) {
this.vals = res.vals
get(globals, objPath).filter.vals = JSON.parse(JSON.stringify(res.vals))
}
})
}
onAutocompleteLoadingMore(
event: OnLoadingMoreEvent,
variable: any,
ind: any,
clauseIndex: any
) {
this.getValuesLoadMore(event, variable, ind, clauseIndex)
}
ngAfterContentInit(): void {
this.getFromGlobals()
setTimeout(() => {
let varInput: HTMLElement | null =
document.querySelector('#vals_var_id0_0')
if (varInput) varInput.focus()
}, 500)
}
ngOnDestroy(): void {
if (this.columnsSub) {
this.columnsSub.unsubscribe()
}
if (this.valuesSub) {
this.valuesSub.unsubscribe()
}
}
}
<div class="clr-flex-row clr-form-control m-0">
<div class="clr-control-container">
<clr-checkbox-wrapper>
<input clrCheckbox [(ngModel)]="dynamicWhereClause" type="checkbox" />
<label class="clr-control-label">Dynamic where clause</label>
</clr-checkbox-wrapper>
</div>
<div class="clr-control-container ml-15">
<clr-checkbox-wrapper>
<input
clrCheckbox
[(ngModel)]="usePickers"
(change)="usePickersChange()"
type="checkbox"
id="usePickers"
/>
<label class="clr-control-label"> Use pickers </label>
</clr-checkbox-wrapper>
</div>
</div>
<div class="clr-row">
<div class="select clr-col-md-2 mb-30">
<clr-select-container>
<label>Group logic</label>
<select
[disabled]="clauses?.queryObj?.length < 1"
[(ngModel)]="groupLogic"
(ngModelChange)="setGroupLogic(groupLogic)"
clrSelect
>
@for (logic of logicOperators; track logic) {
<option [selected]="logicOperators[0]">
{{ logic }}
</option>
}
</select>
</clr-select-container>
</div>
<div class="clr-col-md-10 mb-30">
<pre class="line-numbers language-markup">
@if (whereClauseLoading) {
<div class="progresStatic progress loop">
<progress></progress>
</div>
}
<code class="language-sql">{{whereClause}}</code>
</pre>
</div>
</div>
<div class="content">
<div
class="clauses-container clr-col-md-12"
[class.clr-col-md-10]="clauses?.queryObj?.length > 1"
>
@if (clauses?.queryObj?.length > 1) {
<div></div>
}
@for (clause of clauses.queryObj; track clause; let clauseIndex = $index) {
<div class="clause-row">
<div class="clr-row" [class.invalid-clause]="clause.invalidClause">
<div class="clause-logic clr-col-md-2">
<div class="select">
<clr-select-container>
<label>Logic</label>
<select
[(ngModel)]="clause.clauseLogic"
(ngModelChange)="setLogic()"
[disabled]="clause.elements.length < 2"
clrSelect
>
@for (logic of logicOperators; track logic) {
<option [selected]="logicOperators[0]">
{{ logic }}
</option>
}
</select>
</clr-select-container>
</div>
<br />
@if (innerWidth > 768) {
<button
class="btn btn-primary btn-block mt-10"
(click)="addGroupClause()"
>
<clr-icon shape="plus"></clr-icon>
<span>Group</span>
</button>
}
</div>
<div class="clause-query clr-col-md-10">
@if (clauses.queryObj.length > 1) {
<clr-icon
(click)="removeGroupClause(clauseIndex)"
shape="times"
size="36"
class="remove-group-clause-button"
></clr-icon>
}
@for (
query of clause.elements;
track query;
let queryIndex = $index
) {
<div class="clr-row" [class.invalid-clause]="query.invalidClause">
<!-- VARIABLE -->
<div class="variable-col form-group clr-col-md-3">
<div class="datalist-wrapper">
<app-soft-select
label="Variable"
[id]="
'select_vals_var_id' + queryIndex + '_' + clauseIndex
"
[inputId]="'vals_var_id' + queryIndex + '_' + clauseIndex"
[emitOnlySelected]="true"
[(value)]="query.variable"
(onInputEvent)="
variableInputChange(
query.variable,
queryIndex,
clauseIndex,
$event
)
"
>
@for (column of cols; track column) {
<option>
{{ column.NAME }}
</option>
}
</app-soft-select>
</div>
</div>
<!-- OPERATOR -->
<div class="operator-col form-group clr-col-md-3">
<clr-select-container>
<label>Operator</label>
<select
[(ngModel)]="query.operator"
(ngModelChange)="
setVariableOperator(
queryIndex,
query.operator,
clauseIndex
)
"
clrSelect
>
@for (opr of query.operators; track opr) {
<option>{{ opr }}</option>
}
</select>
</clr-select-container>
</div>
<!-- VALUE -->
<div
*ngVar="
query.ddtype === 'DATE' ||
query.ddtype === 'DATETIME' ||
query.ddtype === 'TIME' as isDateTime
"
class="value-col form-group clr-col-md-3"
>
@if (query.operator === 'IN' || query.operator === 'NOT IN') {
<div class="checkbox-vals">
<button
(click)="
currentQueryIndex = queryIndex;
currentClauseIndex = clauseIndex
"
type="button"
class="btn btn-link"
>
Choose values
</button>
<ng-container
*ngTemplateOutlet="
checkboxValues;
context: {
query: query,
queryIndex: queryIndex,
clauseIndex: clauseIndex
}
"
>
</ng-container>
</div>
}
@if (
query.operator !== 'BETWEEN' &&
query.operator !== 'IN' &&
query.operator !== 'NOT IN' &&
query.operator !== 'LIKE' &&
query.operator !== 'CONTAINS' &&
query.operator !== 'BEGINS_WITH'
) {
<div class="single-field-vals">
<ng-container
*ngTemplateOutlet="
isDateTime && usePickers ? picker : notPicker;
context: {
query: query,
queryIndex: queryIndex,
clauseIndex: clauseIndex,
isDateTime: isDateTime
}
"
>
<!-- Based on check above, here correct ng-template will be put (picker or dropdown) -->
</ng-container>
</div>
}
@if (query.operator === 'BETWEEN') {
<div class="range-vals">
<div class="from">
<ng-container
*ngTemplateOutlet="
isDateTime && usePickers ? picker : notPickerRange;
context: {
range: 'start',
query: query,
queryValueIndex: 0,
queryIndex: queryIndex,
clauseIndex: clauseIndex,
isDateTime: isDateTime
}
"
>
<!-- Based on check above, here correct ng-template will be put (picker or dropdown) -->
</ng-container>
</div>
<div class="to">
<ng-container
*ngTemplateOutlet="
isDateTime && usePickers ? picker : notPickerRange;
context: {
range: 'end',
query: query,
queryValueIndex: 1,
queryIndex: queryIndex,
clauseIndex: clauseIndex,
isDateTime: isDateTime
}
"
>
<!-- Based on check above, here correct ng-template will be put (picker or dropdown) -->
</ng-container>
</div>
</div>
}
@if (
query.operator === 'LIKE' ||
query.operator === 'BEGINS_WITH' ||
query.operator === 'CONTAINS'
) {
<div class="contains-vals">
<label class="clr-control-label">Value</label>
<input
class="input-val"
type="text"
[(ngModel)]="query.value"
(ngModelChange)="
setVariableValues($event, queryIndex, clauseIndex)
"
/>
</div>
}
</div>
<div class="clause-buttons clr-col-md-2 btn-group">
<button
class="btn btn-warning btn-block"
(click)="removeClause(queryIndex, clauseIndex)"
[disabled]="
clauses.queryObj[clauseIndex].elements.length === 1
"
>
<clr-icon shape="minus"></clr-icon>
<span></span>
</button>
<button
class="btn btn-success btn-block"
(click)="addClause(clauseIndex)"
>
<clr-icon shape="plus"></clr-icon>
<span></span>
</button>
</div>
</div>
}
</div>
</div>
<br />
</div>
}
</div>
</div>
<!-- If type is DATE, DATETIME or TIME -->
<ng-template
#picker
let-query="query"
let-queryValueIndex="queryValueIndex"
let-queryIndex="queryIndex"
let-clauseIndex="clauseIndex"
>
@switch (query.ddtype) {
@case ('DATE') {
<app-soft-select
label="Value"
type="date"
[disableSoftselect]="true"
[inputId]="'vals_date' + queryIndex + '_' + clauseIndex"
[value]="
queryValueIndex !== undefined
? (query.value[queryValueIndex] | sasToJsDate)
: (query.value | sasToJsDate)
"
(valueChange)="
dateChange($event, query, queryIndex, clauseIndex, queryValueIndex)
"
>
<!-- In case we want to enable dropdown on the pickers, uncomment below -->
<!-- <ng-container *ngFor="let value of query.values">
<option *ngIf="value.unformatted !== null">
{{ value.formatted | dateTimeFormatter: 'date' }}
</option>
</ng-container> -->
</app-soft-select>
}
@case ('DATETIME') {
<app-soft-select
label="Value"
type="date"
[disableSoftselect]="true"
[inputId]="'vals_date' + queryIndex + '_' + clauseIndex"
[value]="
queryValueIndex !== undefined
? (query.value[queryValueIndex] | sasToJsDate: 'seconds')
: (query.value | sasToJsDate: 'seconds')
"
(valueChange)="getQueryDateTime(clauseIndex, queryIndex).date = $event"
(onInputEvent)="
dateTimeChange(query, queryIndex, clauseIndex, queryValueIndex)
"
>
<!-- In case we want to enable dropdown on the pickers, uncomment below -->
<!-- <ng-container *ngFor="let value of query.values">
<option *ngIf="value.unformatted !== null">
{{ value.formatted | dateTimeFormatter: 'date' }}
</option>
</ng-container> -->
</app-soft-select>
<app-soft-select
type="time"
[disableSoftselect]="true"
[inputId]="'vals_time' + queryIndex + '_' + clauseIndex"
[value]="
queryValueIndex !== undefined
? (query.value[queryValueIndex] | sasToJsDate: 'seconds' : true)
: (query.value | sasToJsDate: 'seconds' : true)
"
(valueChange)="getQueryDateTime(clauseIndex, queryIndex).time = $event"
(onInputEvent)="
dateTimeChange(query, queryIndex, clauseIndex, queryValueIndex)
"
>
<!-- In case we want to enable dropdown on the pickers, uncomment below -->
<!-- <ng-container *ngFor="let value of query.values">
<option *ngIf="value.unformatted !== null">
{{ value.formatted | dateTimeFormatter: 'time' }}
</option>
</ng-container> -->
</app-soft-select>
}
@case ('TIME') {
<app-soft-select
label="Value"
type="time"
[disableSoftselect]="true"
[inputId]="'vals_' + queryIndex + '_' + clauseIndex"
[value]="
queryValueIndex !== undefined
? (query.value[queryValueIndex] | secondsParser)
: (query.value | secondsParser)
"
(onInputEvent)="
timeChange($event, query, queryIndex, clauseIndex, queryValueIndex)
"
>
<!-- In case we want to enable dropdown on the pickers, uncomment below -->
<!-- <ng-container *ngFor="let value of query.values">
<option *ngIf="value.unformatted !== null">
{{ value.formatted | dateTimeFormatter: 'time' }}
</option>
</ng-container> -->
</app-soft-select>
}
}
</ng-template>
<ng-template
#notPicker
let-isDateTime="isDateTime"
let-query="query"
let-queryIndex="queryIndex"
let-clauseIndex="clauseIndex"
>
<app-soft-select
label="Value"
[secondLabel]="'Variable'"
[emitOnlySelected]="query.valueVariable"
[inputId]="'vals_' + queryIndex + '_' + clauseIndex"
(selectedLabelChange)="selectedLabelChange($event, query)"
[(value)]="query.value"
[enableLoadMore]="query.nobs > query.values.length"
(onInputEvent)="
setVariableValues($event.target.value, queryIndex, clauseIndex)
"
(onAutocompleteLoadingMore)="
onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex)
"
>
@if (!query.valueVariable) {
<div>
@for (column of query.values; track column) {
<option [value]="column.unformatted">
{{ column.formatted.trim() }}
</option>
}
</div>
}
@if (query.valueVariable) {
<div>
@for (column of cols; track column) {
@if (column.TYPE === query.type) {
<option [value]="column.NAME">
{{ column.NAME }}
</option>
}
}
</div>
}
</app-soft-select>
</ng-template>
<ng-template
#notPickerRange
let-range="range"
let-query="query"
let-queryValueIndex="queryValueIndex"
let-queryIndex="queryIndex"
let-clauseIndex="clauseIndex"
>
<app-soft-select
label="Value"
[inputId]="'vals-' + range + '_' + queryIndex + '_' + clauseIndex"
[(value)]="query.value[queryValueIndex]"
[enableLoadMore]="query.nobs > query.values.length"
(onInputEvent)="setVariableValues(query.value, queryIndex, clauseIndex)"
(onAutocompleteLoadingMore)="
onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex)
"
[disabled]="
queryValueIndex !== undefined
? range !== 'start'
? !query.value[0]
: false
: false
"
>
@for (column of query.values; track column) {
<option [value]="column.formatted">
{{ column.formatted }}
</option>
}
</app-soft-select>
</ng-template>
<ng-template
#checkboxValues
let-query="query"
let-queryIndex="queryIndex"
let-clauseIndex="clauseIndex"
>
<clr-modal
[clrModalOpen]="
currentQueryIndex === queryIndex && currentClauseIndex === clauseIndex
"
[clrModalClosable]="false"
class="in-values-modal"
>
<h3 class="modal-title">Select values</h3>
<div class="modal-body">
@if (!isArr(query.value)) {
<h5 class="no-values">No values available.</h5>
}
@if (isArr(query.value)) {
<section class="form-block">
<clr-checkbox-container>
@for (column of query.values; track column; let i = $index) {
<clr-checkbox-wrapper>
<input
type="checkbox"
clrCheckbox
[(ngModel)]="query.value[i].checked"
(ngModelChange)="
setVariableValues($event, queryIndex, clauseIndex)
"
/>
<label>
{{ column.formatted }}
</label>
</clr-checkbox-wrapper>
}
</clr-checkbox-container>
</section>
}
</div>
<div class="modal-footer">
<button
(click)="currentQueryIndex = -1; currentClauseIndex = -1"
type="button"
class="btn btn-outline"
>
Apply
</button>
</div>
</clr-modal>
</ng-template>
./query.component.scss