$.extend(true, DataTable.defaults) ignores 'column' defaults in DataTables 3

$.extend(true, DataTable.defaults) ignores 'column' defaults in DataTables 3

K3nguruhK3nguruh Posts: 9Questions: 1Answers: 0

Hi Allan,

Sorry to bother you again! I'm currently migrating from DataTables 2 to DataTables 3, so I'm noticing a few unusual behaviors along the way. I hope that's okay and I'm not being a nuisance! ;-)

Issue Description

I use DataTable.defaults to define baseline configurations so that all tables share the same default setup. If a specific table requires different settings, I override them directly inside new DataTable().

However, setting global column defaults via column (such as orderSequence) no longer seems to work in DataTables 3, whereas it worked as expected in DataTables 2.3.7.

$.extend(true, DataTable.defaults, {
  // layout - works
  layout: {
    topStart: null,
    topEnd: null,
    bottomStart: null,
    bottomEnd: null,
  },

  // column - doesn't work (worked in DT 2.3.7)
  column: {
    orderSequence: ["asc", "desc"],
  },
});

const table = new DataTable("#example");

Testcase / Live Example:
https://live.datatables.net/xufodala/4/edit?html,js,output

P.S.: To toggle DataTables 3.0.3 in the live example, please enable/disable the respective stylesheet and script tags in the HTML panel.

