
# class custom renderer

Custom Renderer is a function that allows you to create and use your own renderer when the basic renderer provided by RealGrid is insufficient.
The demo below shows a radio button operating in one cell.
Unlike the renderer method applied to the existing “Custom Renderer - Image Button”, this function is used in a class method.  

### Note
* Using a custom renderer means that the developer directly controls the grid without using its functions. Therefore, it is not basically a technical support area.  
* However, if there is a problem with the code you wrote, you must send us the executable source code to receive `technical support`.  
* The custom renderer was developed for view purposes and is not suitable for input purposes.  
* Since it was not developed with the idea of being able to draw the entire thing using external components, there may be problems if you draw an area larger than the row height.  

```js
const fields = [
  { fieldName: 'KorName', dataType: 'text' },
  { fieldName: 'Gender', dataType: 'text' },
  { fieldName: 'Age', dataType: 'number' },
  { fieldName: 'Phone', dataType: 'text' },
  { fieldName: 'ProductId', dataType: 'text' },
  { fieldName: 'KorCountry', dataType: 'text' },
  { fieldName: 'OrderDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'CardNumber', dataType: 'text' },
  { fieldName: 'Monetary', dataType: 'text' },
  { fieldName: 'StartDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'EndDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'ToMonth', dataType: 'number' },
  { fieldName: 'Month', dataType: 'number' },
  { fieldName: 'Year', dataType: 'number' },
  { fieldName: 'InterestRate', dataType: 'number' },
  { fieldName: 'SaveCost', dataType: 'number' },
  { fieldName: 'SaveMaturity', dataType: 'number' },
  { fieldName: 'CurrentSave', dataType: 'number' }
];

const columns = [
  {
    name: 'KorName',
    fieldName: 'KorName',
    width: '60',
    header: {
      text: 'Name'
    }
  },
  {
    name: 'Gender',
    fieldName: 'Gender',
    width: '100',
    header: {
      text: 'Gender'
    },
    values: ['M', 'F'],
    labels: ['M', 'F'],
    renderer: {
      type: 'radio_renderer'
    }
  },
  {
    name: 'Age',
    fieldName: 'Age',
    width: '40',
    header: {
      text: 'Age'
    },
    styleName: 'right-column'
  },
  {
    name: 'Phone',
    fieldName: 'Phone',
    width: '100',
    header: {
      text: 'Phone'
    }
  },
  {
    name: 'ProductId',
    fieldName: 'ProductId',
    width: '120',
    header: {
      text: 'Product ID'
    }
  },
  {
    name: 'KorCountry',
    fieldName: 'KorCountry',
    width: '100',
    header: {
      text: 'Investment Country'
    }
  },
  {
    name: 'OrderDate',
    fieldName: 'OrderDate',
    width: '100',
    header: {
      text: 'Order Date'
    }
  },
  {
    name: 'CardNumber',
    fieldName: 'CardNumber',
    width: '140',
    header: {
      text: 'Card Number'
    }
  },
  {
    name: 'Monetary',
    fieldName: 'Monetary',
    width: '40',
    header: {
      text: 'Currency'
    }
  },
  {
    name: 'StartDate',
    fieldName: 'StartDate',
    width: '100',
    header: {
      text: 'First Payment Date'
    }
  },
  {
    name: 'EndDate',
    fieldName: 'EndDate',
    width: '100',
    header: {
      text: 'End Date'
    }
  },
  {
    name: 'ToMonth',
    fieldName: 'ToMonth',
    width: '50',
    header: {
      text: 'Payment Count'
    },
    styleName: 'right-column'
  },
  {
    name: 'Month',
    fieldName: 'Month',
    width: '50',
    header: {
      text: 'Remaining Count'
    },
    styleName: 'right-column'
  },
  {
    name: 'InterestRate',
    fieldName: 'InterestRate',
    width: '50',
    numberFormat: '0.00',
    header: {
      text: 'Interest Rate'
    },
    styleName: 'right-column'
  },
  {
    name: 'SaveCost',
    fieldName: 'SaveCost',
    width: '80',
    numberFormat: '#,##0',
    header: {
      text: 'Payment Amount'
    },
    styleName: 'right-column'
  },
  {
    name: 'SaveMaturity',
    fieldName: 'SaveMaturity',
    width: '80',
    numberFormat: '#,##0',
    header: {
      text: 'Maturity Amount'
    },
    styleName: 'right-column'
  },
  {
    name: 'CurrentSave',
    fieldName: 'CurrentSave',
    width: '80',
    numberFormat: '#,##0',
    header: {
      text: 'Current Balance'
    },
    styleName: 'right-column'
  }
];

class CustomRadioRenderer extends RealGrid.CustomCellRendererImpl {
  get styleName() {
    return 'rg-renderer custom-radio-renderer';
  }
  _checks = [];
  createInput(parent, value, label) {
    const span = document.createElement('label');
    const check = document.createElement('input');
    check.type = 'radio';
    check.value = value;
    span.appendChild(check);
    span.appendChild(document.createTextNode(` ${label} `));
    parent.appendChild(span);
    return { check, label: span };
  }

  _doInitContent(parent) {
    const column = this.index.column;
    this._checks = new Array(column.values.length).fill(null);
    this._checks.forEach((v, i, array) => {
      array[i] = this.createInput(parent, column.values[i], column.labels[i]);
    });
  }

  _doClearContent(parent) {
    this._checks.forEach((v) => {
      v.check.parentElement?.removeChild(v.check);
      v.label.parentElement?.removeChild(v.label);
    });
    this._checks = [];
    parent.innerHTML = '';
  }

  render(grid, model, w, h) {
    this._checks.forEach((v) => {
      v.check.checked = model.value === v.check.value;
    });
  }

  canEditClickAt(event) {
    return event.target instanceof HTMLInputElement;
  }

  canClick(event) {
    return true;
  }

  canEdit() {
    return true;
  }

  canClickSpaceKey(event) {
    if (['1', '2', '3'].includes(event.key)) {
      return true;
    }
    return false;
  }

  itemClick(event) {
    if ([49, 50, 51].includes(event.keyCode)) {
      const idx = event.keyCode - 49;
      this._checks[idx] && this._checks[idx].check.click();
    }
  }
  _doEditClick(index, event, result) {
    const target = event.target;
    result.value = target.value;
    return target instanceof HTMLInputElement;
  }

  get showTooltip() {
    return true;
  }

  tooltip(model, index) {
    if (!model.value) {
      return 'Select a value';
    }
    return '';
  }
}

RealGrid.registerCustomRenderer('radio_renderer', CustomRadioRenderer);

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

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


## Custom renderer class settings

```js
class CustomRadioRenderer extends RealGrid.CustomCellRendererImpl {
   getstyleName() {
       return 'rg-renderer custom-radio-renderer';
   }
   _checks = [];
   createInput(parent, value, label) {
       const span = document.createElement("label");
       const check = document.createElement("input");
       check.type = "radio";
       check.value = value;
       span.appendChild(check);
       span.appendChild(document.createTextNode(` ${label} `));
       parent.appendChild(span);
       return {check, label: span};
   }

