Admin UI
Overview

Admin UI

ActiveQL comes with an Angular application that offers basic (CRUD) admin operation with the entity data.

As with the domain configuration the configuration of the Admin UI follows the convention over configuration paradigm. The Admin UI will work without any additional configuration based on assumptions. Surely this purely generated UI will most likely not fit any real world requirements but we find it helpful when developing an UI to start with something that "works somehow" instead of building everything from scratch.

The important part is (and that's whats sets ActiveQL aside from most low/no-code platforms) it works with a lot of magic but it gives you the freedom to code anything you want to code yourself.

The Admin UI uses two sources of configuration

  • the domain configuration from the GraphQL API server
  • the provided configuration from the embedding Angular application

Embedding the library in your Application

You embed the ActiveQL UI library in your Angular application as follows:

import { ActiveQLAdminUIModule } from 'activeql-admin-ui';
import { GraphQLModule } from '../graphql/graphql.module';
import { adminConfig } from './config/admin.config';
 
@NgModule({
  declarations: [
    ...
  ],
  imports: [
    GraphQLModule.forRoot({uri: 'http://localhost:4000/graphql'}),
    ActiveQLAdminUIModule.forRoot( adminConfig ),
    ... (mainly material)
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

As you might have guessed, besides the GraphQL and Material modules the ActiveQL library relies on, you provide your configuration as parameter of type AdminConfig to the ActiveQLAdminUIModule. This is already done by the generated Angular application.

admin.config.ts

The (generated) starter Angular application provides the configuration from the file ./config/admin.config.ts - but you can organize this as you see fit. We recommend to split the configuration between separate files per entity and merge them together.

Entity View Configuration

You can configure the index, show, edit, create view of an entity as follows:

Optiontypedescription
queryFunctionthe GraphQL API call to prepare the rendering of the view
actionCallbacksActionCallbackType[]additional Actions that should be offered on the view

query

Per default the generated query will include all data needed to render the default views. This would include

  • items / item data
  • parent data
  • lookup data for forms

If your view needs other data you can provide your own query here.

Index view

Optiontypedefaultdescription
fields(string|FieldConfig)[]see belowwhat fields to show in the table and how they should be rendered
searchbooleantrue if larger than 10 itemswhether to offer (client side) search of all items in the table
order{ field:string direction:'asc'|'desc' }1st col ascwhich field should be used to sort the table initially
filterFilterConfigTypenoneadding (server-side) filter to the index table and the default state of the filter

Fields

Per default the index table contains

  • all attributes of the entity except id, createdAt, updatedAt
  • all assocTo items

in unreliable order - although usually in the order of the definition of the attributes in the domain configuration.

You can state which field and which order should be rendered by giving an array of either the field or assoc names (with default behavior) or FieldConfig objects which allows you to control nearly every aspect of rendering.

adminConfig:AdminConfig = {
  entities: {
    Car: {
      index: {
        fields: ['license', 'color', 'mileage', 'Driver']
      }
    }
  }
}

FieldConfig

Optiontypedefaultdescription
namestringthe name of the attribute or entity of the assoc or assocToMany
typestringthe type from the domain configurationthe type of the attribute or entity of the assoc
labelstring | ()=>stringlookup in resources or humanized field namethe column label for the field
listbooleandependent from the attribute / assoc type in the domainConfigurationthe column label for the field
renderFunctionuses renderValue / renderListreturns the string-value that should be displayed in the index table
renderValueFunctionsee belowreturns the string-value of exactly one value (e.g. from array values) that should be displayed in the index table
renderListFunctionsee belowif more than one value, gets the (renderd) values and returns the (combined) string-value that should be displayed in the index table
valueanyitem[field]gets the value (as is) from the item
sortValueanyvalue.toString()the string value that should be used to determine the sort order in the index table
querystringattribute or indication for assocpart of the query of the view to add for this field's value

Label

If you do not provide a label, the default label will be looked up under this path in the resources of the AdminConfig.

['resources', locale, 'entities', entity, 'label', field]

If no such entry exists, the humanized attribute name of the entity or the humanized name of the assoc entity will be used.

Render

Let's say you want to render the color attribute in the respective color itself. A (admittelay trivial) implementation could be:

adminConfig:AdminConfig = {
  entities: {
    Car: {
      index: {
        fields: [
          'license', 
          { name: 'color', render: (item:any) => `<span style="color: ${item.color}">${item.color}</span>` }, 
          mileage', 
          'Driver'
        ]
      }
    }
  }
}

Filter

The default index action will query and render all items of the entity and performs pagination, sorting and searching at the client. This could be an issue if there are too many items (think of more than 10.000). You can of course always route to a custom component with another behavior, but often entities have an attribute like a status that you could use to filter this initial loading.

Given a entity configuration:

enum:
  CarStatus: 
    - acquisition
    - inService
    - underRepair
    - inactive
entity: 
  Car: 
    license: String!
    color: String
    status: CarStatus

Let's assume there are all the historic car data in the collection (status: inactive). A query to all Car entity items would result in a > 50.000 list. We want to allow the (server side) filtering of Car items and want to load initially only those with status: acquisition, inService and underRepair.

adminConfig:AdminConfig = {
  entities: {
    Car: {
      index: {
        filter: ['status', 'acquisition', 'inService', 'underRepair']
      }
    }
  }
}

It would show a toggle button control, with all possible values from the CarStatus enum (first element) and the initially chosen values (second and following elements).

This is in fact a shortcut for the following implementation, that shows you how you can implement your own filter logic. Keep in mind that before you implement complex query here you always have the possibility to subclass the IndexActionComponent and implement any custom logic while keeping as much default functionality as you want.

filter: {
  setFilterToQuery: ( query, values:any ) => {
    if( ! values ) values = ['AKTIV', 'PRODUKTION', 'GEPLANT'];
    if( ! _.isArray( values ) ) values = [values];
    values = _.map( values, value => new EnumType(value));
    const filter = { status: { in: values } } ;
    const filterQuery = _.set( {}, ['query', 'kurse', '__args'], {  filter } );
    _.merge( query, filterQuery );
  },
  fields: [
    {
      name: 'status',
      control: 'toggle', 
      multiple: true, 
      options: [
        {value: 'AKTIV', label: 'Aktiv'}, 
        {value: 'PRODUKTION', label: 'In produktion'},
        { value: 'GEPLANT', label: 'Geplant'}, 
        {value: 'INAKTIV ', label: 'Inaktiv'}        
      ],
      defaultValue: ['AKTIV', 'PRODUKTION', 'GEPLANT']
    } 
  ]
}