> For the complete documentation index, see [llms.txt](https://next-docs.cloudio.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://next-docs.cloudio.io/ui/controller-component.md).

# Controller Component

`Controller Component` can be used to create a page level custom component (one time) for some advanced use cases. The below example controller code exports a `Controller Component` named `QuickEntry` that renders couple quick entry input fields that makes parallel async calls to cloud functions and create records on tab out enabling users to perform high speed data entry.

{% code title="Page Controller" overflow="wrap" lineNumbers="true" %}

```tsx
import type { Store, BaseFunctionProps } from 'cloudio';
import { useStringAttribute, useNumberAttribute } from 'cloudio';
import { OmsPreOrderLines } from '@cloudio-saas/datasource-types';
import React, { useRef } from 'react';
import { Box, TextField } from '@mui/material';
import platform from 'platform';

async function createOrUpdateLine(
  linesStore: Store<OmsPreOrderLines>,
  id: string,
  resp: OmsPreOrderLines,
): Promise<string> {
  if (id) {
    // update record
    linesStore.updateRecord(id, resp);
  } else {
    // create record
    const newid = await linesStore.createNew({
      partialRecord: resp,
      addOnTop: true,
      rs: 'I',
    });
    if (!newid) {
      throw new Error('Failed to create new record!');
    }
    id = newid;
  }
  return id;
}

async function onQuickEntryQtyChange({
  appUid,
  pageId,
  itemId,
  platform,
}: BaseFunctionProps) {
  // get the stores
  const quickEntryStore = platform.getStore<OmsPreOrderLines>(
    appUid,
    pageId,
    'quickEntryAlias',
  );
  const linesStore = platform.getStore<OmsPreOrderLines>(
    appUid,
    pageId,
    'omsPreOrderLinesAlias',
  );
  if (!quickEntryStore || !linesStore) {
    platform.showError(`Store not found for ${itemId}`);
    return;
  }
  // get the current quick entry row
  const quickRow = quickEntryStore.getCurrentRecord();
  const { itemCode, itemQty } = quickRow;
  quickEntryStore.clearQuietyly().then(() => {
    quickEntryStore.createNew({ partialRecord: {}, rs: 'N' });
  });
  let id = '';
  // invoke cloud functions in parallel
  await Promise.all([
    platform
      .invokeCloudFunction(appUid, 'get-oms-item-details', {
        itemCode,
        itemQty,
      })
      .then(async (resp: OmsPreOrderLines) => {
        const { itemCode, itemQty, itemDesc } = resp;
        id = await createOrUpdateLine(linesStore, id, {
          itemCode,
          itemQty,
          itemDesc,
        });
        platform.redrawCanvasGrid(appUid, pageId, 'canvasGrid');
      }),
    platform
      .invokeCloudFunction(appUid, 'get-oms-item-price', { itemCode, itemQty })
      .then(async (resp: OmsPreOrderLines) => {
        const { sellPrice } = resp;
        id = await createOrUpdateLine(linesStore, id, {
          itemCode,
          itemQty,
          sellPrice,
        });
        platform.redrawCanvasGrid(appUid, pageId, 'canvasGrid');
      }),
  ]);
}

function QuickEntry(props: BaseFunctionProps) {
  // similar to useState hook in react for an attribute in the current record
  const [itemCode, setItemCode] = useStringAttribute('itemCode');
  const [itemQty, setItemQty] = useNumberAttribute('itemQty');
  // ref used for focus
  const ref = useRef<HTMLInputElement>(null);
  // ref to store the focus value
  const focusVal = useRef<number | undefined>(undefined);

  return (
    <Box sx={{ flex: 1, display: 'flex', minWidth: '400px', gap: '8px' }}>
      <TextField
        inputRef={ref}
        variant="standard"
        label="Item Code"
        value={itemCode ?? ''}
        onChange={(e) => {
          setItemCode(e.target.value);
        }}
        fullWidth
      />
      <TextField
        type="number"
        variant="standard"
        label="Item Qty"
        value={itemQty ?? ''}
        onChange={(e) => {
          let val: number | undefined = Number(e.target.value);
          if (Number.isNaN(val)) {
            val = undefined;
          }
          setItemQty(val);
        }}
        onFocus={() => {
          // store the existing value, so we can check against this in onBlur
          focusVal.current = itemQty;
        }}
        onBlur={(e) => {
          if (focusVal.current !== itemQty) {
            // value changed, create line
            onQuickEntryQtyChange(props);
            ref.current?.focus();
          }
        }}
        fullWidth
      />
    </Box>
  );
}

// must export a default object with all the functions to be used by this page
export default {
  QuickEntry,
};
```

{% endcode %}
