Skip to content

Field validation #28

@MissaelAnda

Description

@MissaelAnda

Hi there! 👋
I've been toying with your project and I tried to add form validation but I see you don't use the inertia router/form helper to consume the api controller instead you use Ky so I was wondering how could the error injection to the form could be performed.

This is my approach to form validation:

export class BaseField<T = unknown> implements Serializable {
  // ...

  protected attributes: Record<string, any> = {};
  protected options: Record<string, any> = {};
+ protected rules: Record<string, any> = {};
  protected display: FieldDisplayOptions = {
    index: true,
    create: true,
    update: true,
    peek: true,
  };

 // ...

+ protected buildRules(): SchemaTypes | null {
+   return null;
+ }

toJSON() {
    return {
      kind: this.$kind,
      name: this.$name,
      label: this.$label,
      icon: this.$icon,
      attributes: this.attributes,
      options: this.options,
      display: this.display,
      defaultValue: this.$defaultValue,
+    rules: this.buildRules(),
    };
  }

The field should add their own rules and then compile the vine object:

export class TextField extends BaseField {
  // ...

  min(value: number) {
    if (value <= 0) {
      throw Error('Min length must be positive');
    }

    this.rules['min'] = value;
    return this;
  }

  protected buildRules(): VineString {
    let rule = vine.string();

    if (!this.attributes['required']) {
      rule.optional()
    }

    if (this.rules['min']) {
      rule.minLength(this.rules['min']);
    }

    return rule;
  }
}

Then we can validate in the LucidBaseResource:

  async create(data: any): Promise<TRecord> {
    const validator = this.compileValidator();
    const processedData = await validator.validate(data) as Partial<ModelAttributes<InstanceType<TModel>>>;

    const record = await this.model.create(processedData);
    return record as TRecord;
  }

  // ...

  compileValidator() {
    let rules: Record<string, SchemaTypes> = {};

    this.toJSON().fields.forEach(field => field.fields.forEach(field => {
      if (field.rules) {
        rules[field.name] = field.rules;
      }
    }));

    return vine.compile(vine.object(rules));
  }

The thing is that if we dont send the Accept: application/json or the X-Inertia: true header the client only gets a error page instead of json response, if we send the inertia header we get the full inertia response including the error page prop but the X-Inertia-Version is missing so we receive a 409 conflict, if we send the application/json it wont get the full inertia response but it does return the errors in standard REST.

I would like to contribute to the project but I'm not a Typescript expert.

What's your proposal for handling errors?

Great project btw!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions