AutoQueryGrid Component

Default CRUD

By default you can create an AutoQueryGrid that allows authorized users the ability to Create, Read, Update & Delete records with just the DataModel, e.g:

<AutoQueryGrid type="Booking" />

This will utilize your App's existing AutoQuery APIs for the specified DataModel to enable its CRUD functionality.

Read Only

You can use apis to limit which AutoQuery APIs AutoQueryGrid should use, so if only the AutoQuery DTO is provided, the AutoQueryGrid will only be browsable in read-only mode:

<AutoQueryGrid type="Booking" apis="QueryBookings"  />

Table Styles

The same DataGrid Table Styles can also be used to style AutoQueryGrid, e.g:

<AutoQueryGrid type="Booking" tableStyle="verticalLines,uppercaseHeadings" />

Custom Styles

The AutoQueryGrid's appearance is further customizable with the property classes & functions below:

defineProps<{
  toolbarButtonClass: string
  tableStyle: "simple" | "fullWidth" | "stripedRows" | "whiteBackground" | "uppercaseHeadings" | "verticalLines"
  gridClass: string
  grid2Class: string
  grid3Class: string
  grid4Class: string
  tableClass: string
  theadClass: string
  tbodyClass: string
  theadRowClass: string
  theadCellClass: string

  rowClass:(model:any,i:number) => string
  rowStyle:(model:any,i:number) => StyleValue
}>()

Custom AutoQueryGrid

Different AutoQueryGrid features can be hidden with hide and functionality disabled with deny, e.g:

<AutoQueryGrid type="Booking" hide="pagingNav,copyApiUrl,downloadCsv" deny="filtering" />

Features that can be hidden and disabled include:

defineProps<{
    deny: "filtering" | "queryString" | "queryFilters"
    hide: "toolbar"   | "preferences" | "pagingNav" | "pagingInfo" | "downloadCsv" | "refresh" 
       | "copyApiUrl" | "filtersView" | "newItem"   | "resetPreferences" 
}>()

Global AutoQueryGrid Configuration

These features can also be disabled at a global level, applying to all <AutoQueryGrid> components with setConfig, e.g:

const { setAutoQueryGridDefaults } = useConfig()

setAutoQueryGridDefaults({
  hide: ['pagingNav','copyApiUrl','downloadCsv']
})

Limit Columns

By default AutoQueryGrid displays all public properties returned in its AutoQuery API which can be further limited with selected-columns:

<AutoQueryGrid type="Booking" selectedColumns="id,name,roomType,roomNumber,cost" />

Simple Responsive Columns

Using visible-from is a simple way to enable a responsive DataGrid by specifying at which Tailwind breakpoints columns should be visible from and header-titles to use friendlier aliases for different columns, e.g:

<AutoQueryGrid type="Booking" 
  selectedColumns="id,name,roomType,roomNumber,cost,bookingStartDate,bookingEndDate" 
  :headerTitles="{ roomNumber:'Room', bookingStartDate:'Start', bookingEndDate:'End' }"
  :visibleFrom="{ bookingStartDate:'lg', bookingEndDate:'xl' }" />

Custom Responsive Columns

Which columns are displayed and how they're formatted are further customizable with <template #column> slots:

<AutoQueryGrid type="Booking" :visibleFrom="{ name:'xl', bookingStartDate:'sm', bookingEndDate:'xl', createdBy:'2xl' }">
    <template #id="{ id }">
        <span class="text-gray-900" v-html="id"></span>
    </template>
    
    <template #name="{ name }">{‎{name}‎}</template>
    
    <template #roomNumber-header>
        <span class="hidden lg:inline">Room </span>No
    </template>

    <template #cost="{ cost }">
        <span v-html="currency(cost)"></span>
    </template>
    
    <template #bookingStartDate-header>
        Start<span class="hidden lg:inline"> Date</span>
    </template>
    
    <template #bookingEndDate-header>
        End<span class="hidden lg:inline"> Date</span>
    </template>

    <template #createdBy-header>
        Employee
    </template>
    <template #createdBy="{ createdBy }" v-html="createdBy"></template>
</AutoQueryGrid>

Custom Functionality

The column template slots can be leveraged to implement custom functionality, e.g. instead of navigating to separate pages to manage related data we can use a custom column to manage Booking Coupons from within the same grid, e.g:

<AutoQueryGrid type="Booking" selectedColumns="id,name,cost,bookingStartDate,bookingEndDate,discount">
    <template #discount="{ discount }">
        <TextLink v-if="discount" class="flex items-end" @click.stop="showCoupon(discount.id)" :title="discount.id">
            <Icon class="w-5 h-5 mr-1" type="Coupon" />
            <PreviewFormat :value="discount.description" />
        </TextLink>
    </template>
</AutoQueryGrid>
<AutoEditForm v-if="coupon" type="UpdateCoupon" v-model="coupon" @done="close" @save="close" />

<script setup lang="ts">
import { ref } from "vue"
import { useClient } from "@servicestack/vue"
import { QueryCoupons } from "dtos"

const client = useClient()
const coupon = ref()

async function showCoupon(id:string) {
    const api = await client.api(new QueryCoupons({ id }))
    if (api.succeeded) {
        coupon.value = api.response!.results[0]
    }
}

const close = () => coupon.value = null
</script>

Data Reference Labels

AutoQuery is able to infer relationships from the POCO References of your Data Models where if your DataModel includes [Reference] attributes so that its related Data is returned in your AutoQuery APIs, AutoQueryGrid will be able to make use of it to render the Contacts & Job Names and Icons instead of just the plain Foreign Key Ids.

An example of this in the JobApplications DataModel DTO:

[Icon(Svg = Icons.Application)]
public class JobApplication : AuditBase
{
    [AutoIncrement]
    public int Id { get; set; }

    [References(typeof(Job))]
    public int JobId { get; set; }

    [References(typeof(Contact))]
    public int ContactId { get; set; }

    [Reference]
    [Format(FormatMethods.Hidden)]
    public Job Position { get; set; }

    [Reference]
    [Format(FormatMethods.Hidden)]
    public Contact Applicant { get; set; }

    [Reference]
    public List<JobApplicationComment> Comments { get; set; }

    public DateTime AppliedDate { get; set; }

    public JobApplicationStatus ApplicationStatus { get; set; }
    //...
}

Which AutoQueryGrid uses to automatically display the Job and Contact name instead of their ids:

<AutoQueryGrid type="JobApplication" :prefs="{take:5}" />

With the original ids are discoverable by hovering over the Job & Contact labels.

Reference Fields

By default AutoQuery will infer using the first string column of the related table for its label, this information can also be explicitly defined with the [Ref] attribute, e.g:

public class JobApplication : AuditBase
{
    [AutoIncrement]
    public int Id { get; set; }

    [References(typeof(Job))]
    [Ref(Model=nameof(Job), RefId=nameof(Job.Id), RefLabel=nameof(Job.Title))]
    public int JobId { get; set; }

    [References(typeof(Contact))]
    [Ref(Model=nameof(Contact), RefId=nameof(Contact.Id), RefLabel=nameof(Contact.DisplayName))]
    public int ContactId { get; set; }
    //...
}

Alternatively you can use [Ref(None=true)] to disable any implicit inferences and render the FK property Ids as-is.

When displaying referential data you can tell AutoQueryGrid to hide rendering the complex data references as well columns using [Format(FormatMethods.Hidden)].

AutoQueryGrid Template Slots

AutoQueryGrid supports a number of Vue slots to customize its built-in UIs, including formheader and formfooter slots to insert custom content before and after the Auto Create & Edit components forms:

<template #formheader="{ form, type, apis, model, id }">
<template #formfooter="{ form, type, apis, model, id }">

This feature is used to implement Locode's Audit History UI for displaying the Audit History of each record in the bottom of the Edit Form for Authorized Users, implemented with:

<AutoQueryGrid :key="store.opDataModel" ref="grid" :type="store.opDataModel">
    <template #formfooter="{ form, type, apis, model, id }">
        <AuditEvents v-if="form === 'edit' && canAccessCrudEvents" class="mt-4" :key="id" :type="type" :id="id" />
    </template>
</AutoQueryGrid>

Which loads the AuditEvents.mjs component at the bottom of Edit forms, allowing Admin Users to inspect the Audit History of each record:

Alternatively you can replace the entire Create and Edit Forms used with the createform and editforms slots:

<template #createform="{ form, apis, type }">
<template #editform="{ form, apis, type }">

Additional toolbar buttons can be added with the toolbarbuttons slot, e.g:

<template #toolbarbuttons="{ toolbarButtonClass }">
  <div class="pl-2 mt-1">
      <button type="button" @click="customAction" :class="toolbarButtonClass">
        <span class="whitespace-nowrap">My Action</span>
      </button>
  </div>
</template>

Alternatively you can replace the entire toolbar with your own with:

<template #toolbar>

All other template slots are passed down to the embedded DataGrid component where they can be used to customize column headers and cells.

AutoQueryGrid Properties

Additional customizations available using AutoQueryGrid properties include:

defineProps<{
    filterDefinitions?: AutoQueryConvention[]
    id?: string
    apis?: string|string[]
    type?: string|InstanceType<any>|Function
    prefs?: ApiPrefs

    deny?: string|GridAllowOptions|GridAllowOptions[]
    hide?: string|GridShowOptions|GridShowOptions[]
    
    selectedColumns?:string[]|string
    toolbarButtonClass?: string
    tableStyle?: TableStyleOptions
    gridClass?: string
    grid2Class?: string
    grid3Class?: string
    grid4Class?: string
    tableClass?: string
    theadClass?: string
    tbodyClass?: string
    theadRowClass?: string
    theadCellClass?: string

    headerTitle?:(name:string) => string
    headerTitles?: {[name:string]:string}
    visibleFrom?: {[name:string]:Breakpoint}
    rowClass?:(model:any,i:number) => string
    rowStyle?:(model:any,i:number) => StyleValue | undefined

    apiPrefs?: ApiPrefs
    canFilter?:(column:string) => boolean
    disableKeyBindings?:(column:string) => boolean
    configureField?: (field:InputProp) => void
    skip?: number
    create?: boolean
    edit?: string|number
}>()

type ApiPrefs = {
    take?: number
    selectedColumns?: string[]    
}

type FormStyle  = "slideOver" | "card"
type TableStyle = "simple" | "fullWidth" | "stripedRows" | "whiteBackground" | "uppercaseHeadings"
  | "verticalLines"

type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "2xl"

type GridAllowOptions = 
  "filtering"   | "queryString" | "queryFilters"

type GridShowOptions = 
  "toolbar"     | "preferences" | "pagingNav"        | "pagingInfo"  | "downloadCsv" | 
  "refresh"     | "copyApiUrl"  | "resetPreferences" | "filtersView" | "newItem"

AutoQueryGrid Events

Whilst the headerSelected and rowSelected events can be used to invoke custom functionality when column headers and rows are selected:

defineEmits<{
    (e: "headerSelected", name:string, ev:Event): void
    (e: "rowSelected", item:any, ev:Event): void
}>()

Powers Locode

AutoQueryGrid is already used extensively and is the key component that enables Locode's Instant Auto UI to manage your App's AutoQuery CRUD APIs.