validateComponents(components = null, data = null, flags = {}) {
    components = components || this.component.components;
    data = data || this.rootValue;
    const { async, dirty, process } = flags;
    const validationProcessorProcess = (context) => this.validationProcessor(context, flags);
    const checkModalProcessorProcess = ({ instance, component, components }) => {
      // If we just validated the last component, and there are errors from our parent, then we need to show a model of those errors.
      if (
        instance &&
        instance.parent &&
        (component === components[components.length - 1]) &&
        instance.parent.componentModal
      ) {
        instance.parent.checkModal(instance.parent.childErrors, dirty);
      }
    };
    const processorContext = {
      process: process || 'unknown',
      components,
      instances: this.componentsMap,
      data: data,
      local: !!flags.local,
      scope: { errors: [] },
      parent: this.component,
      parentPaths: this.paths,
      processors: [
        {
          process: validationProcessorProcess,
          processSync: validationProcessorProcess
        },
        {
          process: checkModalProcessorProcess,
          processSync: checkModalProcessorProcess
        }
      ]
    };
    return async ? processAsync(processorContext).then((scope) => scope.errors) : processSync(processorContext).errors;
  }
Perform a validation on all child components of this nested component.
@param {import('@formio/core').Component[]} components - The components to validate.
@param {import('@formio/core').DataObject} data - The data to validate.
@param {object} flags - The flags to use when validating.
@returns {Promise<Array>|Array} - The errors if any exist.