Replies

  • kthorngrenkthorngren Posts: 22,503Questions: 26Answers: 5,171
    edited September 1

    There is not a column option in Datatables so I'm not sure why it worked in DT 2. There maybe undocumented cases from older DT versions that are bugs, etc that worked that are not added to DT 3. You need to use either columns or columnDefs as shown in the columns.orderSequence examples. I updated your test case with columnDefs to show it works:
    https://live.datatables.net/xufodala/5/edit

    Kevin

  • allanallan Posts: 65,972Questions: 1Answers: 10,980 Site admin

    I have the column's default object aliased to DataTable.defaults.column so things like orderSequence can have a default set. The property is there, I can see it in the console, but it doesn't appear to be getting used! I'm not sure what has gone wrong there, but I would actually expect that to work, as it did in 2.x I'll get back to you when I've had a chance to dig further.

    Allan

  • K3nguruhK3nguruh Posts: 9Questions: 1Answers: 0

    Hi everyone,

    @Kevin thanks for the suggestion, that helped! In my setup, it would need to be columnDefs: [{ orderSequence: ["asc", "desc"], targets: "_all" }].

    However, this approach comes with a major downside: you can no longer easily use columnDefs inside individual table initializations, as the global defaults will overwrite everything—regardless of what you try to set there. Consequently, all subsequent column configurations would have to be handled via columns.

    Testcase / Live Example:
    https://live.datatables.net/xufodala/10/edit?html,js,output

    So that's definitely a tradeoff to consider. Either way, @Allan mentioned he'll take another look at it.

  • allanallan Posts: 65,972Questions: 1Answers: 10,980 Site admin

    Okay, I've realised what is happening here. Using $.extend(true will copy the array, but it is a deep copy, so since the original default length is 3, and a smaller array is copied to it, the length says at 3 - i.e.

    [ 'asc', 'desc' ]
    

    copied to:

    [ 'asc', 'desc', '' ]
    

    results is exactly the same as the original!

    No that didn't happen in 2.x as the defaults were actually hungarian notation, so orderSequence was undefined. I had a mapping in to convert the new style to the old naming. That isn't the case in v3, so we hit this "bug".

    If you simply do:

    DataTable.defaults.column.orderSequence = [ 'asc', 'desc' ];
    

    Then it will work as expected!

    I do have a utility function in DataTables to deep copy objects, but shallow copy arrays, which I thought I was going to be clever with and show you that, but I've just spotted a "todo" note on it for arrays on deep objects. Doh! I'll get that fixed so the utility function is there, but for the moment, just do a direct assignment for any arrays you want to modify on the defaults.

    Allan

  • allanallan Posts: 65,972Questions: 1Answers: 10,980 Site admin

    I've committed the change to resolve that todo item and DataTable.util.object.assignDeepObjects is now the function that you would want.

    Here is the updated test case.

    That change will be in 3.0.4 :).

    Allan

  • K3nguruhK3nguruh Posts: 9Questions: 1Answers: 0

    Hi Allan,

    Thanks for your efforts!

    I wanted to share a custom deep-merge function that I routinely use to combine objects recursively. Perhaps you'll find it useful or interesting as reference material.

    The function handles properties as follows:
    * Primitive values & Functions: Directly replaced/overwritten
    * Arrays: Replaced entirely as a deep copy
    * Objects: Merged recursively (retaining existing properties while adding/overwriting new ones)

    I created a test case using the defaults setup from DataTables. Since defaults$3 and defaults$4 were referenced as variables in the original source, I resolved them directly within the test object.

    /**
     * Creates a deep copy of `defaults` and recursively merges properties from `options`.
     *
     * @param {Object} defaults - The base object containing default settings.
     * @param {Object} [options={}] - The object containing custom options.
     * @returns {Object} A new merged object (the original remains untouched).
     */
    function assignDeep(defaults, options = {}) {
      // Return primitive value, null, undefined, or function directly
      if (defaults === null || typeof defaults !== "object") {
        return defaults;
      }
    
      // Create an element-by-element copy if it's an array
      if (Array.isArray(defaults)) {
        return defaults.map((item) => assignDeep(item));
      }
    
      // Step 1: Create a deep copy of the base object (defaults)
      const output = Object.keys(defaults).reduce((acc, key) => {
        acc[key] = assignDeep(defaults[key]);
        return acc;
      }, {});
    
      // Return clone early if options is invalid
      if (!options || typeof options !== "object") {
        return output;
      }
    
      // Step 2: Override or extend properties from options
      return Object.keys(options).reduce((acc, key) => {
        const sourceValue = options[key];
        const outputValue = acc[key];
    
        // Option is an array -> Apply as a deep copy
        if (Array.isArray(sourceValue)) {
          acc[key] = assignDeep(sourceValue);
        }
        // Option is an object -> Merge recursively with existing target value
        else if (sourceValue && typeof sourceValue === "object") {
          acc[key] = assignDeep(outputValue || {}, sourceValue);
        }
        // Option is a primitive value or function -> Overwrite directly
        else {
          acc[key] = sourceValue;
        }
    
        return acc;
      }, output);
    }
    

    P.S.: This function could be simplified even further (e.g., using structuredClone), but native cloning wouldn't be able to preserve function references.

    Testcase / Live Example:
    https://live.datatables.net/rabusexi/1/edit?js,console,output

  • allanallan Posts: 65,972Questions: 1Answers: 10,980 Site admin

    Very nice - thank you :).

    Allan

  • K3nguruhK3nguruh Posts: 9Questions: 1Answers: 0

    Hi Allan,

    I think I've spotted another issue.

    When there are two or more tables on the same page, the previously configured ordering settings are not applied to the second table and any subsequent ones. This happens regardless of whether DataTable.util.object.assignDeepObjects or $.extend is used.

    Testcase / Live Example:
    https://live.datatables.net/sulilequ/1/edit?js,console,output

    P.S.: Do you have a estimated timeline for releasing version 3.0.4 (with the export selectors fix)?

  • allanallan Posts: 65,972Questions: 1Answers: 10,980 Site admin

    That's a tricky little one! The backwards compatibility actually mutates the defaults object, which is why we see that effect. I've put a little change in to address that, but I might change it further so that defaults does not get mutated, but rather cloned and then modified. I think that would be a better plan in the long term.

    Currently expecting to release 3.0.4 next week. Hopefully Tuesday or Wednesday.

    Allan

Sign In or Register to comment.