
# Performance/Goal Renderer

These are series renderers that compare and display the target value (or expected value) stored in two data fields and the execution value.
There are types such as  [ActualTargetBulletRenderer](/refs/Interface/ActualTargetBulletRenderer),
[ActualTargetTextRenderer](/refs/Interface/ActualTargetTextRenderer), and you must select the appropriate type according to the content of the data.

```js
const fields = [
  {
    fieldName: 'category',
    dataType: 'text',
  },
  {
    fieldName: 'target',
    dataType: 'number',
  },
  {
    fieldName: 'actual',
    dataType: 'number',
  },
];

const columns = [
  {
    width: 180,
    name: 'category',
    fieldName: 'category',
    header: {
      text: 'Category',
    },
  },
  {
    name: 'target',
    fieldName: 'target',
    width: 70,
    header: {
      text: 'Target',
    },
  },
  {
    name: 'actual',
    fieldName: 'actual',
    header: {
      text: 'Actual',
    },
  },
  {
    width: 400,
    name: 'colBullet',
    type: 'series',
    fieldNames: 'target,actual',
    height: 30,
    renderer: {
      type: 'actualtargetbullet',
    },
    header: {
      text: 'Bullet',
    },
    styleCallback: (grid, model) => {
      const ret = {};
      const [target, actual] = model.value;

      if (target < actual) {
        ret.renderer = {
          actualStyle: 'fill-green'
        };
      } else {
        ret.renderer = {
          actualStyle: 'fill-red'
        };
      }

      return ret;
    },
  },
  {
    width: 400,
    name: 'colText',
    type: 'series',
    fieldNames: 'target,actual',
    fillHeight: 100,
    renderer: {
      type: 'actualtargettext',
    },
    header: {
      text: 'Text',
    },
  },
];

const layout = [
  'category',
  'target',
  'actual',
  {
    name: 'renderer',
    width: 400,
    items: ['colBullet', 'colText'],
    direction: 'vertical',
    header: { visible: false },
  },
];

const { gridView, dataProvider } = RealGrid.createGrid('realgrid', {
  columns,
  header: {
    height: 40
  },
  display: {
    rowHeight: 50,
    emptyMessage: 'No data to display.'
  },
  edit: {
    insertable: true,
    appendable: true
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (grid) => {
    grid.setColumnLayout(layout);
    fetch('/data/en/actualTargetData.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js


var columns = [
 .. skip ..
 {
 width: 400,
 name: 'colBullet',
 type: 'series',
 fieldNames: 'target,actual',
 height: 30,
 renderer: {
 type: 'actualtargetbullet',
 },
 header: {
 text: 'Bullet',
 },
 styleCallback: (grid, model) => {
 const ret = {}
 const [target, actual] = model.value;

 if (target < actual) {
 ret.renderer = {
 actualStyle: 'fill-green'
 }
 } else {
 ret.renderer = {
 actualStyle: 'fill-red'
 }
 }

 return ret;
 },
 },
 {
 width: 400,
 name: 'colText',
 type: 'series',
 fieldNames: 'target,actual',
 renderer: {
 type: 'actualtargettext',
 },
 header: {
 text: 'Text',
 },
 },
]

```