Skip to content

Instantly share code, notes, and snippets.

@CurtisL
Created July 15, 2019 17:24
Show Gist options
  • Save CurtisL/880335d97ad7f6164351a6d7a714f2bd to your computer and use it in GitHub Desktop.
Save CurtisL/880335d97ad7f6164351a6d7a714f2bd to your computer and use it in GitHub Desktop.
Statamic Conditional Grid Fields: A Hack

Statamic Conditional Grid Fields: A Hack

This snippet provides the ability to not only add custom conditions to which grid field columns are visible, but change their order on the fly.

According to the statamic documentation

Important: Conditionals work on top-level fields only, not meta fields (fields that include other fields) like Replicator, Bard, and Grid.

They make it sound like it's just not possible with these field types. Which is mostly true... But with a bit of work you can write your own custom ones by extending Statamic.conditions in your site/helpers/cp/scripts.js

The Hack.

When extending the Statamic.conditions you're not given very much information about the condition's environment other than the entire entry fields object. So you have to get tricky by manually querying the dom to get other field values and handling it that way. But when you query the fields, you can gain access to the vue component properties and modify the field settings.

The script provides a gridColumnFilters condition to be used with show_when.

Usage

Create a grid field and add:

show_when: gridColumnFilters

Create a choice field (suggest, checkboxes... etc, for best results use a suggest) with the name of your grid field + _columns (mygrid_columns). Each option should match your grid fields names.

Any grid fields that you want to always be visible add persistent_column: true to that grid fields settings.

The grid field will only be visible when the _columns field has value. The grid fields will also be displayed in the order of the suggest field.

/**
* Custom condition to show/hide grid field columns based off the value of a
* suggest field. Only the available fields are shown and in the order selected.
*
* @returns boolean true if columns are selected.
*/
var cachedFilters = [];
Statamic.conditions.gridColumnFilters = function (fields) {
var column_filters_field = `${this.id}_columns`;
if (! fields.hasOwnProperty(column_filters_field)) {
return false; // No filters column in this set.
}
var active_columns = fields[column_filters_field];
var field_id = this.id;
if (!active_columns || active_columns.length <= 0) {
return false;
}
if (JSON.stringify(cachedFilters) === JSON.stringify(active_columns)) {
return active_columns.length > 0; // No change in filters.
}
cachedFilters = active_columns;
/**
* The condition is fired before the grid field is loaded the first time.
* so we have to check if the document has loaded and queue it up so our
* query selector can actually find the grid componet for manipulation.
*/
if (document.readyState === 'complete') {
filterGridColumns(field_id, active_columns);
} else {
window.addEventListener('load', function() {
filterGridColumns(field_id, active_columns);
});
}
return active_columns.length > 0;
};
function filterGridColumns(field_id, active_columns) {
var grid_fieldtypes = document.querySelectorAll('.grid-fieldtype');
grid_fieldtypes.forEach(grid => {
if (! field_id === grid.__vue__.config.name) {
return; // not the grid we're looking for
}
var columns = grid.__vue__.config.fields;
/**
* Check if this column is enabled.
*/
columns.forEach((column, index) => {
// push any inactive columns to the end
column.position = index;
if (column.persistent_column) {
return; // don't affect this column's state
}
// Make each column honor the sorted order of `{grid_field}_columns`.
if (active_columns.includes(column.name)) {
column.position = active_columns.indexOf(column.name);
}
if (!active_columns.includes(column.name) && column.type !== 'hidden') {
// hide column, store old type so we can get it back later
column.original_type = column.type;
column.type = 'hidden';
} else if (active_columns.includes(column.name) && column.type === 'hidden') {
// Show column. see, this is why we save original_type :)
column.type = column.original_type;
}
});
// Display columns in `{grid_field}_columns` order
columns = columns.sort(function(a, b) {
if (a.position > b.position) {return 1};
if (a.position < b.position) {return -1};
return 0;
});
grid.__vue__.config.fields = columns;
});
}
@CurtisL
Copy link
Author

CurtisL commented Jul 15, 2019

Usage Example:

products_columns:
    options:
        display: Display
        model: Model
        storage: Storage
        processor: Processor
        ram: RAM
    create: false
    type: suggest
    display: 'Products Columns'
products:
    mode: table
    add_row: 'Add Product'
    fields:
        display:
            display: 'Display size'
            type: text
        model:
            type: text
            display: Model
        processor:
            display: Processor
            type: text
        storage:
            display: Storage
            type: text
        ram:
            display: RAM
            type: text
        price:
            display: Price
            type: text
            mode: number
            persistent_column: true
    type: grid
    display: Products
    min_rows: 1
    show_when: gridColumnFilters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment