DEV Community 👩‍💻👨‍💻

Cover image for Stupid form generator
Arnaud Denoyelle
Arnaud Denoyelle

Posted on

Stupid form generator

Or why full automatic forms generators are often a fake good idea, and what to use instead.

But first things first, why do we use form generators in the first place ?

Forms take more time to develop than expected

Let's say you want to build a login/password form. 2 input fields + a submit button. Seems easy and looks like it will take 5 min.

But experienced developers learned the hard way that forms hide some complexity :

  • Field validation
  • Error messages
  • Layout problems

Email validation

If you need a lot of forms in your application, they can be tedious to build. To tackle this problem, some developer's immediate reaction is to use a form generator. There are plenty of them, in every existing language.


Automatic form generators : the fake good idea

Form generators come with a handful of promises :

  • Just configuration, no HTML/CSS/JS/Java to write
  • The code base will become "cleaner"
  • Compatible with any tool/framework/library
  • Fully configurable

At the beginning, everything seems easy to use, and the demo works well.

Then you decide to use it in your project, on real use cases.

This is where the fun begins

Then, you discover problems :

  • A text field is nice, but I need a text area instead
  • But I need a very special component for this field, like a color picker
  • But here, I want to place components differently
  • But here, I want to add a "special offer" tag
  • But I need to react to a value change in this component
  • But I need to show a different component if this field has that value.

And their usual solutions :

  • You have an option for that
  • You can use a design pattern provide your own component
  • You can override the layout with CSS
  • You can use a factory/builder/adapter in order to handle this very special use case
  • You can provide a callback
  • We developed a custom dynamic expression language to handle this use case

In the end, you realize that, for complex use cases, it would have been easier to just use the native API

A form generator that aims at handling every use case is bound to become more complex than the problem it was supposed to solve.

You did not want to write code, but your configuration became the code

You've become the very thing you swore to destroy

Do you remember when you just wrote the form by hand, and were able to do exactly what you wanted without dealing with tons of options ?

With a full automatic form generator, you still end up coding the form manually. You just code with the generator options instead of the native API, but you actually finish the code manually.

Configuring form generators takes time, and if you have less than < 10 forms in your app... just write them by hand.

Or...


Code generators to the rescue

Use a code generator instead. What is the difference ? You generate some code then copy-paste this code in the project. The project does not contain the form generator anymore, only the generated code.

Then, you can finish the code manually, adapt the structure as you wish and use the native API for complex use cases.

Also, as the complexity comes from the intention to handle every case... what if you just generated some code that is only 90% right ?

A code that is only 90% accurate ? Really ?

You underestimate my power

As you don't need to handle every use case anymore, the complexity falls dramatically. With a rule like "1 field = 1 component", you can build yourself a code generator that will handle 90% of the use cases in only ~100 lines of code.

And as you code the generator yourself, you can decide what you want to automatize or not and fully customize the generated code to your good taste.

I show you how to do that, and I provide a boilerplate


Build your own code generator

This part shows one possible way to build a custom code generator. I provide a working example here.

It generates a basic Angular form with 2 templates :

  • One template for the HTML code
  • One template for the TypeScript code

It is not much, but enough to understand the concept.

The code generator consists in 3 steps :

  1. Describe the form fields
  2. Enrich the model with variables that will be useful in the templates
  3. Apply the enriched model on various templates

1) Describe the fields

The first thing you need is a place to describe the fields. As it will be your code generator and your templates, just think about the info that you will need when writing the templates.

export const input = {
  fields:
    [
      {
        name: "firstName",
        component: "TEXTFIELD",
        required: true,
        placeholder: "John"
      },
      {
        name: "lastName",
        component: "TEXTFIELD",
        required: true,
        placeholder: "Doe"
      },
      [...]
     ]
}
Enter fullscreen mode Exit fullscreen mode

2) Enrich the model

From this point, the variable name firstName is useful, but you will probably need to have it in different cases : FIRST_NAME, first-name, First Name etc. This can be automatized with a simple for loop in order to obtain that :

{
  "fields": [
    {
      "name": "firstName",
      "component": "TEXTFIELD",
      "required": true,
      "placeholder": "John",
      "nameCamelCase": "firstName",
      "nameUpperCase": "FIRST_NAME",
      "nameSnakeCase": "first_name",
      "nameCapitalized": "FirstName",
      "nameKebabCase": "first-name",
      "nameLabel": "First Name"
    },
    {
      "name": "lastName",
      "component": "TEXTFIELD",
      "required": true,
      "placeholder": "Doe",
      "nameCamelCase": "lastName",
      "nameUpperCase": "LAST_NAME",
      "nameSnakeCase": "last_name",
      "nameCapitalized": "LastName",
      "nameKebabCase": "last-name",
      "nameLabel": "Last Name"
    },
Enter fullscreen mode Exit fullscreen mode

3) Apply it to templates

For this part, you have to choose a template engine. I chose ejs because it has the required features : conditions and loops. Also, it is quite standard and well documented

The code is as simple as

  console.log('Loading template ' + templatePath);
  const template = readFileSync(templatePath, 'utf-8');

  console.log('Compiling template');
  const compiledTemplate = ejs.compile(template);

  console.log('Applying template');
  const result = compiledTemplate(enrichedInput);
Enter fullscreen mode Exit fullscreen mode

And here is what the template looks like :

<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
<% fields.forEach(function(field){ %>
  <div>
    <%_ if (field.component === 'TEXTFIELD') { -%>
    <mat-form-field>
        <mat-label><%= field.nameLabel %></mat-label>
        <input matInput [formControl]="formControl<%= field.nameCapitalized%>"<% if (field.required === true) { %> placeholder="<%= field.placeholder %>"<% } %>>
    </mat-form-field>
    <%_ } -%>
    <%_ if (field.component === 'TEXTAREA') { -%>
    <mat-form-field>
        <mat-label><%= field.nameLabel %></mat-label>
        <textarea matInput [formControl]="formControl<%= field.nameCapitalized%>"<% if (field.required === true) { %> placeholder="<%= field.placeholder %>"<% } %>></textarea>
    </mat-form-field>
    <%_ } -%>
Enter fullscreen mode Exit fullscreen mode

Put everything together

The complete program works like that :

  • Read the list of form fields from the input file
  • Enrich the model with various string cases
  • For each template of the templates directory :
    • Apply the enriched model on the template
    • Open the template in your favorite text editor

For me, it opens the generated files in gedit, ready to be copy-pasted to the project !

Generated code


Conclusion

By taking back the control on the generated code, you can have 90% of the code automatically generated while still being able to fully customize the output.

Bonus : using the same strategy, it is possible, from a given class to generate most of the files for the frontend AND the backend part of the app that are more or less always the sames:

  • DAO
  • Service
  • Controller
  • Model
  • View
  • Presenter
  • various pieces of configuration to add in those files that you systematically forget.

I applied this strategy with success in an app that contained hundreds of forms. The code generator (written in Java at the time) would generate 25 different files for every part of the application : frontend, backend, database, configuration.

Code generators are nothing new and they are incredibly efficient. Their power should not be forgotten

I know him, it's me

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.