   _doInitContent(parent) {
    
       const column = this.index.column;
       this._checks = new Array(column.values.length).fill(null);
         this._checks.forEach((v, i, array) => {
             array[i] = this.createInput(parent, column.values[i], column.labels[i]);
         });
   }

   _doClearContent(parent) {
       this._checks.forEach(v => {
           v.check.parentElement?.removeChild(v.check);
           v.label.parentElement?.removeChild(v.label);
       })
       this._checks = [];
       parent.innerHTML = "";
   }
  
   render(grid, model, w, h) {
       this._checks.forEach(v => {
           v.check.checked = model.value === v.check.value;
       });
   }

   canEditClickAt(event) {
       // Enables the HTMLInputElement to enter the editing state only when it is clicked.
       return (event.target instanceof HTMLInputElement);
      
   }

   canClick(event) {
       return true;
   }

   canEdit(){
       return true;
   }

   canClickSpaceKey(event){
       if (['1','2'].includes(event.key)) {
           return true;
       }
       return false;
   }

   itemClick(event) {
       if ([49, 50, 51].includes(event.keyCode)) {
           const idx = event.keyCode - 49;
           this._checks[idx] && this._checks[idx].check.click();
       }
   }
   _doEditClick(index, event, result) {
       const target = event.target
       result.value = target.value;
       return target instanceof HTMLInputElement;
   }

   get showTooltip() {
       return true
   }

   tooltip(model, index) {
       if (!model.value) {
           return 'Please select a value'
       }
       return ""
   }
}
```

## Register and apply renderer

```js
RealGrid.registerCustomRenderer("radio_renderer", CustomRadioRenderer);

//Register renderer when setting column
var columns = [
   ...
   {
     name: "Gender",
     fieldName: "Gender",
     width: "100",
     header: {
       text: "Gender"
     },
     values:["Male", "Female"],
     labels:["Male", "Female"],
     renderer: {
       type: "radio_renderer"
     }
   }
]

```