October: DataTable Dropdown/Autocomplete column(s) options handler not working

Created on 4 Oct 2019  路  1Comment  路  Source: octobercms/october

  • OctoberCMS Build: 459
  • PHP Version: 7.3
  • Database Engine: MySQL 5.7
  • Plugins Installed: RainLab.Builder

Description:

According to the pending docs for datatables, autocomplete and dropdown columns can specify a custom AJAX handler for returning options. There are a couple different scenarios which I will outline the steps below.

Steps To Reproduce:

Basic Setup

  1. Add a datatable field to a form config definition:
data:
        type: datatable
        adding: true
        btnAddRowLabel: Add Above
        btnAddRowBelowLabel: Add Below
        btnDeleteRowLabel: Delete Below
        columns:
            name:
                title: Name
                type: dropdown
                validation:
                    required:
                        message: Please enter a name
  1. Load the form. This produces an error that the getDataTableOptions method is not defined on the model, which is to be expected because no options are defined.

  2. Add options to the column definition with a custom handler:

name:
                title: Name
                type: dropdown
               options: getNameOptions
                validation:
                    required:
                        message: Please enter a name
  1. Add the getNameOptions method to the model.
  2. Refresh the form. The same error is thrown, however, the dropdown is listing the name of the handler as if it has been exploded into an array:
    image
    image

A solution here, if only one dropdown with custom handler is defined, is to just add the getDataTableOptions method to the model, which works as expected. However, this will not work for our requirements.

After some digging, it appears the widget is looking for a specific handler format. That being said, I went ahead and modified the handler to match the expected format:

  1. Update the options handler name on the model:
public function getNameDataTableOptions()
{
return ['foo'=>'bar'];
}

This returns the custom options. However, if more than one dropdown wants to define a custom options handler, it returns the options from the first handler.

  1. Add a second dropdown column:
secondName:
                title: Second name
                type: dropdown

image
image

As you can see, both dropdowns are returning the same options. I believe this is because it is checking for the model's field name, not the column name.

Also, I've noticed that autocomplete columns are not using the options at all. I will be digging into this a bit more, but wanted to log the issue for reference. If anyone has experience working with datatables or these types of columns, I would appreciate feedback or suggestions.

Question

Most helpful comment

@LarBearrr I'll have to find some way of including this in docs, but I've managed to find out the intention of the callback functionality.

So it appears that the callback works on the idea that you can have a callback method that applies across an entire model (ie. getDataTableOptions) and can basically apply to any datatable, and a callback method that applies for a single datatable (ie. getFieldDataTableOptions).

When you use the getDataTableOptions method, it passes three parameters:

  • $this->field - Represents the field that the datatable is for. In your example, this would be data.
  • $field - This actually represents the column, not the field as specified by the variable name. So in your example, this could be either name or secondName.
  • $data - Represents a single row of data in the table. The callback is applied for each row in a datatable, and allows you the flexibility of changing options in a dropdown depending on data in other columns.

With the getFieldDataTableOptions type of method, the first property is excluded (as it's already implied from the name of the method), so only the column name and data are provided in the callback.

What I would suggest is having a single callback method, and using conditions to pass it to callback methods for each column, for example, using your example above:

public function getDataDataTableOptions($column, $row)
{
    if ($column === 'name') {
        return $this->getNameOptionsForDataTable($row);
    } elseif ($column === 'secondName') {
        return $this->getSecondNameOptionsForDataTable($row);
    }

    return [];
}

public function getNameOptionsForDataTable($row)
{
    return [
        'Option 1' => 'Option 1'
        ...
    ];
}

public function getSecondNameOptionsForDataTable($row)
{
    return [
        'Foo' => 'Bar'
        ...
    ];
}

I hope that makes sense. Let me know if you need any further pointers.

>All comments

@LarBearrr I'll have to find some way of including this in docs, but I've managed to find out the intention of the callback functionality.

So it appears that the callback works on the idea that you can have a callback method that applies across an entire model (ie. getDataTableOptions) and can basically apply to any datatable, and a callback method that applies for a single datatable (ie. getFieldDataTableOptions).

When you use the getDataTableOptions method, it passes three parameters:

  • $this->field - Represents the field that the datatable is for. In your example, this would be data.
  • $field - This actually represents the column, not the field as specified by the variable name. So in your example, this could be either name or secondName.
  • $data - Represents a single row of data in the table. The callback is applied for each row in a datatable, and allows you the flexibility of changing options in a dropdown depending on data in other columns.

With the getFieldDataTableOptions type of method, the first property is excluded (as it's already implied from the name of the method), so only the column name and data are provided in the callback.

What I would suggest is having a single callback method, and using conditions to pass it to callback methods for each column, for example, using your example above:

public function getDataDataTableOptions($column, $row)
{
    if ($column === 'name') {
        return $this->getNameOptionsForDataTable($row);
    } elseif ($column === 'secondName') {
        return $this->getSecondNameOptionsForDataTable($row);
    }

    return [];
}

public function getNameOptionsForDataTable($row)
{
    return [
        'Option 1' => 'Option 1'
        ...
    ];
}

public function getSecondNameOptionsForDataTable($row)
{
    return [
        'Foo' => 'Bar'
        ...
    ];
}

I hope that makes sense. Let me know if you need any further pointers.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m49n picture m49n  路  3Comments

mittultechnobrave picture mittultechnobrave  路  3Comments

oppin picture oppin  路  3Comments

Flynsarmy picture Flynsarmy  路  3Comments

d3monfiend picture d3monfiend  路  3Comments